Browse Source

Improved Sound Debug UI, updated ImGui

Thomas Buck 9 years ago
parent
commit
86a7bbab0f

+ 7
- 0
ChangeLog.md View File

@@ -2,6 +2,13 @@
2 2
 
3 3
 ## OpenRaider (0.1.3) xythobuz <xythobuz@xythobuz.de>
4 4
 
5
+    [ 20140309 ]
6
+    * Removed (unused) SSE support script
7
+    * Added level loading notification in main menu
8
+    * Can now start/stop individual SoundSources
9
+    * Added SoundDetail debug UI, improved SoundSources debug UI
10
+    * Updated imgui to version 1.35
11
+
5 12
     [ 20140307 ]
6 13
     * Can now load all TR3 levels without crashing
7 14
     * Sound Sources now working, with (more or less) proper 3D Sound

+ 0
- 1
README.md View File

@@ -174,7 +174,6 @@ There are some included cmake scripts:
174 174
 
175 175
 * [FindALUT](https://github.com/rpavlik/cmake-modules/blob/master/FindALUT.cmake)
176 176
 * [FindSDL2](https://github.com/dhewm/dhewm3/blob/master/neo/sys/cmake/FindSDL2.cmake)
177
-* [FindSSE](https://gitorious.org/vc/vc/source/a1d8b9fc31060d870386613cc72319546c850b87:cmake/FindSSE.cmake)
178 177
 * [GetGitRevisionDescription.cmake](https://github.com/rpavlik/cmake-modules/blob/master/GetGitRevisionDescription.cmake)
179 178
 * [GetGitRevisionDescription.cmake.in](https://github.com/rpavlik/cmake-modules/blob/master/GetGitRevisionDescription.cmake.in)
180 179
 * [FindGLM] (https://github.com/g-truc/glm/blob/master/util/FindGLM.cmake)

+ 0
- 1
TODO.md View File

@@ -8,7 +8,6 @@
8 8
 
9 9
 ## Cmake
10 10
 
11
-* Support SSE with other compilers than Clang (src/CMakeLists.txt)
12 11
 * Visual C++ compiler flags? (CMakeLists.txt)
13 12
 * Better test integration
14 13
 

+ 0
- 124
cmake/FindSSE.cmake View File

@@ -1,124 +0,0 @@
1
-# Check if SSE instructions are available on the machine where 
2
-# the project is compiled.
3
-
4
-IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
5
-    EXEC_PROGRAM(cat ARGS "/proc/cpuinfo" OUTPUT_VARIABLE CPUINFO)
6
-
7
-    # TODO
8
-    set(SSE_FOUND true CACHE BOOL "SSE available on host")
9
-
10
-    STRING(REGEX REPLACE "^.*(sse2).*$" "\\1" SSE_THERE ${CPUINFO})
11
-    STRING(COMPARE EQUAL "sse2" "${SSE_THERE}" SSE2_TRUE)
12
-    IF (SSE2_TRUE)
13
-        set(SSE2_FOUND true CACHE BOOL "SSE2 available on host")
14
-    ELSE (SSE2_TRUE)
15
-        set(SSE2_FOUND false CACHE BOOL "SSE2 available on host")
16
-    ENDIF (SSE2_TRUE)
17
-
18
-    # /proc/cpuinfo apparently omits sse3 :(
19
-    STRING(REGEX REPLACE "^.*[^s](sse3).*$" "\\1" SSE_THERE ${CPUINFO})
20
-    STRING(COMPARE EQUAL "sse3" "${SSE_THERE}" SSE3_TRUE)
21
-    IF (NOT SSE3_TRUE)
22
-        STRING(REGEX REPLACE "^.*(T2300).*$" "\\1" SSE_THERE ${CPUINFO})
23
-        STRING(COMPARE EQUAL "T2300" "${SSE_THERE}" SSE3_TRUE)
24
-    ENDIF (NOT SSE3_TRUE)
25
-
26
-    STRING(REGEX REPLACE "^.*(ssse3).*$" "\\1" SSE_THERE ${CPUINFO})
27
-    STRING(COMPARE EQUAL "ssse3" "${SSE_THERE}" SSSE3_TRUE)
28
-    IF (SSE3_TRUE OR SSSE3_TRUE)
29
-        set(SSE3_FOUND true CACHE BOOL "SSE3 available on host")
30
-    ELSE (SSE3_TRUE OR SSSE3_TRUE)
31
-        set(SSE3_FOUND false CACHE BOOL "SSE3 available on host")
32
-    ENDIF (SSE3_TRUE OR SSSE3_TRUE)
33
-    IF (SSSE3_TRUE)
34
-        set(SSSE3_FOUND true CACHE BOOL "SSSE3 available on host")
35
-    ELSE (SSSE3_TRUE)
36
-        set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host")
37
-    ENDIF (SSSE3_TRUE)
38
-
39
-    STRING(REGEX REPLACE "^.*(sse4_1).*$" "\\1" SSE_THERE ${CPUINFO})
40
-    STRING(COMPARE EQUAL "sse4_1" "${SSE_THERE}" SSE41_TRUE)
41
-    IF (SSE41_TRUE)
42
-        set(SSE4_1_FOUND true CACHE BOOL "SSE4.1 available on host")
43
-    ELSE (SSE41_TRUE)
44
-        set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
45
-    ENDIF (SSE41_TRUE)
46
-ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin")
47
-    EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.features" OUTPUT_VARIABLE
48
-        CPUINFO)
49
-
50
-    STRING(REGEX REPLACE "^.*[^S](SSE).*$" "\\1" SSE_THERE ${CPUINFO})
51
-    STRING(COMPARE EQUAL "SSE" "${SSE_THERE}" SSE_TRUE)
52
-    IF (SSE_TRUE)
53
-        set(SSE_FOUND true CACHE BOOL "SSE available on host")
54
-    ELSE (SSE_TRUE)
55
-        set(SSE_FOUND false CACHE BOOL "SSE available on host")
56
-    ENDIF (SSE_TRUE)
57
-
58
-    STRING(REGEX REPLACE "^.*[^S](SSE2).*$" "\\1" SSE_THERE ${CPUINFO})
59
-    STRING(COMPARE EQUAL "SSE2" "${SSE_THERE}" SSE2_TRUE)
60
-    IF (SSE2_TRUE)
61
-        set(SSE2_FOUND true CACHE BOOL "SSE2 available on host")
62
-    ELSE (SSE2_TRUE)
63
-        set(SSE2_FOUND false CACHE BOOL "SSE2 available on host")
64
-    ENDIF (SSE2_TRUE)
65
-
66
-    STRING(REGEX REPLACE "^.*[^S](SSE3).*$" "\\1" SSE_THERE ${CPUINFO})
67
-    STRING(COMPARE EQUAL "SSE3" "${SSE_THERE}" SSE3_TRUE)
68
-    IF (SSE3_TRUE)
69
-        set(SSE3_FOUND true CACHE BOOL "SSE3 available on host")
70
-    ELSE (SSE3_TRUE)
71
-        set(SSE3_FOUND false CACHE BOOL "SSE3 available on host")
72
-    ENDIF (SSE3_TRUE)
73
-
74
-    STRING(REGEX REPLACE "^.*(SSSE3).*$" "\\1" SSE_THERE ${CPUINFO})
75
-    STRING(COMPARE EQUAL "SSSE3" "${SSE_THERE}" SSSE3_TRUE)
76
-    IF (SSSE3_TRUE)
77
-        set(SSSE3_FOUND true CACHE BOOL "SSSE3 available on host")
78
-    ELSE (SSSE3_TRUE)
79
-        set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host")
80
-    ENDIF (SSSE3_TRUE)
81
-
82
-    STRING(REGEX REPLACE "^.*(SSE4.1).*$" "\\1" SSE_THERE ${CPUINFO})
83
-    STRING(COMPARE EQUAL "SSE4.1" "${SSE_THERE}" SSE41_TRUE)
84
-    IF (SSE41_TRUE)
85
-        set(SSE4_1_FOUND true CACHE BOOL "SSE4.1 available on host")
86
-    ELSE (SSE41_TRUE)
87
-        set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
88
-    ENDIF (SSE41_TRUE)
89
-ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Windows")
90
-    # TODO
91
-    set(SSE_FOUND    true  CACHE BOOL "SSE available on host")
92
-    set(SSE2_FOUND   true  CACHE BOOL "SSE2 available on host")
93
-    set(SSE3_FOUND   false CACHE BOOL "SSE3 available on host")
94
-    set(SSSE3_FOUND  false CACHE BOOL "SSSE3 available on host")
95
-    set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
96
-ELSE(CMAKE_SYSTEM_NAME MATCHES "Linux")
97
-    set(SSE_FOUND    true  CACHE BOOL "SSE available on host")
98
-    set(SSE2_FOUND   true  CACHE BOOL "SSE2 available on host")
99
-    set(SSE3_FOUND   false CACHE BOOL "SSE3 available on host")
100
-    set(SSSE3_FOUND  false CACHE BOOL "SSSE3 available on host")
101
-    set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
102
-ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
103
-
104
-if(NOT SSE_FOUND)
105
-    MESSAGE(STATUS "Could not find hardware support for SSE on this machine.")
106
-endif(NOT SSE_FOUND)
107
-
108
-if(NOT SSE2_FOUND)
109
-    MESSAGE(STATUS "Could not find hardware support for SSE2 on this machine.")
110
-endif(NOT SSE2_FOUND)
111
-
112
-if(NOT SSE3_FOUND)
113
-    MESSAGE(STATUS "Could not find hardware support for SSE3 on this machine.")
114
-endif(NOT SSE3_FOUND)
115
-
116
-if(NOT SSSE3_FOUND)
117
-    MESSAGE(STATUS "Could not find hardware support for SSSE3 on this machine.")
118
-endif(NOT SSSE3_FOUND)
119
-
120
-if(NOT SSE4_1_FOUND)
121
-    MESSAGE(STATUS "Could not find hardware support for SSE4.1 on this machine.")
122
-endif(NOT SSE4_1_FOUND)
123
-
124
-mark_as_advanced(SSE_FOUND SSE2_FOUND SSE3_FOUND SSSE3_FOUND SSE4_1_FOUND)

+ 1
- 1
include/Menu.h View File

@@ -35,7 +35,7 @@ class Menu {
35 35
 
36 36
   protected:
37 37
 
38
-    virtual void showDialog(std::string msg, std::string btn1, std::string btn2,
38
+    virtual void showDialog(std::string msg, std::string btn1, std::string btn2 = "",
39 39
                             std::function<int (bool state)> callback = std::function<int (bool)>());
40 40
 
41 41
     virtual void ackDialog();

+ 5
- 1
include/SoundManager.h View File

@@ -13,16 +13,20 @@
13 13
 class SoundSource {
14 14
   public:
15 15
     SoundSource(glm::vec3 p, int i, int f)
16
-        : pos(p), id(i), flags(f), source(-1) { }
16
+        : pos(p), id(i), flags(f), source(-1), playing(false) { }
17 17
     void prepare();
18
+    void play();
19
+    void stop();
18 20
     glm::vec3 getPos() { return pos; }
19 21
     int getID() { return id; }
20 22
     int getFlags() { return flags; }
21 23
     int getSource() { return source; }
24
+    bool isPlaying() { return playing; }
22 25
 
23 26
   private:
24 27
     glm::vec3 pos;
25 28
     int id, flags, source;
29
+    bool playing;
26 30
 };
27 31
 
28 32
 class SoundDetail {

+ 1
- 0
include/system/Sound.h View File

@@ -24,6 +24,7 @@ class Sound {
24 24
     static void listenAt(glm::vec3 pos, glm::vec3 at, glm::vec3 up);
25 25
 
26 26
     static void play(int source, bool atListener = false);
27
+    static void stop(int source);
27 28
     static void stopAll();
28 29
 
29 30
     static void setEnabled(bool on = true);

+ 1
- 0
include/system/SoundAL.h View File

@@ -26,6 +26,7 @@ class SoundAL {
26 26
     static void listenAt(glm::vec3 pos, glm::vec3 at, glm::vec3 up);
27 27
 
28 28
     static void play(int source, bool atListener);
29
+    static void stop(int source);
29 30
     static void stopAll();
30 31
 
31 32
     static void setEnabled(bool on);

+ 0
- 30
src/CMakeLists.txt View File

@@ -170,36 +170,6 @@ endif (APPLE)
170 170
 
171 171
 #################################################################
172 172
 
173
-# Check for SSE, enable if available
174
-include (../cmake/FindSSE.cmake)
175
-if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
176
-    if (SSE_FOUND)
177
-        set (SSE_FOUND_MSG "${SSE_FOUND_MSG} SSE")
178
-        set (OpenRaider_CXX_FLAGS "${OpenRaider_CXX_FLAGS} -msse")
179
-    endif (SSE_FOUND)
180
-    if (SSE2_FOUND)
181
-        set (SSE_FOUND_MSG "${SSE_FOUND_MSG} SSE2")
182
-        set (OpenRaider_CXX_FLAGS "${OpenRaider_CXX_FLAGS} -msse2")
183
-    endif (SSE2_FOUND)
184
-    if (SSE3_FOUND)
185
-        set (SSE_FOUND_MSG "${SSE_FOUND_MSG} SSE3")
186
-        set (OpenRaider_CXX_FLAGS "${OpenRaider_CXX_FLAGS} -msse3")
187
-    endif (SSE3_FOUND)
188
-    if (SSSE3_FOUND)
189
-        set (SSE_FOUND_MSG "${SSE_FOUND_MSG} SSSE3")
190
-        set (OpenRaider_CXX_FLAGS "${OpenRaider_CXX_FLAGS} -mssse3")
191
-    endif (SSSE3_FOUND)
192
-    if (SSE4_1_FOUND)
193
-        set (SSE_FOUND_MSG "${SSE_FOUND_MSG} SSE4.1")
194
-        set (OpenRaider_CXX_FLAGS "${OpenRaider_CXX_FLAGS} -msse4.1")
195
-    endif (SSE4_1_FOUND)
196
-
197
-    # Display message if something is enabled
198
-    if (NOT "${SSE_FOUND_MSG}" STREQUAL "")
199
-        message (STATUS "Enabled${SSE_FOUND_MSG}...")
200
-    endif (NOT "${SSE_FOUND_MSG}" STREQUAL "")
201
-endif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
202
-
203 173
 # Apply Flags
204 174
 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenRaider_CXX_FLAGS}")
205 175
 set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${OpenRaider_CXX_FLAGS} ${OpenRaider_CXX_FLAGS_DEBUG}")

+ 3
- 0
src/MenuFolder.cpp View File

@@ -119,8 +119,11 @@ void MenuFolder::loadOrOpen() {
119 119
             showDialog("Error reading subfolder!", "OK", "");
120 120
         }
121 121
     } else {
122
+        showDialog("Loading...", "OK");
123
+        renderFrame();
122 124
         int error = Game::loadLevel(mapFolder->getFile((unsigned long)mCursor
123 125
                                     - 1 - mapFolder->folderCount()).getPath().c_str());
126
+        ackDialog();
124 127
         if (error == 0) {
125 128
             visible = false;
126 129
         } else {

+ 92
- 9
src/SoundManager.cpp View File

@@ -32,11 +32,24 @@ void SoundSource::prepare() {
32 32
             if (ret < 0) {
33 33
                 Log::get(LOG_ERROR) << "Error positioning SoundSource " << id << Log::endl;
34 34
             }
35
-            Sound::play(source, false);
36 35
         }
37 36
     }
38 37
 }
39 38
 
39
+void SoundSource::play() {
40
+    playing = true;
41
+
42
+    if (source >= 0)
43
+        Sound::play(source, false);
44
+}
45
+
46
+void SoundSource::stop() {
47
+    playing = false;
48
+
49
+    if (source >= 0)
50
+        Sound::stop(source);
51
+}
52
+
40 53
 // ----------------------------------------------------------------------------
41 54
 
42 55
 std::vector<SoundSource> SoundManager::soundSources;
@@ -69,6 +82,7 @@ int SoundManager::prepareSources() {
69 82
 
70 83
     for (int i = 0; i < soundSources.size(); i++) {
71 84
         soundSources.at(i).prepare();
85
+        soundSources.at(i).play();
72 86
     }
73 87
 
74 88
     return 0;
@@ -142,35 +156,104 @@ int SoundManager::playSound(int index) {
142 156
 }
143 157
 
144 158
 void SoundManager::display() {
159
+    static bool offsets = false;
145 160
     if (ImGui::CollapsingHeader("Sound Sources")) {
146 161
         ImGui::Columns(5, "soundsources");
147
-        ImGui::Text("No"); ImGui::NextColumn();
148
-        ImGui::Text("ID"); ImGui::NextColumn();
149
-        ImGui::Text("Flags"); ImGui::NextColumn();
150
-        ImGui::Text("Pos"); ImGui::NextColumn();
151
-        ImGui::Text("Go"); ImGui::NextColumn();
162
+        ImGui::Text("No");
163
+        ImGui::NextColumn();
164
+        ImGui::Text("ID");
165
+        ImGui::NextColumn();
166
+        ImGui::Text("Flags");
167
+        ImGui::NextColumn();
168
+        ImGui::Text("Pos");
169
+        ImGui::NextColumn();
170
+        ImGui::Text("Tools");
171
+        ImGui::NextColumn();
152 172
         ImGui::Separator();
173
+        if (!offsets) {
174
+            ImGui::SetColumnOffset(1, 40.0f);
175
+            ImGui::SetColumnOffset(2, 80.0f);
176
+            ImGui::SetColumnOffset(3, 130.0f);
177
+            ImGui::SetColumnOffset(4, 350.0f);
178
+            offsets = true;
179
+        }
153 180
         for (int i = 0; i < soundSources.size(); i++) {
154 181
             auto& ss = soundSources.at(i);
155 182
             ImGui::Text("%03d", i);
156 183
             ImGui::NextColumn();
157 184
             ImGui::Text("%d", ss.getID());
158 185
             ImGui::NextColumn();
159
-            ImGui::Text("%X", ss.getFlags());
186
+            ImGui::Text("0x%X", ss.getFlags());
160 187
             ImGui::NextColumn();
161 188
             ImGui::Text("%.1f %.1f %.1f", ss.getPos().x, ss.getPos().y, ss.getPos().z);
162 189
             ImGui::NextColumn();
163 190
             ImGui::PushID(i);
164
-            if (ImGui::Button("Go!")) {
191
+            if (ImGui::Button("Warp")) {
165 192
                 Camera::setPosition(ss.getPos());
166 193
             }
194
+            ImGui::SameLine();
195
+            if (ImGui::Button("Play")) {
196
+                playSound(soundSources.at(i).getID());
197
+            }
198
+            ImGui::SameLine();
199
+            if (ss.isPlaying()) {
200
+                if (ImGui::Button("Stop")) {
201
+                    ss.stop();
202
+                }
203
+            } else {
204
+                if (ImGui::Button("Start")) {
205
+                    ss.play();
206
+                }
207
+            }
208
+            ImGui::PopID();
209
+            ImGui::NextColumn();
210
+        }
211
+        ImGui::Columns(1);
212
+    }
213
+
214
+    static bool offsets2 = false;
215
+    if (ImGui::CollapsingHeader("Sound Details")) {
216
+        ImGui::Columns(4, "sounddetails");
217
+        ImGui::Text("No");
218
+        ImGui::NextColumn();
219
+        ImGui::Text("Vol");
220
+        ImGui::NextColumn();
221
+        ImGui::Text("Sample");
222
+        ImGui::NextColumn();
223
+        ImGui::Text("Tools");
224
+        ImGui::NextColumn();
225
+        ImGui::Separator();
226
+        if (!offsets2) {
227
+            ImGui::SetColumnOffset(1, 40.0f);
228
+            ImGui::SetColumnOffset(2, 80.0f);
229
+            ImGui::SetColumnOffset(3, 180.0f);
230
+            offsets2 = true;
231
+        }
232
+        for (int i = 0; i < soundDetails.size(); i++) {
233
+            auto& sd = soundDetails.at(i);
234
+            ImGui::Text("%03d", i);
235
+            ImGui::NextColumn();
236
+            ImGui::Text("%.2f", sd.getVolume());
237
+            ImGui::NextColumn();
238
+            if ((sd.getSample() < 0) || (sd.getSample() >= sampleIndices.size())) {
239
+                ImGui::Text("%03d --> ???", sd.getSample());
240
+            } else {
241
+                ImGui::Text("%03d --> %03d", sd.getSample(), sampleIndices.at(sd.getSample()));
242
+            }
243
+            ImGui::NextColumn();
244
+            ImGui::PushID(i);
245
+            if (sd.getSource() >= 0) {
246
+                if (ImGui::Button("Play")) {
247
+                    Sound::play(sd.getSource(), true);
248
+                }
249
+            }
167 250
             ImGui::PopID();
168 251
             ImGui::NextColumn();
169 252
         }
170 253
         ImGui::Columns(1);
171 254
     }
172 255
 
173
-    if (ImGui::CollapsingHeader("Sound Player")) {
256
+    if (ImGui::CollapsingHeader("Sound Map Player")) {
174 257
         if (!Sound::getEnabled()) {
175 258
             ImGui::Text("Please enable Sound first!");
176 259
             if (ImGui::Button("Enable Sound!")) {

+ 2
- 2
src/UI.cpp View File

@@ -139,7 +139,7 @@ int UI::initialize() {
139 139
     style.ItemInnerSpacing                      = ImVec2(1, 1);
140 140
     style.TouchExtraPadding                     = ImVec2(0, 0);
141 141
     style.TreeNodeSpacing                       = 3;
142
-    style.ScrollBarWidth                        = 10;
142
+    style.ScrollbarWidth                        = 10;
143 143
 
144 144
     return 0;
145 145
 }
@@ -274,7 +274,7 @@ void UI::display() {
274 274
 
275 275
     static bool showTestWindow = false;
276 276
     static bool showStyleWindow = false;
277
-    if (ImGui::Begin("Engine", &visible, ImVec2(400, 400))) {
277
+    if (ImGui::Begin("Engine", &visible, ImVec2(500, 600))) {
278 278
         Render::displayUI();
279 279
         RunTime::display();
280 280
         TextureManager::display();

+ 267
- 142
src/deps/imgui/imgui.cpp View File

@@ -1,4 +1,4 @@
1
-// ImGui library v1.35 wip
1
+// ImGui library v1.35
2 2
 // See ImGui::ShowTestWindow() for sample code.
3 3
 // Read 'Programmer guide' below for notes on how to setup ImGui in your codebase.
4 4
 // Get latest version at https://github.com/ocornut/imgui
@@ -129,6 +129,7 @@
129 129
  Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
130 130
  Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
131 131
  
132
+ - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth
132 133
  - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond), kept inline redirection function.
133 134
  - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
134 135
  - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
@@ -262,8 +263,6 @@
262 263
  - main: IsItemHovered() returns true even if mouse is active on another widget (e.g. dragging outside of sliders). Maybe not a sensible default? Add parameter or alternate function?
263 264
  - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes
264 265
  - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
265
- - scrollbar: use relative mouse movement when first-clicking inside of scroll grab box.
266
- - scrollbar: make the grab visible and a minimum size for long scroll regions
267 266
 !- input number: very large int not reliably supported because of int<>float conversions.
268 267
  - input number: optional range min/max for Input*() functions
269 268
  - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
@@ -290,7 +289,7 @@
290 289
  - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID)
291 290
  - file selection widget -> build the tool in our codebase to improve model-dialog idioms
292 291
  - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt()
293
- - slider: initial absolute click is imprecise. change to relative movement slider? hide mouse cursor, allow more precise input using less screen-space.
292
+ - slider: initial absolute click is imprecise. change to relative movement slider? hide mouse cursor, allow more precise input using less screen-space. same as scrollbar.
294 293
  - text edit: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now.
295 294
  - text edit: centered text for slider as input text so it matches typical positioning.
296 295
  - text edit: flag to disable live update of the user buffer. 
@@ -393,8 +392,8 @@ namespace IMGUI_STB_NAMESPACE
393 392
 #endif
394 393
 #include "stb_truetype.h"
395 394
 
396
-#define STB_TEXTEDIT_STRING ImGuiTextEditState
397
-#define STB_TEXTEDIT_CHARTYPE ImWchar
395
+#define STB_TEXTEDIT_STRING    ImGuiTextEditState
396
+#define STB_TEXTEDIT_CHARTYPE  ImWchar
398 397
 #include "stb_textedit.h"
399 398
 
400 399
 #ifdef __clang__
@@ -410,7 +409,14 @@ using namespace IMGUI_STB_NAMESPACE;
410 409
 // Forward Declarations
411 410
 //-------------------------------------------------------------------------
412 411
 
412
+struct ImGuiColMod;
413
+struct ImGuiStyleMod;
413 414
 struct ImGuiAabb;
415
+struct ImGuiDrawContext;
416
+struct ImGuiTextEditState;
417
+struct ImGuiIniData;
418
+struct ImGuiState;
419
+struct ImGuiWindow;
414 420
 
415 421
 static bool         ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, bool repeat = false, bool pressed_on_click = false);
416 422
 static void         LogText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
@@ -431,6 +437,7 @@ static bool         IsClipped(const ImGuiAabb& bb);
431 437
 static bool         IsMouseHoveringBox(const ImGuiAabb& bb);
432 438
 static bool         IsKeyPressedMap(ImGuiKey key, bool repeat = true);
433 439
 
440
+static void         Scrollbar(ImGuiWindow* window);
434 441
 static bool         CloseWindowButton(bool* p_opened = NULL);
435 442
 static void         FocusWindow(ImGuiWindow* window);
436 443
 static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs);
@@ -454,9 +461,9 @@ static inline bool  ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c ==
454 461
 static int          ImTextCharToUtf8(char* buf, size_t buf_size, unsigned int in_char);                                // return output UTF-8 bytes count
455 462
 static ptrdiff_t    ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_text, const ImWchar* in_text_end);   // return output UTF-8 bytes count
456 463
 static int          ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);          // return input UTF-8 bytes count
457
-static ptrdiff_t    ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end);    // return input UTF-8 bytes count
464
+static ptrdiff_t    ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL);   // return input UTF-8 bytes count
458 465
 static int          ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end);                            // return number of UTF-8 code-points (NOT bytes count)
459
-static int          ImTextCountUtf8BytesFromWchar(const ImWchar* in_text, const ImWchar* in_text_end);                 // return number of bytes to express string as UTF-8 code-points
466
+static int          ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end);                   // return number of bytes to express string as UTF-8 code-points
460 467
 
461 468
 //-----------------------------------------------------------------------------
462 469
 // Platform dependent default implementations
@@ -497,7 +504,8 @@ ImGuiStyle::ImGuiStyle()
497 504
     WindowFillAlphaDefault  = 0.70f;            // Default alpha of window background, if not specified in ImGui::Begin()
498 505
     TreeNodeSpacing         = 22.0f;            // Horizontal spacing when entering a tree node
499 506
     ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns
500
-    ScrollBarWidth          = 16.0f;            // Width of the vertical scroll bar
507
+    ScrollbarWidth          = 16.0f;            // Width of the vertical scrollbar
508
+    GrabMinSize             = 10.0f;            // Minimum width/height of a slider or scrollbar grab
501 509
 
502 510
     Colors[ImGuiCol_Text]                   = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
503 511
     Colors[ImGuiCol_WindowBg]               = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
@@ -948,8 +956,9 @@ struct ImGuiTextEditState
948 956
 {
949 957
     ImGuiID             Id;                             // widget id owning the text state
950 958
     ImWchar             Text[1024];                     // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
951
-    char                InitialText[1024*3+1];          // backup of end-user buffer at the time of focus (in UTF-8, unconverted)
952
-    size_t              BufSize;                        // end-user buffer size, <= 1024 (or increase above)
959
+    char                InitialText[1024*4+1];          // backup of end-user buffer at the time of focus (in UTF-8, unaltered)
960
+    size_t              CurLenA, CurLenW;               // we need to maintain our buffer length in both UTF-8 and wchar format.
961
+    size_t              BufSizeA;                       // end-user buffer size, <= 1024 (or increase above)
953 962
     float               Width;                          // widget width
954 963
     float               ScrollX;
955 964
     STB_TexteditState   StbState;
@@ -1039,6 +1048,7 @@ struct ImGuiState
1039 1048
     ImGuiID                 SliderAsInputTextId;
1040 1049
     ImGuiStorage            ColorEditModeStorage;               // for user selection
1041 1050
     ImGuiID                 ActiveComboID;
1051
+    float                   ScrollbarClickDeltaToGrabCenter;    // distance between mouse and center of grab box, normalized in parent space
1042 1052
     char                    Tooltip[1024];
1043 1053
     char*                   PrivateClipboard;                   // if no custom clipboard handler is defined
1044 1054
 
@@ -1088,6 +1098,7 @@ struct ImGuiState
1088 1098
 
1089 1099
         SliderAsInputTextId = 0;
1090 1100
         ActiveComboID = 0;
1101
+        ScrollbarClickDeltaToGrabCenter = 0.0f;
1091 1102
         memset(Tooltip, 0, sizeof(Tooltip));
1092 1103
         PrivateClipboard = NULL;
1093 1104
 
@@ -1115,7 +1126,8 @@ struct ImGuiWindow
1115 1126
     ImVec2                  Pos;                                // Position rounded-up to nearest pixel
1116 1127
     ImVec2                  Size;                               // Current size (==SizeFull or collapsed title bar size)
1117 1128
     ImVec2                  SizeFull;                           // Size when non collapsed
1118
-    ImVec2                  SizeContentsFit;                    // Size of contents (extents reach by the drawing cursor) - may not fit within Size.
1129
+    ImVec2                  SizeContents;                       // Size of contents (== extents reach of the drawing cursor) from previous frame
1130
+    ImVec2                  SizeContentsCurrent;                // Size of contents, currently extending
1119 1131
     float                   ScrollY;
1120 1132
     float                   NextScrollY;
1121 1133
     bool                    ScrollbarY;
@@ -1457,7 +1469,7 @@ ImGuiWindow::ImGuiWindow(const char* name)
1457 1469
     Flags = 0;
1458 1470
     PosFloat = Pos = ImVec2(0.0f, 0.0f);
1459 1471
     Size = SizeFull = ImVec2(0.0f, 0.0f);
1460
-    SizeContentsFit = ImVec2(0.0f, 0.0f);
1472
+    SizeContents = SizeContentsCurrent = ImVec2(0.0f, 0.0f);
1461 1473
     ScrollY = 0.0f;
1462 1474
     NextScrollY = 0.0f;
1463 1475
     ScrollbarY = false;
@@ -1823,7 +1835,7 @@ void ImGui::NewFrame()
1823 1835
     // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application.
1824 1836
     // When clicking outside of a window we assume the click is owned by the application and won't request capture.
1825 1837
     int mouse_earliest_button_down = -1;
1826
-    for (size_t i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
1838
+    for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
1827 1839
     {
1828 1840
         if (g.IO.MouseClicked[i])
1829 1841
             g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL);
@@ -2795,6 +2807,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
2795 2807
         window->LastFrameDrawn = current_frame;
2796 2808
         window->ClipRectStack.resize(0);
2797 2809
 
2810
+        // Reset contents size for auto-fitting
2811
+        window->SizeContents = window->SizeContentsCurrent;
2812
+        window->SizeContentsCurrent = ImVec2(0.0f, 0.0f);
2813
+
2798 2814
         if (flags & ImGuiWindowFlags_ChildWindow)
2799 2815
         {
2800 2816
             parent_window->DC.ChildWindows.push_back(window);
@@ -2889,7 +2905,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
2889 2905
         window->ScrollY = window->NextScrollY;
2890 2906
         window->ScrollY = ImMax(window->ScrollY, 0.0f);
2891 2907
         if (!window->Collapsed && !window->SkipItems)
2892
-            window->ScrollY = ImMin(window->ScrollY, ImMax(0.0f, (float)window->SizeContentsFit.y - window->SizeFull.y));
2908
+            window->ScrollY = ImMin(window->ScrollY, ImMax(0.0f, window->SizeContents.y - window->SizeFull.y));
2893 2909
         window->NextScrollY = window->ScrollY;
2894 2910
 
2895 2911
         // At this point we don't have a clipping rectangle setup yet, so we can test and draw in title bar
@@ -2929,12 +2945,12 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
2929 2945
             if ((window->Flags & ImGuiWindowFlags_Tooltip) != 0)
2930 2946
             {
2931 2947
                 // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
2932
-                const ImVec2 size_auto_fit = window->SizeContentsFit + style.WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
2948
+                const ImVec2 size_auto_fit = window->SizeContents + style.WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
2933 2949
                 window->SizeFull = size_auto_fit;
2934 2950
             }
2935 2951
             else
2936 2952
             {
2937
-                const ImVec2 size_auto_fit = ImClamp(window->SizeContentsFit + style.AutoFitPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - style.AutoFitPadding));
2953
+                const ImVec2 size_auto_fit = ImClamp(window->SizeContents + style.AutoFitPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - style.AutoFitPadding));
2938 2954
                 if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
2939 2955
                 {
2940 2956
                     // Don't continuously mark settings as dirty, the size of the window doesn't need to be stored.
@@ -2982,7 +2998,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
2982 2998
             }
2983 2999
 
2984 3000
             // Scrollbar
2985
-            window->ScrollbarY = (window->SizeContentsFit.y > window->Size.y) && !(window->Flags & ImGuiWindowFlags_NoScrollbar);
3001
+            window->ScrollbarY = (window->SizeContents.y > window->Size.y) && !(window->Flags & ImGuiWindowFlags_NoScrollbar);
2986 3002
 
2987 3003
             // Window background
2988 3004
             if (bg_alpha > 0.0f)
@@ -2992,7 +3008,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
2992 3008
                 else if ((window->Flags & ImGuiWindowFlags_Tooltip) != 0)
2993 3009
                     window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_TooltipBg, bg_alpha), window_rounding);
2994 3010
                 else if ((window->Flags & ImGuiWindowFlags_ChildWindow) != 0)
2995
-                    window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size-ImVec2(window->ScrollbarY?style.ScrollBarWidth:0.0f,0.0f), window->Color(ImGuiCol_ChildWindowBg, bg_alpha), window_rounding, window->ScrollbarY ? (1|8) : (0xF));
3011
+                    window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size-ImVec2(window->ScrollbarY?style.ScrollbarWidth:0.0f,0.0f), window->Color(ImGuiCol_ChildWindowBg, bg_alpha), window_rounding, window->ScrollbarY ? (1|8) : (0xF));
2996 3012
                 else
2997 3013
                     window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_WindowBg, bg_alpha), window_rounding);
2998 3014
             }
@@ -3012,38 +3028,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
3012 3028
 
3013 3029
             // Scrollbar
3014 3030
             if (window->ScrollbarY)
3015
-            {
3016
-                ImGuiAabb scrollbar_bb(window->Aabb().Max.x - style.ScrollBarWidth, title_bar_aabb.Max.y+1, window->Aabb().Max.x, window->Aabb().Max.y-1);
3017
-                //window->DrawList->AddLine(scrollbar_bb.GetTL(), scrollbar_bb.GetBL(), g.Colors[ImGuiCol_Border]);
3018
-                window->DrawList->AddRectFilled(scrollbar_bb.Min, scrollbar_bb.Max, window->Color(ImGuiCol_ScrollbarBg));
3019
-                scrollbar_bb.Expand(ImVec2(-3,-3));
3020
-
3021
-                const float grab_size_y_norm = ImSaturate(window->Size.y / ImMax(window->SizeContentsFit.y, window->Size.y));
3022
-                const float grab_size_y = scrollbar_bb.GetHeight() * grab_size_y_norm;
3023
-
3024
-                // Handle input right away (none of the code above is relying on scrolling position)
3025
-                bool held = false;
3026
-                bool hovered = false;
3027
-                if (grab_size_y_norm < 1.0f)
3028
-                {
3029
-                    const ImGuiID scrollbar_id = window->GetID("#SCROLLY");
3030
-                    ButtonBehaviour(scrollbar_bb, scrollbar_id, &hovered, &held, true);
3031
-                    if (held)
3032
-                    {
3033
-                        g.HoveredId = scrollbar_id;
3034
-                        const float pos_y_norm = ImSaturate((g.IO.MousePos.y - (scrollbar_bb.Min.y + grab_size_y*0.5f)) / (scrollbar_bb.GetHeight() - grab_size_y)) * (1.0f - grab_size_y_norm);
3035
-                        window->ScrollY = (float)(int)(pos_y_norm * window->SizeContentsFit.y);
3036
-                        window->NextScrollY = window->ScrollY;
3037
-                    }
3038
-                }
3039
-
3040
-                // Normalized height of the grab
3041
-                const float pos_y_norm = ImSaturate(window->ScrollY / ImMax(0.0f, window->SizeContentsFit.y));
3042
-                const ImU32 grab_col = window->Color(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
3043
-                window->DrawList->AddRectFilled(
3044
-                    ImVec2(scrollbar_bb.Min.x, ImLerp(scrollbar_bb.Min.y, scrollbar_bb.Max.y, pos_y_norm)), 
3045
-                    ImVec2(scrollbar_bb.Max.x, ImLerp(scrollbar_bb.Min.y, scrollbar_bb.Max.y, pos_y_norm + grab_size_y_norm)), grab_col);
3046
-            }
3031
+                Scrollbar(window);
3047 3032
 
3048 3033
             // Render resize grip
3049 3034
             // (after the input handling so we don't have a frame of latency)
@@ -3088,8 +3073,6 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
3088 3073
         window->DC.TreeDepth = 0;
3089 3074
         window->DC.StateStorage = &window->StateStorage;
3090 3075
 
3091
-        // Reset contents size for auto-fitting
3092
-        window->SizeContentsFit = ImVec2(0.0f, 0.0f);
3093 3076
         if (window->AutoFitFrames > 0)
3094 3077
             window->AutoFitFrames--;
3095 3078
 
@@ -3121,7 +3104,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg
3121 3104
     const ImGuiAabb title_bar_aabb = window->TitleBarAabb();
3122 3105
     ImVec4 clip_rect(title_bar_aabb.Min.x+0.5f+window->WindowPadding().x*0.5f, title_bar_aabb.Max.y+0.5f, window->Aabb().Max.x+0.5f-window->WindowPadding().x*0.5f, window->Aabb().Max.y-1.5f);
3123 3106
     if (window->ScrollbarY)
3124
-        clip_rect.z -= style.ScrollBarWidth;
3107
+        clip_rect.z -= style.ScrollbarWidth;
3125 3108
     PushClipRect(clip_rect);
3126 3109
 
3127 3110
     // Clear 'accessed' flag last thing
@@ -3171,6 +3154,75 @@ void ImGui::End()
3171 3154
     g.CurrentWindow = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
3172 3155
 }
3173 3156
 
3157
+// Vertical scrollbar
3158
+// The entire piece of code below is rather confusing because:
3159
+// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
3160
+// - We store values as ratio and in a form that allows the window content to change while we are holding on a scrollbar
3161
+static void Scrollbar(ImGuiWindow* window)
3162
+{
3163
+    ImGuiState& g = *GImGui;
3164
+    const ImGuiStyle& style = g.Style;
3165
+    const ImGuiID id = window->GetID("#SCROLLY");
3166
+
3167
+    // Render background
3168
+    ImGuiAabb bb(window->Aabb().Max.x - style.ScrollbarWidth, window->Pos.y + window->TitleBarHeight()+1, window->Aabb().Max.x, window->Aabb().Max.y-1);
3169
+    window->DrawList->AddRectFilled(bb.Min, bb.Max, window->Color(ImGuiCol_ScrollbarBg));
3170
+    bb.Expand(ImVec2(-3,-3));
3171
+    const float scrollbar_height = bb.GetHeight();
3172
+
3173
+    // The grabable box size generally represent the amount visible (vs the total scrollable amount)
3174
+    // But we maintain a minimum size in pixel to allow for the user to still aim inside.
3175
+    const float grab_h_pixels = ImMax(style.GrabMinSize, scrollbar_height * ImSaturate(window->Size.y / ImMax(window->SizeContents.y, window->Size.y)));
3176
+    const float grab_h_norm = grab_h_pixels / scrollbar_height;
3177
+
3178
+    // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
3179
+    bool held = false;
3180
+    bool hovered = false;
3181
+    const bool previously_held = (g.ActiveId == id);
3182
+    ButtonBehaviour(bb, id, &hovered, &held, true);
3183
+
3184
+    const float scroll_max = ImMax(1.0f, window->SizeContents.y - window->Size.y);
3185
+    float scroll_ratio = ImSaturate(window->ScrollY / scroll_max);
3186
+    float grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height;
3187
+    if (held)
3188
+    {
3189
+        const float clicked_y_norm = ImSaturate((g.IO.MousePos.y - bb.Min.y) / scrollbar_height);     // Click position in scrollbar space (0.0f->1.0f)
3190
+        g.HoveredId = id;
3191
+
3192
+        bool seek_absolute = false;
3193
+        if (!previously_held)
3194
+        {
3195
+            // On initial click calculate the distance between mouse and the center of the grab
3196
+            if (clicked_y_norm >= grab_y_norm && clicked_y_norm <= grab_y_norm + grab_h_norm)
3197
+            {
3198
+                g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f;
3199
+            }
3200
+            else
3201
+            {
3202
+                seek_absolute = true;
3203
+                g.ScrollbarClickDeltaToGrabCenter = 0;
3204
+            }
3205
+        }
3206
+
3207
+        // Apply scroll
3208
+        const float scroll_y_norm = ImSaturate((clicked_y_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
3209
+        window->ScrollY = (float)(int)(0.5f + scroll_y_norm * (window->SizeContents.y - window->Size.y));
3210
+        window->NextScrollY = window->ScrollY;
3211
+
3212
+        // Update values for rendering
3213
+        scroll_ratio = ImSaturate(window->ScrollY / scroll_max);
3214
+        grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height;
3215
+
3216
+        // Update distance to grab now that we have seeked and saturated
3217
+        if (seek_absolute)
3218
+            g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f;
3219
+    }
3220
+
3221
+    // Render
3222
+    const ImU32 grab_col = window->Color(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
3223
+    window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm) + grab_h_pixels), grab_col);
3224
+}
3225
+
3174 3226
 // Moving window to front of display (which happens to be back of our sorted list)
3175 3227
 static void FocusWindow(ImGuiWindow* window)
3176 3228
 {
@@ -3591,7 +3643,7 @@ ImVec2 ImGui::GetContentRegionMax()
3591 3643
     else
3592 3644
     {
3593 3645
         if (window->ScrollbarY)
3594
-            mx.x -= GImGui->Style.ScrollBarWidth;
3646
+            mx.x -= GImGui->Style.ScrollbarWidth;
3595 3647
     }
3596 3648
     return mx;
3597 3649
 }
@@ -3607,7 +3659,7 @@ ImVec2 ImGui::GetWindowContentRegionMax()
3607 3659
     ImGuiWindow* window = GetCurrentWindow();
3608 3660
     ImVec2 m = window->Size - window->WindowPadding();
3609 3661
     if (window->ScrollbarY)
3610
-        m.x -= GImGui->Style.ScrollBarWidth;
3662
+        m.x -= GImGui->Style.ScrollbarWidth;
3611 3663
     return m;
3612 3664
 }
3613 3665
 
@@ -3670,21 +3722,21 @@ void ImGui::SetCursorPos(const ImVec2& pos)
3670 3722
 {
3671 3723
     ImGuiWindow* window = GetCurrentWindow();
3672 3724
     window->DC.CursorPos = window->Pos + pos;
3673
-    window->SizeContentsFit = ImMax(window->SizeContentsFit, pos + ImVec2(0.0f, window->ScrollY));
3725
+    window->SizeContentsCurrent = ImMax(window->SizeContentsCurrent, pos + ImVec2(0.0f, window->ScrollY));
3674 3726
 }
3675 3727
 
3676 3728
 void ImGui::SetCursorPosX(float x)
3677 3729
 {
3678 3730
     ImGuiWindow* window = GetCurrentWindow();
3679 3731
     window->DC.CursorPos.x = window->Pos.x + x;
3680
-    window->SizeContentsFit.x = ImMax(window->SizeContentsFit.x, x);
3732
+    window->SizeContentsCurrent.x = ImMax(window->SizeContentsCurrent.x, x);
3681 3733
 }
3682 3734
 
3683 3735
 void ImGui::SetCursorPosY(float y)
3684 3736
 {
3685 3737
     ImGuiWindow* window = GetCurrentWindow();
3686 3738
     window->DC.CursorPos.y = window->Pos.y + y;
3687
-    window->SizeContentsFit.y = ImMax(window->SizeContentsFit.y, y + window->ScrollY);
3739
+    window->SizeContentsCurrent.y = ImMax(window->SizeContentsCurrent.y, y + window->ScrollY);
3688 3740
 }
3689 3741
 
3690 3742
 ImVec2 ImGui::GetCursorScreenPos()
@@ -3699,6 +3751,18 @@ void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
3699 3751
     window->DC.CursorPos = screen_pos;
3700 3752
 }
3701 3753
 
3754
+float ImGui::GetScrollPosY()
3755
+{
3756
+    ImGuiWindow* window = GetCurrentWindow();
3757
+    return window->ScrollY;
3758
+}
3759
+
3760
+float ImGui::GetScrollMaxY()
3761
+{
3762
+    ImGuiWindow* window = GetCurrentWindow();
3763
+    return window->SizeContents.y - window->SizeFull.y;
3764
+}
3765
+
3702 3766
 void ImGui::SetScrollPosHere()
3703 3767
 {
3704 3768
     ImGuiWindow* window = GetCurrentWindow();
@@ -4635,9 +4699,9 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
4635 4699
     const float grab_size_in_units = 1.0f;                                                              // In 'v' units. Probably needs to be parametrized, based on a 'v_step' value? decimal precision?
4636 4700
     float grab_size_in_pixels;
4637 4701
     if (decimal_precision > 0 || is_unbound)
4638
-        grab_size_in_pixels = 10.0f;
4702
+        grab_size_in_pixels = style.GrabMinSize;
4639 4703
     else
4640
-        grab_size_in_pixels = ImMax(grab_size_in_units * (w / (v_max-v_min+1.0f)), 8.0f);               // Integer sliders
4704
+        grab_size_in_pixels = ImMax(grab_size_in_units * (w / (v_max-v_min+1.0f)), style.GrabMinSize);  // Integer sliders
4641 4705
     const float slider_effective_w = slider_bb.GetWidth() - grab_size_in_pixels;
4642 4706
     const float slider_effective_x1 = slider_bb.Min.x + grab_size_in_pixels*0.5f;
4643 4707
     const float slider_effective_x2 = slider_bb.Max.x - grab_size_in_pixels*0.5f;
@@ -5205,17 +5269,38 @@ static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob
5205 5269
 
5206 5270
 static bool is_separator(unsigned int c)                                                          { return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
5207 5271
 #define STB_TEXTEDIT_IS_SPACE(CH)                                                                 ( ImCharIsSpace((unsigned int)CH) || is_separator((unsigned int)CH) )
5208
-static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)                    { ImWchar* dst = obj->Text+pos; const ImWchar* src = obj->Text+pos+n; while (ImWchar c = *src++) *dst++ = c; *dst = '\0'; }
5272
+static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
5273
+{
5274
+    ImWchar* dst = obj->Text + pos;
5275
+
5276
+    // We maintain our buffer length in both UTF-8 and wchar formats
5277
+    obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
5278
+    obj->CurLenW -= n;
5279
+
5280
+    // Offset remaining text
5281
+    const ImWchar* src = obj->Text + pos + n; 
5282
+    while (ImWchar c = *src++)
5283
+        *dst++ = c; 
5284
+    *dst = '\0';
5285
+}
5286
+
5209 5287
 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
5210 5288
 {
5211
-    const size_t text_len = ImStrlenW(obj->Text);
5212
-    if ((size_t)new_text_len + text_len + 1 > obj->BufSize)
5289
+    const size_t text_len = obj->CurLenW;
5290
+    if ((size_t)new_text_len + text_len + 1 > IM_ARRAYSIZE(obj->Text))
5291
+        return false;
5292
+
5293
+    const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
5294
+    if ((size_t)new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
5213 5295
         return false;
5214 5296
 
5215 5297
     if (pos != (int)text_len)
5216 5298
         memmove(obj->Text + (size_t)pos + new_text_len, obj->Text + (size_t)pos, (text_len - (size_t)pos) * sizeof(ImWchar));
5217 5299
     memcpy(obj->Text + (size_t)pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
5218
-    obj->Text[text_len + (size_t)new_text_len] = '\0';
5300
+
5301
+    obj->CurLenW += new_text_len;
5302
+    obj->CurLenA += new_text_len_utf8;
5303
+    obj->Text[obj->CurLenW] = '\0';
5219 5304
 
5220 5305
     return true;
5221 5306
 }
@@ -5310,11 +5395,15 @@ void ImGuiTextEditState::RenderTextScrolledClipped(ImFont* font, float font_size
5310 5395
     const char* text_start = GetTextPointerClippedA(font, font_size, buf, scroll_x, NULL);
5311 5396
     const char* text_end = GetTextPointerClippedA(font, font_size, text_start, width, &text_size);
5312 5397
 
5398
+    // We need to test for the possibility of malformed UTF-8 (instead of just text_end[0] != 0)
5399
+    unsigned int text_end_char = 0;
5400
+    ImTextCharFromUtf8(&text_end_char, text_end, NULL);
5401
+
5313 5402
     // Draw a little clip symbol if we've got text on either left or right of the box
5314 5403
     const char symbol_c = '~';
5315 5404
     const float symbol_w = font_size*0.40f;     // FIXME: compute correct width
5316 5405
     const float clip_begin = (text_start > buf && text_start < text_end) ? symbol_w : 0.0f;
5317
-    const float clip_end = (text_end[0] != '\0' && text_end > text_start) ? symbol_w : 0.0f;
5406
+    const float clip_end = (text_end_char != 0 && text_end > text_start) ? symbol_w : 0.0f;
5318 5407
 
5319 5408
     // Draw text
5320 5409
     RenderText(pos+ImVec2(clip_begin,0), text_start+(clip_begin>0.0f?1:0), text_end-(clip_end>0.0f?1:0), false);
@@ -5459,7 +5548,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
5459 5548
 
5460 5549
         if (flags & ImGuiInputTextFlags_CharsUppercase)
5461 5550
             if (c >= 'a' && c <= 'z')
5462
-                *p_char = (c += 'A'-'a');
5551
+                *p_char = (c += (unsigned int)('A'-'a'));
5463 5552
 
5464 5553
         if (flags & ImGuiInputTextFlags_CharsNoBlank)
5465 5554
             if (ImCharIsSpace(c))
@@ -5471,7 +5560,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
5471 5560
         ImGuiTextEditCallbackData callback_data;
5472 5561
         memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
5473 5562
         callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; 
5474
-        callback_data.EventChar = c;
5563
+        callback_data.EventChar = (ImWchar)c;
5475 5564
         callback_data.Flags = flags;
5476 5565
         callback_data.UserData = user_data;
5477 5566
         if (callback(&callback_data) != 0)
@@ -5526,8 +5615,11 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT
5526 5615
         {
5527 5616
             // Start edition
5528 5617
             // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
5618
+            // From the moment we focused we are ignoring the content of 'buf'
5529 5619
             ImFormatString(edit_state.InitialText, IM_ARRAYSIZE(edit_state.InitialText), "%s", buf);
5530
-            size_t buf_len = ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), buf, NULL);
5620
+            const char* buf_end = NULL;
5621
+            edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), buf, NULL, &buf_end);
5622
+            edit_state.CurLenA = buf_end - buf; // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
5531 5623
             edit_state.Width = w;
5532 5624
             edit_state.InputCursorScreenPos = ImVec2(-1.f,-1.f);
5533 5625
             edit_state.CursorAnimReset();
@@ -5544,9 +5636,9 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT
5544 5636
             {
5545 5637
                 // Recycle existing cursor/selection/undo stack but clamp position
5546 5638
                 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
5547
-                edit_state.StbState.cursor = ImMin(edit_state.StbState.cursor, buf_len);
5548
-                edit_state.StbState.select_start = ImMin(edit_state.StbState.select_start, buf_len);
5549
-                edit_state.StbState.select_end = ImMin(edit_state.StbState.select_end, buf_len);
5639
+                edit_state.StbState.cursor = ImMin(edit_state.StbState.cursor, (int)edit_state.CurLenW);
5640
+                edit_state.StbState.select_start = ImMin(edit_state.StbState.select_start, (int)edit_state.CurLenW);
5641
+                edit_state.StbState.select_end = ImMin(edit_state.StbState.select_end, (int)edit_state.CurLenW);
5550 5642
             }
5551 5643
             if (focus_requested_by_tab || (user_clicked && is_ctrl_down))
5552 5644
                 select_all = true;
@@ -5575,10 +5667,10 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT
5575 5667
     if (g.ActiveId == id)
5576 5668
     {
5577 5669
         // Edit in progress
5578
-        edit_state.BufSize = buf_size < IM_ARRAYSIZE(edit_state.Text) ? buf_size : IM_ARRAYSIZE(edit_state.Text);
5670
+        edit_state.BufSizeA = buf_size;
5579 5671
         edit_state.Font = window->Font();
5580 5672
         edit_state.FontSize = window->FontSize();
5581
-    
5673
+
5582 5674
         const float mx = g.IO.MousePos.x - frame_bb.Min.x - style.FramePadding.x;
5583 5675
         const float my = window->FontSize()*0.5f;   // Flatten mouse because we are doing a single-line edit
5584 5676
 
@@ -5643,7 +5735,7 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT
5643 5735
             if (g.IO.SetClipboardTextFn)
5644 5736
             {
5645 5737
                 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
5646
-                const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : (int)ImStrlenW(edit_state.Text);
5738
+                const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : (int)edit_state.CurLenW;
5647 5739
                 ImTextStrToUtf8(text_tmp_utf8, IM_ARRAYSIZE(text_tmp_utf8), edit_state.Text+ib, edit_state.Text+ie);
5648 5740
                 g.IO.SetClipboardTextFn(text_tmp_utf8);
5649 5741
             }
@@ -5659,7 +5751,7 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT
5659 5751
                 if (const char* clipboard = g.IO.GetClipboardTextFn())
5660 5752
                 {
5661 5753
                     // Remove new-line from pasted buffer
5662
-                    size_t clipboard_len = strlen(clipboard);
5754
+                    const size_t clipboard_len = strlen(clipboard);
5663 5755
                     ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar));
5664 5756
                     int clipboard_filtered_len = 0;
5665 5757
                     for (const char* s = clipboard; *s; )
@@ -5729,22 +5821,22 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT
5729 5821
                     callback_data.EventFlag = event_flag; 
5730 5822
                     callback_data.EventKey = event_key;
5731 5823
                     callback_data.Buf = text_tmp_utf8;
5732
-                    callback_data.BufSize = edit_state.BufSize;
5824
+                    callback_data.BufSize = edit_state.BufSizeA;
5733 5825
                     callback_data.BufDirty = false;
5734 5826
                     callback_data.Flags = flags;
5735 5827
                     callback_data.UserData = user_data;
5736 5828
 
5737 5829
                     // We have to convert from position from wchar to UTF-8 positions
5738
-                    const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.cursor);
5739
-                    const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.select_start);
5740
-                    const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.select_end);
5830
+                    const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.cursor);
5831
+                    const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_start);
5832
+                    const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_end);
5741 5833
 
5742 5834
                     // Call user code
5743 5835
                     callback(&callback_data);
5744 5836
 
5745 5837
                     // Read back what user may have modified
5746 5838
                     IM_ASSERT(callback_data.Buf == text_tmp_utf8);             // Invalid to modify those fields
5747
-                    IM_ASSERT(callback_data.BufSize == edit_state.BufSize);
5839
+                    IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
5748 5840
                     IM_ASSERT(callback_data.Flags == flags);
5749 5841
                     if (callback_data.CursorPos != utf8_cursor_pos)            edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
5750 5842
                     if (callback_data.SelectionStart != utf8_selection_start)  edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
@@ -6406,7 +6498,7 @@ static void ItemSize(ImVec2 size, ImVec2* adjust_vertical_offset)
6406 6498
     window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
6407 6499
     window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
6408 6500
 
6409
-    window->SizeContentsFit = ImMax(window->SizeContentsFit, ImVec2(window->DC.CursorPosPrevLine.x - window->Pos.x, window->DC.CursorPos.y + window->ScrollY - window->Pos.y));
6501
+    window->SizeContentsCurrent = ImMax(window->SizeContentsCurrent, ImVec2(window->DC.CursorPosPrevLine.x - window->Pos.x, window->DC.CursorPos.y + window->ScrollY - window->Pos.y));
6410 6502
 
6411 6503
     window->DC.PrevLineHeight = line_height;
6412 6504
     window->DC.CurrentLineHeight = 0.0f;
@@ -6536,7 +6628,7 @@ float ImGui::GetColumnOffset(int column_index)
6536 6628
     const float t = window->DC.ColumnsOffsetsT[column_index];
6537 6629
 
6538 6630
     const float min_x = window->DC.ColumnsStartX;
6539
-    const float max_x = window->Size.x - (g.Style.ScrollBarWidth);// - window->WindowPadding().x;
6631
+    const float max_x = window->Size.x - (g.Style.ScrollbarWidth);// - window->WindowPadding().x;
6540 6632
     const float offset = min_x + t * (max_x - min_x);
6541 6633
     return offset;
6542 6634
 }
@@ -6552,7 +6644,7 @@ void ImGui::SetColumnOffset(int column_index, float offset)
6552 6644
     const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index);
6553 6645
 
6554 6646
     const float min_x = window->DC.ColumnsStartX;
6555
-    const float max_x = window->Size.x - (g.Style.ScrollBarWidth);// - window->WindowPadding().x;
6647
+    const float max_x = window->Size.x - (g.Style.ScrollbarWidth);// - window->WindowPadding().x;
6556 6648
     const float t = (offset - min_x) / (max_x - min_x);
6557 6649
     window->DC.StateStorage->SetFloat(column_id, t);
6558 6650
     window->DC.ColumnsOffsetsT[column_index] = t;
@@ -7536,8 +7628,7 @@ const ImWchar*  ImFontAtlas::GetGlyphRangesChinese()
7536 7628
     static const ImWchar ranges[] =
7537 7629
     {
7538 7630
         0x0020, 0x00FF, // Basic Latin + Latin Supplement
7539
-        0x3000, 0x3000, // Ideographic Space
7540
-        0x3040, 0x30FF, // Hiragana, Katakana
7631
+        0x3000, 0x30FF, // Punctuations, Hiragana, Katakana
7541 7632
         0x31F0, 0x31FF, // Katakana Phonetic Extensions
7542 7633
         0xFF00, 0xFFEF, // Half-width characters
7543 7634
         0x4e00, 0x9FAF, // CJK Ideograms
@@ -7586,11 +7677,10 @@ const ImWchar*  ImFontAtlas::GetGlyphRangesJapanese()
7586 7677
         109,2,18,23,0,0,9,61,3,0,28,41,77,27,19,17,81,5,2,14,5,83,57,252,14,154,263,14,20,8,13,6,57,39,38,
7587 7678
     };
7588 7679
     static int ranges_unpacked = false;
7589
-    static ImWchar ranges[10 + IM_ARRAYSIZE(offsets_from_0x4E00)*2 + 1] =
7680
+    static ImWchar ranges[8 + IM_ARRAYSIZE(offsets_from_0x4E00)*2 + 1] =
7590 7681
     {
7591 7682
         0x0020, 0x00FF, // Basic Latin + Latin Supplement
7592
-        0x3000, 0x3000, // Ideographic Space
7593
-        0x3040, 0x30FF, // Hiragana, Katakana
7683
+        0x3000, 0x30FF, // Punctuations, Hiragana, Katakana
7594 7684
         0x31F0, 0x31FF, // Katakana Phonetic Extensions
7595 7685
         0xFF00, 0xFFEF, // Half-width characters
7596 7686
     };
@@ -7598,7 +7688,7 @@ const ImWchar*  ImFontAtlas::GetGlyphRangesJapanese()
7598 7688
     {
7599 7689
         // Unpack
7600 7690
         int codepoint = 0x4e00;
7601
-        ImWchar* dst = &ranges[10];
7691
+        ImWchar* dst = &ranges[8];
7602 7692
         for (int n = 0; n < IM_ARRAYSIZE(offsets_from_0x4E00); n++, dst += 2)
7603 7693
             dst[0] = dst[1] = (ImWchar)(codepoint += (offsets_from_0x4E00[n] + 1));
7604 7694
         dst[0] = 0;
@@ -7670,6 +7760,7 @@ const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const
7670 7760
 
7671 7761
 // Convert UTF-8 to 32-bits character, process single character input.
7672 7762
 // Based on stb_from_utf8() from github.com/nothings/stb/
7763
+// We handle UTF-8 decoding error by skipping forward.
7673 7764
 static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
7674 7765
 {
7675 7766
     unsigned int c = (unsigned int)-1;
@@ -7682,42 +7773,45 @@ static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const
7682 7773
     }
7683 7774
     if ((*str & 0xe0) == 0xc0) 
7684 7775
     {
7685
-        if (in_text_end && in_text_end - (const char*)str < 2) return -1;
7686
-        if (*str < 0xc2) return -1;
7776
+        *out_char = 0;
7777
+        if (in_text_end && in_text_end - (const char*)str < 2) return 0; 
7778
+        if (*str < 0xc2) return 0;
7687 7779
         c = (unsigned int)((*str++ & 0x1f) << 6);
7688
-        if ((*str & 0xc0) != 0x80) return -1;
7780
+        if ((*str & 0xc0) != 0x80) return 0;
7689 7781
         c += (*str++ & 0x3f);
7690 7782
         *out_char = c;
7691 7783
         return 2;
7692 7784
     }
7693 7785
     if ((*str & 0xf0) == 0xe0) 
7694 7786
     {
7695
-        if (in_text_end && in_text_end - (const char*)str < 3) return -1;
7696
-        if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return -1;
7697
-        if (*str == 0xed && str[1] > 0x9f) return -1; // str[1] < 0x80 is checked below
7787
+        *out_char = 0;
7788
+        if (in_text_end && in_text_end - (const char*)str < 3) return 0;
7789
+        if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 0;
7790
+        if (*str == 0xed && str[1] > 0x9f) return 0; // str[1] < 0x80 is checked below
7698 7791
         c = (unsigned int)((*str++ & 0x0f) << 12);
7699
-        if ((*str & 0xc0) != 0x80) return -1;
7792
+        if ((*str & 0xc0) != 0x80) return 0;
7700 7793
         c += (unsigned int)((*str++ & 0x3f) << 6);
7701
-        if ((*str & 0xc0) != 0x80) return -1;
7794
+        if ((*str & 0xc0) != 0x80) return 0;
7702 7795
         c += (*str++ & 0x3f);
7703 7796
         *out_char = c;
7704 7797
         return 3;
7705 7798
     }
7706 7799
     if ((*str & 0xf8) == 0xf0) 
7707 7800
     {
7708
-        if (in_text_end && in_text_end - (const char*)str < 4) return -1;
7709
-        if (*str > 0xf4) return -1;
7710
-        if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return -1;
7711
-        if (*str == 0xf4 && str[1] > 0x8f) return -1; // str[1] < 0x80 is checked below
7801
+        *out_char = 0;
7802
+        if (in_text_end && in_text_end - (const char*)str < 4) return 0;
7803
+        if (*str > 0xf4) return 0;
7804
+        if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 0;
7805
+        if (*str == 0xf4 && str[1] > 0x8f) return 0; // str[1] < 0x80 is checked below
7712 7806
         c = (unsigned int)((*str++ & 0x07) << 18);
7713
-        if ((*str & 0xc0) != 0x80) return -1;
7807
+        if ((*str & 0xc0) != 0x80) return 0;
7714 7808
         c += (unsigned int)((*str++ & 0x3f) << 12);
7715
-        if ((*str & 0xc0) != 0x80) return -1;
7809
+        if ((*str & 0xc0) != 0x80) return 0;
7716 7810
         c += (unsigned int)((*str++ & 0x3f) << 6);
7717
-        if ((*str & 0xc0) != 0x80) return -1;
7811
+        if ((*str & 0xc0) != 0x80) return 0;
7718 7812
         c += (*str++ & 0x3f);
7719 7813
         // utf-8 encodings of values used in surrogate pairs are invalid
7720
-        if ((c & 0xFFFFF800) == 0xD800) return -1;
7814
+        if ((c & 0xFFFFF800) == 0xD800) return 0;
7721 7815
         *out_char = c;
7722 7816
         return 4;
7723 7817
     }
@@ -7725,7 +7819,7 @@ static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const
7725 7819
     return 0;
7726 7820
 }
7727 7821
 
7728
-static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end)
7822
+static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
7729 7823
 {
7730 7824
     ImWchar* buf_out = buf;
7731 7825
     ImWchar* buf_end = buf + buf_size;
@@ -7733,10 +7827,14 @@ static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in
7733 7827
     {
7734 7828
         unsigned int c;
7735 7829
         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
7830
+        if (c == 0)
7831
+            break;
7736 7832
         if (c < 0x10000)    // FIXME: Losing characters that don't fit in 2 bytes
7737 7833
             *buf_out++ = (ImWchar)c;
7738 7834
     }
7739 7835
     *buf_out = 0;
7836
+    if (in_text_remaining)
7837
+        *in_text_remaining = in_text;
7740 7838
     return buf_out - buf;
7741 7839
 }
7742 7840
 
@@ -7747,6 +7845,8 @@ static int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end
7747 7845
     {
7748 7846
         unsigned int c;
7749 7847
         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
7848
+        if (c == 0)
7849
+            break;
7750 7850
         if (c < 0x10000)
7751 7851
             char_count++;
7752 7852
     }
@@ -7811,7 +7911,7 @@ static ptrdiff_t ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_t
7811 7911
     return buf_out - buf;
7812 7912
 }
7813 7913
 
7814
-static int ImTextCountUtf8BytesFromWchar(const ImWchar* in_text, const ImWchar* in_text_end)
7914
+static int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
7815 7915
 {
7816 7916
     int bytes_count = 0;
7817 7917
     while ((!in_text_end || in_text < in_text_end) && *in_text)
@@ -7857,6 +7957,8 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
7857 7957
             next_s = s + 1;
7858 7958
         else
7859 7959
             next_s = s + ImTextCharFromUtf8(&c, s, text_end);
7960
+        if (c == 0)
7961
+            break;
7860 7962
 
7861 7963
         if (c == '\n')
7862 7964
         {
@@ -7958,9 +8060,15 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
7958 8060
         // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte)
7959 8061
         unsigned int c = (unsigned int)*s;
7960 8062
         if (c < 0x80)
8063
+        {
7961 8064
             s += 1;
8065
+        }
7962 8066
         else
8067
+        {
7963 8068
             s += ImTextCharFromUtf8(&c, s, text_end);
8069
+            if (c == 0)
8070
+                break;
8071
+        }
7964 8072
         
7965 8073
         if (c == '\n')
7966 8074
         {
@@ -8090,9 +8198,15 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re
8090 8198
         // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte)
8091 8199
         unsigned int c = (unsigned int)*s;
8092 8200
         if (c < 0x80)
8201
+        {
8093 8202
             s += 1;
8203
+        }
8094 8204
         else
8205
+        {
8095 8206
             s += ImTextCharFromUtf8(&c, s, text_end);
8207
+            if (c == 0)
8208
+                break;
8209
+        }
8096 8210
 
8097 8211
         if (c == '\n')
8098 8212
         {
@@ -8190,12 +8304,16 @@ static const char* GetClipboardTextFn_DefaultImpl()
8190 8304
     }
8191 8305
     if (!OpenClipboard(NULL)) 
8192 8306
         return NULL;
8193
-    HANDLE buf_handle = GetClipboardData(CF_TEXT); 
8194
-    if (buf_handle == NULL)
8307
+    HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT); 
8308
+    if (wbuf_handle == NULL)
8195 8309
         return NULL;
8196
-    if (char* buf_global = (char*)GlobalLock(buf_handle))
8197
-        buf_local = ImStrdup(buf_global);
8198
-    GlobalUnlock(buf_handle); 
8310
+    if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
8311
+    {
8312
+        int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
8313
+        buf_local = (char*)ImGui::MemAlloc(buf_len * sizeof(char));
8314
+        ImTextStrToUtf8(buf_local, buf_len, wbuf_global, NULL);
8315
+    }
8316
+    GlobalUnlock(wbuf_handle); 
8199 8317
     CloseClipboard(); 
8200 8318
     return buf_local;
8201 8319
 }
@@ -8205,17 +8323,16 @@ static void SetClipboardTextFn_DefaultImpl(const char* text)
8205 8323
 {
8206 8324
     if (!OpenClipboard(NULL))
8207 8325
         return;
8208
-    const char* text_end = text + strlen(text);
8209
-    const int buf_length = (int)(text_end - text) + 1;
8210
-    HGLOBAL buf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)buf_length * sizeof(char)); 
8211
-    if (buf_handle == NULL)
8326
+
8327
+    const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
8328
+    HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); 
8329
+    if (wbuf_handle == NULL)
8212 8330
         return;
8213
-    char* buf_global = (char *)GlobalLock(buf_handle); 
8214
-    memcpy(buf_global, text, (size_t)(text_end - text));
8215
-    buf_global[text_end - text] = 0;
8216
-    GlobalUnlock(buf_handle); 
8331
+    ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); 
8332
+    ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
8333
+    GlobalUnlock(wbuf_handle); 
8217 8334
     EmptyClipboard();
8218
-    SetClipboardData(CF_TEXT, buf_handle);
8335
+    SetClipboardData(CF_UNICODETEXT, wbuf_handle);
8219 8336
     CloseClipboard();
8220 8337
 }
8221 8338
 
@@ -8333,7 +8450,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
8333 8450
         ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
8334 8451
         ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
8335 8452
         ImGui::SliderFloat("TreeNodeSpacing", &style.TreeNodeSpacing, 0.0f, 20.0f, "%.0f");
8336
-        ImGui::SliderFloat("ScrollBarWidth", &style.ScrollBarWidth, 0.0f, 20.0f, "%.0f");
8453
+        ImGui::SliderFloat("ScrollBarWidth", &style.ScrollbarWidth, 1.0f, 20.0f, "%.0f");
8454
+        ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
8337 8455
         ImGui::TreePop();
8338 8456
     }
8339 8457
 
@@ -8410,6 +8528,26 @@ static void ShowExampleAppCustomRendering(bool* opened);
8410 8528
 // Demonstrate ImGui features (unfortunately this makes this function a little bloated!)
8411 8529
 void ImGui::ShowTestWindow(bool* opened)
8412 8530
 {
8531
+    // Examples apps
8532
+    static bool show_app_console = false;
8533
+    static bool show_app_long_text = false;
8534
+    static bool show_app_auto_resize = false;
8535
+    static bool show_app_fixed_overlay = false;
8536
+    static bool show_app_custom_rendering = false;
8537
+    static bool show_app_manipulating_window_title = false;
8538
+    if (show_app_console)
8539
+        ShowExampleAppConsole(&show_app_console);
8540
+    if (show_app_long_text)
8541
+        ShowExampleAppLongText(&show_app_long_text);
8542
+    if (show_app_auto_resize)
8543
+        ShowExampleAppAutoResize(&show_app_auto_resize);
8544
+    if (show_app_fixed_overlay)
8545
+        ShowExampleAppFixedOverlay(&show_app_fixed_overlay);
8546
+    if (show_app_manipulating_window_title)
8547
+        ShowExampleAppManipulatingWindowTitle(&show_app_manipulating_window_title);
8548
+    if (show_app_custom_rendering)
8549
+        ShowExampleAppCustomRendering(&show_app_custom_rendering);
8550
+
8413 8551
     static bool no_titlebar = false;
8414 8552
     static bool no_border = true;
8415 8553
     static bool no_resize = false;
@@ -9114,12 +9252,6 @@ void ImGui::ShowTestWindow(bool* opened)
9114 9252
         }
9115 9253
     }
9116 9254
 
9117
-    static bool show_app_console = false;
9118
-    static bool show_app_long_text = false;
9119
-    static bool show_app_auto_resize = false;
9120
-    static bool show_app_fixed_overlay = false;
9121
-    static bool show_app_custom_rendering = false;
9122
-    static bool show_app_manipulating_window_title = false;
9123 9255
     if (ImGui::CollapsingHeader("App Examples"))
9124 9256
     {
9125 9257
         ImGui::Checkbox("Console", &show_app_console);
@@ -9129,18 +9261,6 @@ void ImGui::ShowTestWindow(bool* opened)
9129 9261
         ImGui::Checkbox("Manipulating window title", &show_app_manipulating_window_title);
9130 9262
         ImGui::Checkbox("Custom rendering", &show_app_custom_rendering);
9131 9263
     }
9132
-    if (show_app_console)
9133
-        ShowExampleAppConsole(&show_app_console);
9134
-    if (show_app_long_text)
9135
-        ShowExampleAppLongText(&show_app_long_text);
9136
-    if (show_app_auto_resize)
9137
-        ShowExampleAppAutoResize(&show_app_auto_resize);
9138
-    if (show_app_fixed_overlay)
9139
-        ShowExampleAppFixedOverlay(&show_app_fixed_overlay);
9140
-    if (show_app_manipulating_window_title)
9141
-        ShowExampleAppManipulatingWindowTitle(&show_app_manipulating_window_title);
9142
-    if (show_app_custom_rendering)
9143
-        ShowExampleAppCustomRendering(&show_app_custom_rendering);
9144 9264
 
9145 9265
     ImGui::End();
9146 9266
 }
@@ -9180,6 +9300,8 @@ static void ShowExampleAppFixedOverlay(bool* opened)
9180 9300
 
9181 9301
 static void ShowExampleAppManipulatingWindowTitle(bool* opened)
9182 9302
 {
9303
+    (void)opened;
9304
+
9183 9305
     // By default, Windows are uniquely identified by their title.
9184 9306
     // You can use the "##" and "###" markers to manipulate the display/ID. Read FAQ at the top of this file!
9185 9307
 
@@ -9205,6 +9327,7 @@ static void ShowExampleAppManipulatingWindowTitle(bool* opened)
9205 9327
 
9206 9328
 static void ShowExampleAppCustomRendering(bool* opened)
9207 9329
 {
9330
+    ImGui::SetNextWindowSize(ImVec2(300,350), ImGuiSetCond_FirstUseEver);
9208 9331
     if (!ImGui::Begin("Example: Custom Rendering", opened))
9209 9332
     {
9210 9333
         ImGui::End();
@@ -9219,7 +9342,7 @@ static void ShowExampleAppCustomRendering(bool* opened)
9219 9342
     static ImVector<ImVec2> points;
9220 9343
     static bool adding_line = false;
9221 9344
     if (ImGui::Button("Clear")) points.clear();
9222
-    if (points.size() > 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) points.pop_back(); }
9345
+    if (points.size() >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } }
9223 9346
     ImGui::Text("Left-click and drag to add lines");
9224 9347
     ImGui::Text("Right-click to undo");
9225 9348
 
@@ -9325,6 +9448,8 @@ struct ExampleAppConsole
9325 9448
         if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.size()); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); 
9326 9449
         if (ImGui::SmallButton("Add Dummy Error")) AddLog("[error] something went wrong"); ImGui::SameLine(); 
9327 9450
         if (ImGui::SmallButton("Clear")) ClearLog();
9451
+        //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
9452
+
9328 9453
         ImGui::Separator();
9329 9454
 
9330 9455
         ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0));

+ 16
- 14
src/deps/imgui/imgui.h View File

@@ -1,4 +1,4 @@
1
-// ImGui library v1.35 wip
1
+// ImGui library v1.35
2 2
 // See .cpp file for documentation.
3 3
 // See ImGui::ShowTestWindow() for sample code.
4 4
 // Read 'Programmer guide' in .cpp for notes on how to setup ImGui in your codebase.
@@ -6,17 +6,7 @@
6 6
 
7 7
 #pragma once
8 8
 
9
-struct ImDrawCmd;
10
-struct ImDrawList;
11
-struct ImFont;
12
-struct ImFontAtlas;
13
-struct ImGuiAabb;
14
-struct ImGuiIO;
15
-struct ImGuiStorage;
16
-struct ImGuiStyle;
17
-struct ImGuiWindow;
18
-
19
-#include "imconfig.h"
9
+#include "imconfig.h"       // User-editable configuration file
20 10
 #include <float.h>          // FLT_MAX
21 11
 #include <stdarg.h>         // va_list
22 12
 #include <stddef.h>         // ptrdiff_t
@@ -34,6 +24,15 @@ struct ImGuiWindow;
34 24
 #define IMGUI_API
35 25
 #endif
36 26
 
27
+// Forward declarations
28
+struct ImDrawCmd;
29
+struct ImDrawList;
30
+struct ImFont;
31
+struct ImFontAtlas;
32
+struct ImGuiIO;
33
+struct ImGuiStorage;
34
+struct ImGuiStyle;
35
+
37 36
 typedef unsigned int ImU32;
38 37
 typedef unsigned short ImWchar;     // character for display
39 38
 typedef void* ImTextureID;          // user data to refer to a texture (e.g. store your texture handle/id)
@@ -191,6 +190,8 @@ namespace ImGui
191 190
     IMGUI_API void          SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond = 0);   // set named window collapsed state.
192 191
     IMGUI_API void          SetWindowFocus(const char* name);                                              // set named window to be focused / front-most
193 192
 
193
+    IMGUI_API float         GetScrollPosY();                                                    // get scrolling position (0..GetScrollMaxY())
194
+    IMGUI_API float         GetScrollMaxY();                                                    // get maximum scrolling position == ContentSize.Y - WindowSize.Y
194 195
     IMGUI_API void          SetScrollPosHere();                                                 // adjust scrolling position to center into the current cursor position.
195 196
     IMGUI_API void          SetKeyboardFocusHere(int offset = 0);                               // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget.
196 197
     IMGUI_API void          SetStateStorage(ImGuiStorage* tree);                                // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it).
@@ -385,7 +386,7 @@ enum ImGuiWindowFlags_
385 386
     ImGuiWindowFlags_NoTitleBar             = 1 << 0,   // Disable title-bar
386 387
     ImGuiWindowFlags_NoResize               = 1 << 1,   // Disable user resizing with the lower-right grip
387 388
     ImGuiWindowFlags_NoMove                 = 1 << 2,   // Disable user moving the window
388
-    ImGuiWindowFlags_NoScrollbar            = 1 << 3,   // Disable scroll bar (window can still scroll with mouse or programatically)
389
+    ImGuiWindowFlags_NoScrollbar            = 1 << 3,   // Disable scrollbar (window can still scroll with mouse or programatically)
389 390
     ImGuiWindowFlags_NoScrollWithMouse      = 1 << 4,   // Disable user scrolling with mouse wheel
390 391
     ImGuiWindowFlags_NoCollapse             = 1 << 5,   // Disable user collapsing window by double-clicking on it
391 392
     ImGuiWindowFlags_AlwaysAutoResize       = 1 << 6,   // Resize every window to its content every frame
@@ -533,7 +534,8 @@ struct ImGuiStyle
533 534
     float       WindowFillAlphaDefault;     // Default alpha of window background, if not specified in ImGui::Begin()
534 535
     float       TreeNodeSpacing;            // Horizontal spacing when entering a tree node
535 536
     float       ColumnsMinSpacing;          // Minimum horizontal spacing between two columns
536
-    float       ScrollBarWidth;             // Width of the vertical scroll bar
537
+    float       ScrollbarWidth;             // Width of the vertical scrollbar
538
+    float       GrabMinSize;                // Minimum width/height of a slider or scrollbar grab
537 539
     ImVec4      Colors[ImGuiCol_COUNT];
538 540
 
539 541
     IMGUI_API ImGuiStyle();

+ 2
- 0
src/loader/LoaderTR2.cpp View File

@@ -1120,6 +1120,8 @@ void LoaderTR2::loadExternalSoundFile(std::string f) {
1120 1120
         return;
1121 1121
     }
1122 1122
 
1123
+    Log::get(LOG_INFO) << "LoaderTR2: Loading \"" << f << "\"" << Log::endl;
1124
+
1123 1125
     int riffCount = loadSoundFiles(sfx);
1124 1126
     if (riffCount > 0)
1125 1127
         Log::get(LOG_INFO) << "LoaderTR2: Loaded " << riffCount << " SoundSamples" << Log::endl;

+ 6
- 0
src/system/Sound.cpp View File

@@ -84,6 +84,12 @@ void Sound::play(int source, bool atListener) {
84 84
 #endif
85 85
 }
86 86
 
87
+void Sound::stop(int source) {
88
+#ifdef USING_AL
89
+    SoundAL::stop(source);
90
+#endif
91
+}
92
+
87 93
 void Sound::stopAll() {
88 94
 #ifdef USING_AL
89 95
     SoundAL::stopAll();

+ 10
- 0
src/system/SoundAL.cpp View File

@@ -222,6 +222,16 @@ void SoundAL::play(int source, bool atListener) {
222 222
     }
223 223
 }
224 224
 
225
+void SoundAL::stop(int source) {
226
+    if (!init)
227
+        return;
228
+
229
+    if ((source >= 0) && (source < sources.size()))
230
+        alSourceStop(sources.at(source));
231
+    else
232
+        Log::get(LOG_ERROR) << "SoundAL: Can't stop non-existing source!" << Log::endl;
233
+}
234
+
225 235
 void SoundAL::stopAll() {
226 236
     if (!init)
227 237
         return;

Loading…
Cancel
Save