Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/imgui/src/imgui_demo.cpp
4246 views
1
// dear imgui, v1.92.0
2
// (demo code)
3
4
// Help:
5
// - Read FAQ at http://dearimgui.com/faq
6
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
7
// - Need help integrating Dear ImGui in your codebase?
8
// - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started
9
// - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
10
// Read top of imgui.cpp and imgui.h for many details, documentation, comments, links.
11
// Get the latest version at https://github.com/ocornut/imgui
12
13
// How to easily locate code?
14
// - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools
15
// - Browse an online version the demo with code linked to hovered widgets: https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html
16
// - Find a visible string and search for it in the code!
17
18
//---------------------------------------------------
19
// PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT!
20
//---------------------------------------------------
21
// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:
22
// Think again! It is the most useful reference code that you and other coders will want to refer to and call.
23
// Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of your game/app!
24
// Also include Metrics! ItemPicker! DebugLog! and other debug features.
25
// Removing this file from your project is hindering access to documentation for everyone in your team,
26
// likely leading you to poorer usage of the library.
27
// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().
28
// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be
29
// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
30
// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.
31
// Thank you,
32
// -Your beloved friend, imgui_demo.cpp (which you won't delete)
33
34
//--------------------------------------------
35
// ABOUT THE MEANING OF THE 'static' KEYWORD:
36
//--------------------------------------------
37
// In this demo code, we frequently use 'static' variables inside functions.
38
// A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function.
39
// Think of "static int n = 0;" as "global int n = 0;" !
40
// We do this IN THE DEMO because we want:
41
// - to gather code and data in the same place.
42
// - to make the demo source code faster to read, faster to change, smaller in size.
43
// - it is also a convenient way of storing simple UI related information as long as your function
44
// doesn't need to be reentrant or used in multiple threads.
45
// This might be a pattern you will want to use in your code, but most of the data you would be working
46
// with in a complex codebase is likely going to be stored outside your functions.
47
48
//-----------------------------------------
49
// ABOUT THE CODING STYLE OF OUR DEMO CODE
50
//-----------------------------------------
51
// The Demo code in this file is designed to be easy to copy-and-paste into your application!
52
// Because of this:
53
// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.
54
// - We try to declare static variables in the local scope, as close as possible to the code using them.
55
// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.
56
// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided
57
// by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional
58
// and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
59
// Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
60
61
// Navigating this file:
62
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
63
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
64
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
65
// - You can search/grep for all sections listed in the index to find the section.
66
67
/*
68
69
Index of this file:
70
71
// [SECTION] Forward Declarations
72
// [SECTION] Helpers
73
// [SECTION] Demo Window / ShowDemoWindow()
74
// [SECTION] DemoWindowMenuBar()
75
// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
76
// [SECTION] DemoWindowWidgetsBasic()
77
// [SECTION] DemoWindowWidgetsBullets()
78
// [SECTION] DemoWindowWidgetsCollapsingHeaders()
79
// [SECTION] DemoWindowWidgetsComboBoxes()
80
// [SECTION] DemoWindowWidgetsColorAndPickers()
81
// [SECTION] DemoWindowWidgetsDataTypes()
82
// [SECTION] DemoWindowWidgetsDisableBlocks()
83
// [SECTION] DemoWindowWidgetsDragAndDrop()
84
// [SECTION] DemoWindowWidgetsDragsAndSliders()
85
// [SECTION] DemoWindowWidgetsFonts()
86
// [SECTION] DemoWindowWidgetsImages()
87
// [SECTION] DemoWindowWidgetsListBoxes()
88
// [SECTION] DemoWindowWidgetsMultiComponents()
89
// [SECTION] DemoWindowWidgetsPlotting()
90
// [SECTION] DemoWindowWidgetsProgressBars()
91
// [SECTION] DemoWindowWidgetsQueryingStatuses()
92
// [SECTION] DemoWindowWidgetsSelectables()
93
// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
94
// [SECTION] DemoWindowWidgetsTabs()
95
// [SECTION] DemoWindowWidgetsText()
96
// [SECTION] DemoWindowWidgetsTextFilter()
97
// [SECTION] DemoWindowWidgetsTextInput()
98
// [SECTION] DemoWindowWidgetsTooltips()
99
// [SECTION] DemoWindowWidgetsTreeNodes()
100
// [SECTION] DemoWindowWidgetsVerticalSliders()
101
// [SECTION] DemoWindowWidgets()
102
// [SECTION] DemoWindowLayout()
103
// [SECTION] DemoWindowPopups()
104
// [SECTION] DemoWindowTables()
105
// [SECTION] DemoWindowInputs()
106
// [SECTION] About Window / ShowAboutWindow()
107
// [SECTION] Style Editor / ShowStyleEditor()
108
// [SECTION] User Guide / ShowUserGuide()
109
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
110
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
111
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
112
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
113
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
114
// [SECTION] Example App: Long Text / ShowExampleAppLongText()
115
// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
116
// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
117
// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
118
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
119
// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()
120
// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
121
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
122
// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
123
124
*/
125
126
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
127
#define _CRT_SECURE_NO_WARNINGS
128
#endif
129
130
#include "imgui.h"
131
#ifndef IMGUI_DISABLE
132
133
// System includes
134
#include <ctype.h> // toupper
135
#include <limits.h> // INT_MIN, INT_MAX
136
#include <math.h> // sqrtf, powf, cosf, sinf, floorf, ceilf
137
#include <stdio.h> // vsnprintf, sscanf, printf
138
#include <stdlib.h> // NULL, malloc, free, atoi
139
#include <stdint.h> // intptr_t
140
#if !defined(_MSC_VER) || _MSC_VER >= 1800
141
#include <inttypes.h> // PRId64/PRIu64, not avail in some MinGW headers.
142
#endif
143
#ifdef __EMSCRIPTEN__
144
#include <emscripten/version.h> // __EMSCRIPTEN_major__ etc.
145
#endif
146
147
// Visual Studio warnings
148
#ifdef _MSC_VER
149
#pragma warning (disable: 4127) // condition expression is constant
150
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
151
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
152
#endif
153
154
// Clang/GCC warnings with -Weverything
155
#if defined(__clang__)
156
#if __has_warning("-Wunknown-warning-option")
157
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
158
#endif
159
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
160
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
161
#pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning: 'xx' is deprecated: The POSIX name for this.. // for strdup used in demo code (so user can copy & paste the code)
162
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type
163
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
164
#pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal
165
#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning: declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
166
#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used.
167
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
168
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
169
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
170
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
171
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
172
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
173
#elif defined(__GNUC__)
174
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
175
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
176
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
177
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
178
#pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure)
179
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
180
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
181
#pragma GCC diagnostic ignored "-Wmisleading-indentation" // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub.
182
#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
183
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
184
#endif
185
186
// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
187
#ifdef _WIN32
188
#define IM_NEWLINE "\r\n"
189
#else
190
#define IM_NEWLINE "\n"
191
#endif
192
193
// Helpers
194
#if defined(_MSC_VER) && !defined(snprintf)
195
#define snprintf _snprintf
196
#endif
197
#if defined(_MSC_VER) && !defined(vsnprintf)
198
#define vsnprintf _vsnprintf
199
#endif
200
201
// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013)
202
#if !defined(PRId64) && defined(_MSC_VER)
203
#define PRId64 "I64d"
204
#define PRIu64 "I64u"
205
#elif !defined(PRId64)
206
#define PRId64 "lld"
207
#define PRIu64 "llu"
208
#endif
209
210
// Helpers macros
211
// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
212
// but making an exception here as those are largely simplifying code...
213
// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
214
#define IM_MIN(A, B) (((A) < (B)) ? (A) : (B))
215
#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
216
#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
217
218
// Enforce cdecl calling convention for functions called by the standard library,
219
// in case compilation settings changed the default to e.g. __vectorcall
220
#ifndef IMGUI_CDECL
221
#ifdef _MSC_VER
222
#define IMGUI_CDECL __cdecl
223
#else
224
#define IMGUI_CDECL
225
#endif
226
#endif
227
228
//-----------------------------------------------------------------------------
229
// [SECTION] Forward Declarations
230
//-----------------------------------------------------------------------------
231
232
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS)
233
234
// Forward Declarations
235
struct ImGuiDemoWindowData;
236
static void ShowExampleAppMainMenuBar();
237
static void ShowExampleAppAssetsBrowser(bool* p_open);
238
static void ShowExampleAppConsole(bool* p_open);
239
static void ShowExampleAppCustomRendering(bool* p_open);
240
static void ShowExampleAppDocuments(bool* p_open);
241
static void ShowExampleAppLog(bool* p_open);
242
static void ShowExampleAppLayout(bool* p_open);
243
static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);
244
static void ShowExampleAppSimpleOverlay(bool* p_open);
245
static void ShowExampleAppAutoResize(bool* p_open);
246
static void ShowExampleAppConstrainedResize(bool* p_open);
247
static void ShowExampleAppFullscreen(bool* p_open);
248
static void ShowExampleAppLongText(bool* p_open);
249
static void ShowExampleAppWindowTitles(bool* p_open);
250
static void ShowExampleMenuFile();
251
252
// We split the contents of the big ShowDemoWindow() function into smaller functions
253
// (because the link time of very large functions tends to grow non-linearly)
254
static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data);
255
static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data);
256
static void DemoWindowLayout();
257
static void DemoWindowPopups();
258
static void DemoWindowTables();
259
static void DemoWindowColumns();
260
static void DemoWindowInputs();
261
262
// Helper tree functions used by Property Editor & Multi-Select demos
263
struct ExampleTreeNode;
264
static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent);
265
static void ExampleTree_DestroyNode(ExampleTreeNode* node);
266
267
//-----------------------------------------------------------------------------
268
// [SECTION] Helpers
269
//-----------------------------------------------------------------------------
270
271
// Helper to display a little (?) mark which shows a tooltip when hovered.
272
// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
273
static void HelpMarker(const char* desc)
274
{
275
ImGui::TextDisabled("(?)");
276
if (ImGui::BeginItemTooltip())
277
{
278
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
279
ImGui::TextUnformatted(desc);
280
ImGui::PopTextWrapPos();
281
ImGui::EndTooltip();
282
}
283
}
284
285
// Helper to wire demo markers located in code to an interactive browser
286
typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data);
287
extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback;
288
extern void* GImGuiDemoMarkerCallbackUserData;
289
ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL;
290
void* GImGuiDemoMarkerCallbackUserData = NULL;
291
#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
292
293
//-----------------------------------------------------------------------------
294
// [SECTION] Demo Window / ShowDemoWindow()
295
//-----------------------------------------------------------------------------
296
297
// Data to be shared across different functions of the demo.
298
struct ImGuiDemoWindowData
299
{
300
// Examples Apps (accessible from the "Examples" menu)
301
bool ShowMainMenuBar = false;
302
bool ShowAppAssetsBrowser = false;
303
bool ShowAppConsole = false;
304
bool ShowAppCustomRendering = false;
305
bool ShowAppDocuments = false;
306
bool ShowAppLog = false;
307
bool ShowAppLayout = false;
308
bool ShowAppPropertyEditor = false;
309
bool ShowAppSimpleOverlay = false;
310
bool ShowAppAutoResize = false;
311
bool ShowAppConstrainedResize = false;
312
bool ShowAppFullscreen = false;
313
bool ShowAppLongText = false;
314
bool ShowAppWindowTitles = false;
315
316
// Dear ImGui Tools (accessible from the "Tools" menu)
317
bool ShowMetrics = false;
318
bool ShowDebugLog = false;
319
bool ShowIDStackTool = false;
320
bool ShowStyleEditor = false;
321
bool ShowAbout = false;
322
323
// Other data
324
bool DisableSections = false;
325
ExampleTreeNode* DemoTree = NULL;
326
327
~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); }
328
};
329
330
// Demonstrate most Dear ImGui features (this is big function!)
331
// You may execute this function to experiment with the UI and understand what it does.
332
// You may then search for keywords in the code when you are interested by a specific feature.
333
void ImGui::ShowDemoWindow(bool* p_open)
334
{
335
// Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
336
// Most functions would normally just assert/crash if the context is missing.
337
IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!");
338
339
// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
340
IMGUI_CHECKVERSION();
341
342
// Stored data
343
static ImGuiDemoWindowData demo_data;
344
345
// Examples Apps (accessible from the "Examples" menu)
346
if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); }
347
if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); }
348
if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); }
349
if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); }
350
if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); }
351
if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); }
352
if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); }
353
if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); }
354
if (demo_data.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(&demo_data.ShowAppSimpleOverlay); }
355
if (demo_data.ShowAppAutoResize) { ShowExampleAppAutoResize(&demo_data.ShowAppAutoResize); }
356
if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo_data.ShowAppConstrainedResize); }
357
if (demo_data.ShowAppFullscreen) { ShowExampleAppFullscreen(&demo_data.ShowAppFullscreen); }
358
if (demo_data.ShowAppLongText) { ShowExampleAppLongText(&demo_data.ShowAppLongText); }
359
if (demo_data.ShowAppWindowTitles) { ShowExampleAppWindowTitles(&demo_data.ShowAppWindowTitles); }
360
361
// Dear ImGui Tools (accessible from the "Tools" menu)
362
if (demo_data.ShowMetrics) { ImGui::ShowMetricsWindow(&demo_data.ShowMetrics); }
363
if (demo_data.ShowDebugLog) { ImGui::ShowDebugLogWindow(&demo_data.ShowDebugLog); }
364
if (demo_data.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(&demo_data.ShowIDStackTool); }
365
if (demo_data.ShowAbout) { ImGui::ShowAboutWindow(&demo_data.ShowAbout); }
366
if (demo_data.ShowStyleEditor)
367
{
368
ImGui::Begin("Dear ImGui Style Editor", &demo_data.ShowStyleEditor);
369
ImGui::ShowStyleEditor();
370
ImGui::End();
371
}
372
373
// Demonstrate the various window flags. Typically you would just use the default!
374
static bool no_titlebar = false;
375
static bool no_scrollbar = false;
376
static bool no_menu = false;
377
static bool no_move = false;
378
static bool no_resize = false;
379
static bool no_collapse = false;
380
static bool no_close = false;
381
static bool no_nav = false;
382
static bool no_background = false;
383
static bool no_bring_to_front = false;
384
static bool unsaved_document = false;
385
386
ImGuiWindowFlags window_flags = 0;
387
if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar;
388
if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar;
389
if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar;
390
if (no_move) window_flags |= ImGuiWindowFlags_NoMove;
391
if (no_resize) window_flags |= ImGuiWindowFlags_NoResize;
392
if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse;
393
if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
394
if (no_background) window_flags |= ImGuiWindowFlags_NoBackground;
395
if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
396
if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument;
397
if (no_close) p_open = NULL; // Don't pass our bool* to Begin
398
399
// We specify a default position/size in case there's no data in the .ini file.
400
// We only do it to make the demo applications a little more welcoming, but typically this isn't required.
401
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
402
ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver);
403
ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver);
404
405
// Main body of the Demo window starts here.
406
if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags))
407
{
408
// Early out if the window is collapsed, as an optimization.
409
ImGui::End();
410
return;
411
}
412
413
// Most framed widgets share a common width settings. Remaining width is used for the label.
414
// The width of the frame may be changed with PushItemWidth() or SetNextItemWidth().
415
// - Positive value for absolute size, negative value for right-alignment.
416
// - The default value is about GetWindowWidth() * 0.65f.
417
// - See 'Demo->Layout->Widgets Width' for details.
418
// Here we change the frame width based on how much width we want to give to the label.
419
const float label_width_base = ImGui::GetFontSize() * 12; // Some amount of width for label, based on font size.
420
const float label_width_max = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets.
421
const float label_width = IM_MIN(label_width_base, label_width_max);
422
ImGui::PushItemWidth(-label_width); // Right-align: framed items will leave 'label_width' available for the label.
423
//ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for framed widgets, leaving 60% width for labels.
424
//ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for labels, leaving 60% width for framed widgets.
425
//ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Use XXX width for labels, leaving the rest for framed widgets.
426
427
// Menu Bar
428
DemoWindowMenuBar(&demo_data);
429
430
ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
431
ImGui::Spacing();
432
433
IMGUI_DEMO_MARKER("Help");
434
if (ImGui::CollapsingHeader("Help"))
435
{
436
ImGui::SeparatorText("ABOUT THIS DEMO:");
437
ImGui::BulletText("Sections below are demonstrating many aspects of the library.");
438
ImGui::BulletText("The \"Examples\" menu above leads to more demo contents.");
439
ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n"
440
"and Metrics/Debugger (general purpose Dear ImGui debugging tool).");
441
442
ImGui::SeparatorText("PROGRAMMER GUIDE:");
443
ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!");
444
ImGui::BulletText("See comments in imgui.cpp.");
445
ImGui::BulletText("See example applications in the examples/ folder.");
446
ImGui::BulletText("Read the FAQ at ");
447
ImGui::SameLine(0, 0);
448
ImGui::TextLinkOpenURL("https://www.dearimgui.com/faq/");
449
ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.");
450
ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.");
451
452
ImGui::SeparatorText("USER GUIDE:");
453
ImGui::ShowUserGuide();
454
}
455
456
IMGUI_DEMO_MARKER("Configuration");
457
if (ImGui::CollapsingHeader("Configuration"))
458
{
459
ImGuiIO& io = ImGui::GetIO();
460
461
if (ImGui::TreeNode("Configuration##2"))
462
{
463
ImGui::SeparatorText("General");
464
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
465
ImGui::SameLine(); HelpMarker("Enable keyboard controls.");
466
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
467
ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
468
ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", &io.ConfigFlags, ImGuiConfigFlags_NoMouse);
469
ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable mouse inputs and interactions.");
470
471
// The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:
472
if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
473
{
474
if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f)
475
{
476
ImGui::SameLine();
477
ImGui::Text("<<PRESS SPACE TO DISABLE>>");
478
}
479
// Prevent both being checked
480
if (ImGui::IsKeyPressed(ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard))
481
io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
482
}
483
484
ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
485
ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.");
486
ImGui::CheckboxFlags("io.ConfigFlags: NoKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NoKeyboard);
487
ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable keyboard inputs and interactions.");
488
489
ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue);
490
ImGui::SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.");
491
ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
492
ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).");
493
494
ImGui::SeparatorText("Keyboard/Gamepad Navigation");
495
ImGui::Checkbox("io.ConfigNavSwapGamepadButtons", &io.ConfigNavSwapGamepadButtons);
496
ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos);
497
ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
498
ImGui::Checkbox("io.ConfigNavCaptureKeyboard", &io.ConfigNavCaptureKeyboard);
499
ImGui::Checkbox("io.ConfigNavEscapeClearFocusItem", &io.ConfigNavEscapeClearFocusItem);
500
ImGui::SameLine(); HelpMarker("Pressing Escape clears focused item.");
501
ImGui::Checkbox("io.ConfigNavEscapeClearFocusWindow", &io.ConfigNavEscapeClearFocusWindow);
502
ImGui::SameLine(); HelpMarker("Pressing Escape clears focused window.");
503
ImGui::Checkbox("io.ConfigNavCursorVisibleAuto", &io.ConfigNavCursorVisibleAuto);
504
ImGui::SameLine(); HelpMarker("Using directional navigation key makes the cursor visible. Mouse click hides the cursor.");
505
ImGui::Checkbox("io.ConfigNavCursorVisibleAlways", &io.ConfigNavCursorVisibleAlways);
506
ImGui::SameLine(); HelpMarker("Navigation cursor is always visible.");
507
508
ImGui::SeparatorText("Windows");
509
ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
510
ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.");
511
ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
512
ImGui::Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]
513
ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* CTRL+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order.");
514
ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
515
ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.");
516
517
ImGui::SeparatorText("Widgets");
518
ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
519
ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).");
520
ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive);
521
ImGui::SameLine(); HelpMarker("Pressing Enter will keep item active and select contents (single-line only).");
522
ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
523
ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
524
ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors);
525
ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
526
ImGui::Text("Also see Style->Rendering for rendering options.");
527
528
// Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
529
ImGui::SeparatorText("Error Handling");
530
531
ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
532
ImGui::SameLine(); HelpMarker(
533
"Options to configure how we handle recoverable errors.\n"
534
"- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
535
"- You not are not supposed to rely on it in the course of a normal application run.\n"
536
"- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
537
"- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! "
538
"Otherwise it would severely hinder your ability to catch and correct mistakes!");
539
ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert);
540
ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog);
541
ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip);
542
if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
543
io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
544
545
// Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
546
ImGui::SeparatorText("Debug");
547
ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
548
ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
549
ImGui::Checkbox("io.ConfigDebugHighlightIdConflicts", &io.ConfigDebugHighlightIdConflicts);
550
ImGui::SameLine(); HelpMarker("Highlight and show an error message when multiple items have conflicting identifiers.");
551
ImGui::BeginDisabled();
552
ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce);
553
ImGui::EndDisabled();
554
ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover.");
555
ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop);
556
ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
557
ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss);
558
ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.");
559
ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
560
ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).");
561
562
ImGui::TreePop();
563
ImGui::Spacing();
564
}
565
566
IMGUI_DEMO_MARKER("Configuration/Backend Flags");
567
if (ImGui::TreeNode("Backend Flags"))
568
{
569
HelpMarker(
570
"Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n"
571
"Here we expose them as read-only fields to avoid breaking interactions with your backend.");
572
573
// FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?
574
ImGui::BeginDisabled();
575
ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &io.BackendFlags, ImGuiBackendFlags_HasGamepad);
576
ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
577
ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);
578
ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);
579
ImGui::CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);
580
ImGui::EndDisabled();
581
582
ImGui::TreePop();
583
ImGui::Spacing();
584
}
585
586
IMGUI_DEMO_MARKER("Configuration/Style, Fonts");
587
if (ImGui::TreeNode("Style, Fonts"))
588
{
589
ImGui::Checkbox("Style Editor", &demo_data.ShowStyleEditor);
590
ImGui::SameLine();
591
HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.");
592
ImGui::TreePop();
593
ImGui::Spacing();
594
}
595
596
IMGUI_DEMO_MARKER("Configuration/Capture, Logging");
597
if (ImGui::TreeNode("Capture/Logging"))
598
{
599
HelpMarker(
600
"The logging API redirects all text output so you can easily capture the content of "
601
"a window or a block. Tree nodes can be automatically expanded.\n"
602
"Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
603
ImGui::LogButtons();
604
605
HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output.");
606
if (ImGui::Button("Copy \"Hello, world!\" to clipboard"))
607
{
608
ImGui::LogToClipboard();
609
ImGui::LogText("Hello, world!");
610
ImGui::LogFinish();
611
}
612
ImGui::TreePop();
613
}
614
}
615
616
IMGUI_DEMO_MARKER("Window options");
617
if (ImGui::CollapsingHeader("Window options"))
618
{
619
if (ImGui::BeginTable("split", 3))
620
{
621
ImGui::TableNextColumn(); ImGui::Checkbox("No titlebar", &no_titlebar);
622
ImGui::TableNextColumn(); ImGui::Checkbox("No scrollbar", &no_scrollbar);
623
ImGui::TableNextColumn(); ImGui::Checkbox("No menu", &no_menu);
624
ImGui::TableNextColumn(); ImGui::Checkbox("No move", &no_move);
625
ImGui::TableNextColumn(); ImGui::Checkbox("No resize", &no_resize);
626
ImGui::TableNextColumn(); ImGui::Checkbox("No collapse", &no_collapse);
627
ImGui::TableNextColumn(); ImGui::Checkbox("No close", &no_close);
628
ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav);
629
ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background);
630
ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front);
631
ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document);
632
ImGui::EndTable();
633
}
634
}
635
636
// All demo contents
637
DemoWindowWidgets(&demo_data);
638
DemoWindowLayout();
639
DemoWindowPopups();
640
DemoWindowTables();
641
DemoWindowInputs();
642
643
// End of ShowDemoWindow()
644
ImGui::PopItemWidth();
645
ImGui::End();
646
}
647
648
//-----------------------------------------------------------------------------
649
// [SECTION] DemoWindowMenuBar()
650
//-----------------------------------------------------------------------------
651
652
static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
653
{
654
IMGUI_DEMO_MARKER("Menu");
655
if (ImGui::BeginMenuBar())
656
{
657
if (ImGui::BeginMenu("Menu"))
658
{
659
IMGUI_DEMO_MARKER("Menu/File");
660
ShowExampleMenuFile();
661
ImGui::EndMenu();
662
}
663
if (ImGui::BeginMenu("Examples"))
664
{
665
IMGUI_DEMO_MARKER("Menu/Examples");
666
ImGui::MenuItem("Main menu bar", NULL, &demo_data->ShowMainMenuBar);
667
668
ImGui::SeparatorText("Mini apps");
669
ImGui::MenuItem("Assets Browser", NULL, &demo_data->ShowAppAssetsBrowser);
670
ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole);
671
ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering);
672
ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments);
673
ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog);
674
ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor);
675
ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout);
676
ImGui::MenuItem("Simple overlay", NULL, &demo_data->ShowAppSimpleOverlay);
677
678
ImGui::SeparatorText("Concepts");
679
ImGui::MenuItem("Auto-resizing window", NULL, &demo_data->ShowAppAutoResize);
680
ImGui::MenuItem("Constrained-resizing window", NULL, &demo_data->ShowAppConstrainedResize);
681
ImGui::MenuItem("Fullscreen window", NULL, &demo_data->ShowAppFullscreen);
682
ImGui::MenuItem("Long text display", NULL, &demo_data->ShowAppLongText);
683
ImGui::MenuItem("Manipulating window titles", NULL, &demo_data->ShowAppWindowTitles);
684
685
ImGui::EndMenu();
686
}
687
//if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar!
688
if (ImGui::BeginMenu("Tools"))
689
{
690
IMGUI_DEMO_MARKER("Menu/Tools");
691
ImGuiIO& io = ImGui::GetIO();
692
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
693
const bool has_debug_tools = true;
694
#else
695
const bool has_debug_tools = false;
696
#endif
697
ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools);
698
if (ImGui::BeginMenu("Debug Options"))
699
{
700
ImGui::BeginDisabled(!has_debug_tools);
701
ImGui::Checkbox("Highlight ID Conflicts", &io.ConfigDebugHighlightIdConflicts);
702
ImGui::EndDisabled();
703
ImGui::Checkbox("Assert on error recovery", &io.ConfigErrorRecoveryEnableAssert);
704
ImGui::TextDisabled("(see Demo->Configuration for details & more)");
705
ImGui::EndMenu();
706
}
707
ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools);
708
ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools);
709
bool is_debugger_present = io.ConfigDebugIsDebuggerPresent;
710
if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools))// && is_debugger_present))
711
ImGui::DebugStartItemPicker();
712
if (!is_debugger_present)
713
ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable some extra features to avoid casual users crashing the application.");
714
ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor);
715
ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout);
716
717
ImGui::EndMenu();
718
}
719
ImGui::EndMenuBar();
720
}
721
}
722
723
//-----------------------------------------------------------------------------
724
// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
725
//-----------------------------------------------------------------------------
726
727
// Simple representation for a tree
728
// (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.)
729
struct ExampleTreeNode
730
{
731
// Tree structure
732
char Name[28] = "";
733
int UID = 0;
734
ExampleTreeNode* Parent = NULL;
735
ImVector<ExampleTreeNode*> Childs;
736
unsigned short IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily
737
738
// Leaf Data
739
bool HasData = false; // All leaves have data
740
bool DataMyBool = true;
741
int DataMyInt = 128;
742
ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f);
743
};
744
745
// Simple representation of struct metadata/serialization data.
746
// (this is a minimal version of what a typical advanced application may provide)
747
struct ExampleMemberInfo
748
{
749
const char* Name; // Member name
750
ImGuiDataType DataType; // Member type
751
int DataCount; // Member count (1 when scalar)
752
int Offset; // Offset inside parent structure
753
};
754
755
// Metadata description of ExampleTreeNode struct.
756
static const ExampleMemberInfo ExampleTreeNodeMemberInfos[]
757
{
758
{ "MyName", ImGuiDataType_String, 1, offsetof(ExampleTreeNode, Name) },
759
{ "MyBool", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataMyBool) },
760
{ "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataMyInt) },
761
{ "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataMyVec2) },
762
};
763
764
static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent)
765
{
766
ExampleTreeNode* node = IM_NEW(ExampleTreeNode);
767
snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name);
768
node->UID = uid;
769
node->Parent = parent;
770
node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0;
771
if (parent)
772
parent->Childs.push_back(node);
773
return node;
774
}
775
776
static void ExampleTree_DestroyNode(ExampleTreeNode* node)
777
{
778
for (ExampleTreeNode* child_node : node->Childs)
779
ExampleTree_DestroyNode(child_node);
780
IM_DELETE(node);
781
}
782
783
// Create example tree data
784
// (this allocates _many_ more times than most other code in either Dear ImGui or others demo)
785
static ExampleTreeNode* ExampleTree_CreateDemoTree()
786
{
787
static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" };
788
const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name);
789
char name_buf[NAME_MAX_LEN];
790
int uid = 0;
791
ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL);
792
const int root_items_multiplier = 2;
793
for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++)
794
{
795
snprintf(name_buf, IM_ARRAYSIZE(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier);
796
ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);
797
const int number_of_childs = (int)strlen(node_L1->Name);
798
for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
799
{
800
snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Child %d", idx_L1);
801
ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1);
802
node_L2->HasData = true;
803
if (idx_L1 == 0)
804
{
805
snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Sub-child %d", 0);
806
ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2);
807
node_L3->HasData = true;
808
}
809
}
810
}
811
return node_L0;
812
}
813
814
//-----------------------------------------------------------------------------
815
// [SECTION] DemoWindowWidgetsBasic()
816
//-----------------------------------------------------------------------------
817
818
static void DemoWindowWidgetsBasic()
819
{
820
IMGUI_DEMO_MARKER("Widgets/Basic");
821
if (ImGui::TreeNode("Basic"))
822
{
823
ImGui::SeparatorText("General");
824
825
IMGUI_DEMO_MARKER("Widgets/Basic/Button");
826
static int clicked = 0;
827
if (ImGui::Button("Button"))
828
clicked++;
829
if (clicked & 1)
830
{
831
ImGui::SameLine();
832
ImGui::Text("Thanks for clicking me!");
833
}
834
835
IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox");
836
static bool check = true;
837
ImGui::Checkbox("checkbox", &check);
838
839
IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton");
840
static int e = 0;
841
ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine();
842
ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine();
843
ImGui::RadioButton("radio c", &e, 2);
844
845
ImGui::AlignTextToFramePadding();
846
ImGui::TextLinkOpenURL("Hyperlink", "https://github.com/ocornut/imgui/wiki/Error-Handling");
847
848
// Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
849
IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)");
850
for (int i = 0; i < 7; i++)
851
{
852
if (i > 0)
853
ImGui::SameLine();
854
ImGui::PushID(i);
855
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));
856
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));
857
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));
858
ImGui::Button("Click");
859
ImGui::PopStyleColor(3);
860
ImGui::PopID();
861
}
862
863
// Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
864
// (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
865
// See 'Demo->Layout->Text Baseline Alignment' for details.
866
ImGui::AlignTextToFramePadding();
867
ImGui::Text("Hold to repeat:");
868
ImGui::SameLine();
869
870
// Arrow buttons with Repeater
871
IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)");
872
static int counter = 0;
873
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
874
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
875
if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; }
876
ImGui::SameLine(0.0f, spacing);
877
if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; }
878
ImGui::PopItemFlag();
879
ImGui::SameLine();
880
ImGui::Text("%d", counter);
881
882
ImGui::Button("Tooltip");
883
ImGui::SetItemTooltip("I am a tooltip");
884
885
ImGui::LabelText("label", "Value");
886
887
ImGui::SeparatorText("Inputs");
888
889
{
890
// To wire InputText() with std::string or any other custom string type,
891
// see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
892
IMGUI_DEMO_MARKER("Widgets/Basic/InputText");
893
static char str0[128] = "Hello, world!";
894
ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0));
895
ImGui::SameLine(); HelpMarker(
896
"USER:\n"
897
"Hold SHIFT or use mouse to select text.\n"
898
"CTRL+Left/Right to word jump.\n"
899
"CTRL+A or Double-Click to select all.\n"
900
"CTRL+X,CTRL+C,CTRL+V for clipboard.\n"
901
"CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.\n"
902
"ESCAPE to revert.\n\n"
903
"PROGRAMMER:\n"
904
"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
905
"to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
906
"in imgui_demo.cpp).");
907
908
static char str1[128] = "";
909
ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1));
910
911
IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat");
912
static int i0 = 123;
913
ImGui::InputInt("input int", &i0);
914
915
static float f0 = 0.001f;
916
ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f");
917
918
static double d0 = 999999.00000001;
919
ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f");
920
921
static float f1 = 1.e10f;
922
ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e");
923
ImGui::SameLine(); HelpMarker(
924
"You can input value using the scientific notation,\n"
925
" e.g. \"1e+8\" becomes \"100000000\".");
926
927
static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
928
ImGui::InputFloat3("input float3", vec4a);
929
}
930
931
ImGui::SeparatorText("Drags");
932
933
{
934
IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat");
935
static int i1 = 50, i2 = 42, i3 = 128;
936
ImGui::DragInt("drag int", &i1, 1);
937
ImGui::SameLine(); HelpMarker(
938
"Click and drag to edit value.\n"
939
"Hold SHIFT/ALT for faster/slower edit.\n"
940
"Double-click or CTRL+click to input value.");
941
ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp);
942
ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround);
943
944
static float f1 = 1.00f, f2 = 0.0067f;
945
ImGui::DragFloat("drag float", &f1, 0.005f);
946
ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns");
947
//ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround);
948
}
949
950
ImGui::SeparatorText("Sliders");
951
952
{
953
IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat");
954
static int i1 = 0;
955
ImGui::SliderInt("slider int", &i1, -1, 3);
956
ImGui::SameLine(); HelpMarker("CTRL+click to input value.");
957
958
static float f1 = 0.123f, f2 = 0.0f;
959
ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
960
ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic);
961
962
IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle");
963
static float angle = 0.0f;
964
ImGui::SliderAngle("slider angle", &angle);
965
966
// Using the format string to display a name instead of an integer.
967
// Here we completely omit '%d' from the format string, so it'll only display a name.
968
// This technique can also be used with DragInt().
969
IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)");
970
enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };
971
static int elem = Element_Fire;
972
const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
973
const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
974
ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here.
975
ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer.");
976
}
977
978
ImGui::SeparatorText("Selectors/Pickers");
979
980
{
981
IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4");
982
static float col1[3] = { 1.0f, 0.0f, 0.2f };
983
static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
984
ImGui::ColorEdit3("color 1", col1);
985
ImGui::SameLine(); HelpMarker(
986
"Click on the color square to open a color picker.\n"
987
"Click and hold to use drag and drop.\n"
988
"Right-click on the color square to show options.\n"
989
"CTRL+click on individual component to input value.\n");
990
991
ImGui::ColorEdit4("color 2", col2);
992
}
993
994
{
995
// Using the _simplified_ one-liner Combo() api here
996
// See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.
997
IMGUI_DEMO_MARKER("Widgets/Basic/Combo");
998
const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" };
999
static int item_current = 0;
1000
ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items));
1001
ImGui::SameLine(); HelpMarker(
1002
"Using the simplified one-liner Combo API here.\n"
1003
"Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.");
1004
}
1005
1006
{
1007
// Using the _simplified_ one-liner ListBox() api here
1008
// See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.
1009
IMGUI_DEMO_MARKER("Widgets/Basic/ListBox");
1010
const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
1011
static int item_current = 1;
1012
ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4);
1013
ImGui::SameLine(); HelpMarker(
1014
"Using the simplified one-liner ListBox API here.\n"
1015
"Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.");
1016
}
1017
1018
// Testing ImGuiOnceUponAFrame helper.
1019
//static ImGuiOnceUponAFrame once;
1020
//for (int i = 0; i < 5; i++)
1021
// if (once)
1022
// ImGui::Text("This will be displayed only once.");
1023
1024
ImGui::TreePop();
1025
}
1026
}
1027
1028
//-----------------------------------------------------------------------------
1029
// [SECTION] DemoWindowWidgetsBullets()
1030
//-----------------------------------------------------------------------------
1031
1032
static void DemoWindowWidgetsBullets()
1033
{
1034
IMGUI_DEMO_MARKER("Widgets/Bullets");
1035
if (ImGui::TreeNode("Bullets"))
1036
{
1037
ImGui::BulletText("Bullet point 1");
1038
ImGui::BulletText("Bullet point 2\nOn multiple lines");
1039
if (ImGui::TreeNode("Tree node"))
1040
{
1041
ImGui::BulletText("Another bullet point");
1042
ImGui::TreePop();
1043
}
1044
ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)");
1045
ImGui::Bullet(); ImGui::SmallButton("Button");
1046
ImGui::TreePop();
1047
}
1048
}
1049
1050
//-----------------------------------------------------------------------------
1051
// [SECTION] DemoWindowWidgetsCollapsingHeaders()
1052
//-----------------------------------------------------------------------------
1053
1054
static void DemoWindowWidgetsCollapsingHeaders()
1055
{
1056
IMGUI_DEMO_MARKER("Widgets/Collapsing Headers");
1057
if (ImGui::TreeNode("Collapsing Headers"))
1058
{
1059
static bool closable_group = true;
1060
ImGui::Checkbox("Show 2nd header", &closable_group);
1061
if (ImGui::CollapsingHeader("Header", ImGuiTreeNodeFlags_None))
1062
{
1063
ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1064
for (int i = 0; i < 5; i++)
1065
ImGui::Text("Some content %d", i);
1066
}
1067
if (ImGui::CollapsingHeader("Header with a close button", &closable_group))
1068
{
1069
ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1070
for (int i = 0; i < 5; i++)
1071
ImGui::Text("More content %d", i);
1072
}
1073
/*
1074
if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet))
1075
ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1076
*/
1077
ImGui::TreePop();
1078
}
1079
}
1080
1081
//-----------------------------------------------------------------------------
1082
// [SECTION] DemoWindowWidgetsColorAndPickers()
1083
//-----------------------------------------------------------------------------
1084
1085
static void DemoWindowWidgetsColorAndPickers()
1086
{
1087
IMGUI_DEMO_MARKER("Widgets/Color");
1088
if (ImGui::TreeNode("Color/Picker Widgets"))
1089
{
1090
static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);
1091
static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None;
1092
1093
ImGui::SeparatorText("Options");
1094
ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &base_flags, ImGuiColorEditFlags_NoAlpha);
1095
ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque);
1096
ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg);
1097
ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf);
1098
ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop);
1099
ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options.");
1100
ImGui::CheckboxFlags("ImGuiColorEditFlags_HDR", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets.");
1101
1102
IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit");
1103
ImGui::SeparatorText("Inline color editor");
1104
ImGui::Text("Color widget:");
1105
ImGui::SameLine(); HelpMarker(
1106
"Click on the color square to open a color picker.\n"
1107
"CTRL+click on individual component to input value.\n");
1108
ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags);
1109
1110
IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)");
1111
ImGui::Text("Color widget HSV with Alpha:");
1112
ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | base_flags);
1113
1114
IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)");
1115
ImGui::Text("Color widget with Float Display:");
1116
ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | base_flags);
1117
1118
IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)");
1119
ImGui::Text("Color button with Picker:");
1120
ImGui::SameLine(); HelpMarker(
1121
"With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n"
1122
"With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only "
1123
"be used for the tooltip and picker popup.");
1124
ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags);
1125
1126
IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)");
1127
ImGui::Text("Color button with Custom Picker Popup:");
1128
1129
// Generate a default palette. The palette will persist and can be edited.
1130
static bool saved_palette_init = true;
1131
static ImVec4 saved_palette[32] = {};
1132
if (saved_palette_init)
1133
{
1134
for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1135
{
1136
ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f,
1137
saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);
1138
saved_palette[n].w = 1.0f; // Alpha
1139
}
1140
saved_palette_init = false;
1141
}
1142
1143
static ImVec4 backup_color;
1144
bool open_popup = ImGui::ColorButton("MyColor##3b", color, base_flags);
1145
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
1146
open_popup |= ImGui::Button("Palette");
1147
if (open_popup)
1148
{
1149
ImGui::OpenPopup("mypicker");
1150
backup_color = color;
1151
}
1152
if (ImGui::BeginPopup("mypicker"))
1153
{
1154
ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!");
1155
ImGui::Separator();
1156
ImGui::ColorPicker4("##picker", (float*)&color, base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);
1157
ImGui::SameLine();
1158
1159
ImGui::BeginGroup(); // Lock X position
1160
ImGui::Text("Current");
1161
ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40));
1162
ImGui::Text("Previous");
1163
if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)))
1164
color = backup_color;
1165
ImGui::Separator();
1166
ImGui::Text("Palette");
1167
for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1168
{
1169
ImGui::PushID(n);
1170
if ((n % 8) != 0)
1171
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
1172
1173
ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
1174
if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, ImVec2(20, 20)))
1175
color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
1176
1177
// Allow user to drop colors into each palette entry. Note that ColorButton() is already a
1178
// drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
1179
if (ImGui::BeginDragDropTarget())
1180
{
1181
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
1182
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);
1183
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
1184
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);
1185
ImGui::EndDragDropTarget();
1186
}
1187
1188
ImGui::PopID();
1189
}
1190
ImGui::EndGroup();
1191
ImGui::EndPopup();
1192
}
1193
1194
IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)");
1195
ImGui::Text("Color button only:");
1196
static bool no_border = false;
1197
ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border);
1198
ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80));
1199
1200
IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker");
1201
ImGui::SeparatorText("Color picker");
1202
1203
static bool ref_color = false;
1204
static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);
1205
static int picker_mode = 0;
1206
static int display_mode = 0;
1207
static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar;
1208
1209
ImGui::PushID("Color picker");
1210
ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &color_picker_flags, ImGuiColorEditFlags_NoAlpha);
1211
ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaBar", &color_picker_flags, ImGuiColorEditFlags_AlphaBar);
1212
ImGui::CheckboxFlags("ImGuiColorEditFlags_NoSidePreview", &color_picker_flags, ImGuiColorEditFlags_NoSidePreview);
1213
if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview)
1214
{
1215
ImGui::SameLine();
1216
ImGui::Checkbox("With Ref Color", &ref_color);
1217
if (ref_color)
1218
{
1219
ImGui::SameLine();
1220
ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags);
1221
}
1222
}
1223
1224
ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0");
1225
ImGui::SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode.");
1226
1227
ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0");
1228
ImGui::SameLine(); HelpMarker(
1229
"ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
1230
"but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
1231
"if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
1232
1233
ImGuiColorEditFlags flags = base_flags | color_picker_flags;
1234
if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar;
1235
if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel;
1236
if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays
1237
if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode
1238
if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV;
1239
if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;
1240
ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);
1241
1242
ImGui::Text("Set defaults in code:");
1243
ImGui::SameLine(); HelpMarker(
1244
"SetColorEditOptions() is designed to allow you to set boot-time default.\n"
1245
"We don't have Push/Pop functions because you can force options on a per-widget basis if needed, "
1246
"and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid "
1247
"encouraging you to persistently save values that aren't forward-compatible.");
1248
if (ImGui::Button("Default: Uint8 + HSV + Hue Bar"))
1249
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);
1250
if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
1251
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
1252
1253
// Always display a small version of both types of pickers
1254
// (that's in order to make it more visible in the demo to people who are skimming quickly through it)
1255
ImGui::Text("Both types:");
1256
float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;
1257
ImGui::SetNextItemWidth(w);
1258
ImGui::ColorPicker3("##MyColor##5", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1259
ImGui::SameLine();
1260
ImGui::SetNextItemWidth(w);
1261
ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1262
ImGui::PopID();
1263
1264
// HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
1265
static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
1266
ImGui::Spacing();
1267
ImGui::Text("HSV encoded colors");
1268
ImGui::SameLine(); HelpMarker(
1269
"By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV "
1270
"allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the "
1271
"added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
1272
ImGui::Text("Color widget with InputHSV:");
1273
ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1274
ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1275
ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f);
1276
1277
ImGui::TreePop();
1278
}
1279
}
1280
1281
//-----------------------------------------------------------------------------
1282
// [SECTION] DemoWindowWidgetsComboBoxes()
1283
//-----------------------------------------------------------------------------
1284
1285
static void DemoWindowWidgetsComboBoxes()
1286
{
1287
IMGUI_DEMO_MARKER("Widgets/Combo");
1288
if (ImGui::TreeNode("Combo"))
1289
{
1290
// Combo Boxes are also called "Dropdown" in other systems
1291
// Expose flags as checkbox for the demo
1292
static ImGuiComboFlags flags = 0;
1293
ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft);
1294
ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo");
1295
if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton))
1296
flags &= ~ImGuiComboFlags_NoPreview; // Clear incompatible flags
1297
if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview))
1298
flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags
1299
if (ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview))
1300
flags &= ~ImGuiComboFlags_NoPreview;
1301
1302
// Override default popup height
1303
if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightSmall", &flags, ImGuiComboFlags_HeightSmall))
1304
flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall);
1305
if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightRegular", &flags, ImGuiComboFlags_HeightRegular))
1306
flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular);
1307
if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightLargest", &flags, ImGuiComboFlags_HeightLargest))
1308
flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest);
1309
1310
// Using the generic BeginCombo() API, you have full control over how to display the combo contents.
1311
// (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1312
// stored in the object itself, etc.)
1313
const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1314
static int item_selected_idx = 0; // Here we store our selection data as an index.
1315
1316
// Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[])
1317
const char* combo_preview_value = items[item_selected_idx];
1318
if (ImGui::BeginCombo("combo 1", combo_preview_value, flags))
1319
{
1320
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1321
{
1322
const bool is_selected = (item_selected_idx == n);
1323
if (ImGui::Selectable(items[n], is_selected))
1324
item_selected_idx = n;
1325
1326
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1327
if (is_selected)
1328
ImGui::SetItemDefaultFocus();
1329
}
1330
ImGui::EndCombo();
1331
}
1332
1333
// Show case embedding a filter using a simple trick: displaying the filter inside combo contents.
1334
// See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives.
1335
if (ImGui::BeginCombo("combo 2 (w/ filter)", combo_preview_value, flags))
1336
{
1337
static ImGuiTextFilter filter;
1338
if (ImGui::IsWindowAppearing())
1339
{
1340
ImGui::SetKeyboardFocusHere();
1341
filter.Clear();
1342
}
1343
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F);
1344
filter.Draw("##Filter", -FLT_MIN);
1345
1346
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1347
{
1348
const bool is_selected = (item_selected_idx == n);
1349
if (filter.PassFilter(items[n]))
1350
if (ImGui::Selectable(items[n], is_selected))
1351
item_selected_idx = n;
1352
}
1353
ImGui::EndCombo();
1354
}
1355
1356
ImGui::Spacing();
1357
ImGui::SeparatorText("One-liner variants");
1358
HelpMarker("The Combo() function is not greatly useful apart from cases were you want to embed all options in a single strings.\nFlags above don't apply to this section.");
1359
1360
// Simplified one-liner Combo() API, using values packed in a single constant string
1361
// This is a convenience for when the selection set is small and known at compile-time.
1362
static int item_current_2 = 0;
1363
ImGui::Combo("combo 3 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
1364
1365
// Simplified one-liner Combo() using an array of const char*
1366
// This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
1367
static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview
1368
ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_ARRAYSIZE(items));
1369
1370
// Simplified one-liner Combo() using an accessor function
1371
static int item_current_4 = 0;
1372
ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items));
1373
1374
ImGui::TreePop();
1375
}
1376
}
1377
1378
//-----------------------------------------------------------------------------
1379
// [SECTION] DemoWindowWidgetsDataTypes()
1380
//-----------------------------------------------------------------------------
1381
1382
static void DemoWindowWidgetsDataTypes()
1383
{
1384
IMGUI_DEMO_MARKER("Widgets/Data Types");
1385
if (ImGui::TreeNode("Data Types"))
1386
{
1387
// DragScalar/InputScalar/SliderScalar functions allow various data types
1388
// - signed/unsigned
1389
// - 8/16/32/64-bits
1390
// - integer/float/double
1391
// To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
1392
// to pass the type, and passing all arguments by pointer.
1393
// This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type.
1394
// In practice, if you frequently use a given type that is not covered by the normal API entry points,
1395
// you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
1396
// and then pass their address to the generic function. For example:
1397
// bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
1398
// {
1399
// return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
1400
// }
1401
1402
// Setup limits (as helper variables so we can take their address, as explained above)
1403
// Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
1404
#ifndef LLONG_MIN
1405
ImS64 LLONG_MIN = -9223372036854775807LL - 1;
1406
ImS64 LLONG_MAX = 9223372036854775807LL;
1407
ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);
1408
#endif
1409
const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127;
1410
const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255;
1411
const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767;
1412
const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535;
1413
const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2;
1414
const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2;
1415
const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2;
1416
const ImU64 u64_zero = 0, u64_one = 1, u64_fifty = 50, u64_min = 0, u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2;
1417
const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;
1418
const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;
1419
1420
// State
1421
static char s8_v = 127;
1422
static ImU8 u8_v = 255;
1423
static short s16_v = 32767;
1424
static ImU16 u16_v = 65535;
1425
static ImS32 s32_v = -1;
1426
static ImU32 u32_v = (ImU32)-1;
1427
static ImS64 s64_v = -1;
1428
static ImU64 u64_v = (ImU64)-1;
1429
static float f32_v = 0.123f;
1430
static double f64_v = 90000.01234567890123456789;
1431
1432
const float drag_speed = 0.2f;
1433
static bool drag_clamp = false;
1434
IMGUI_DEMO_MARKER("Widgets/Data Types/Drags");
1435
ImGui::SeparatorText("Drags");
1436
ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp);
1437
ImGui::SameLine(); HelpMarker(
1438
"As with every widget in dear imgui, we never modify values unless there is a user interaction.\n"
1439
"You can override the clamping limits by using CTRL+Click to input a value.");
1440
ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL);
1441
ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms");
1442
ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL);
1443
ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms");
1444
ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL);
1445
ImGui::DragScalar("drag s32 hex", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X");
1446
ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms");
1447
ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);
1448
ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);
1449
ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f");
1450
ImGui::DragScalar("drag float log", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", ImGuiSliderFlags_Logarithmic);
1451
ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams");
1452
ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic);
1453
1454
IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders");
1455
ImGui::SeparatorText("Sliders");
1456
ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d");
1457
ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u");
1458
ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d");
1459
ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u");
1460
ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d");
1461
ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d");
1462
ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d");
1463
ImGui::SliderScalar("slider s32 hex", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "0x%04X");
1464
ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u");
1465
ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u");
1466
ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u");
1467
ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%" PRId64);
1468
ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" PRId64);
1469
ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" PRId64);
1470
ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" PRIu64 " ms");
1471
ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" PRIu64 " ms");
1472
ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" PRIu64 " ms");
1473
ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one);
1474
ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic);
1475
ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e");
1476
ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams");
1477
ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", ImGuiSliderFlags_Logarithmic);
1478
ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams");
1479
1480
ImGui::SeparatorText("Sliders (reverse)");
1481
ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d");
1482
ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u");
1483
ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d");
1484
ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u");
1485
ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" PRId64);
1486
ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" PRIu64 " ms");
1487
1488
IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs");
1489
static bool inputs_step = true;
1490
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
1491
ImGui::SeparatorText("Inputs");
1492
ImGui::Checkbox("Show step buttons", &inputs_step);
1493
ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
1494
ImGui::CheckboxFlags("ImGuiInputTextFlags_ParseEmptyRefVal", &flags, ImGuiInputTextFlags_ParseEmptyRefVal);
1495
ImGui::CheckboxFlags("ImGuiInputTextFlags_DisplayEmptyRefVal", &flags, ImGuiInputTextFlags_DisplayEmptyRefVal);
1496
ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d", flags);
1497
ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u", flags);
1498
ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d", flags);
1499
ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u", flags);
1500
ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d", flags);
1501
ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X", flags);
1502
ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u", flags);
1503
ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", flags);
1504
ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL, NULL, NULL, flags);
1505
ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL, NULL, NULL, flags);
1506
ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL, NULL, NULL, flags);
1507
ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL, NULL, NULL, flags);
1508
1509
ImGui::TreePop();
1510
}
1511
}
1512
1513
//-----------------------------------------------------------------------------
1514
// [SECTION] DemoWindowWidgetsDisableBlocks()
1515
//-----------------------------------------------------------------------------
1516
1517
static void DemoWindowWidgetsDisableBlocks(ImGuiDemoWindowData* demo_data)
1518
{
1519
IMGUI_DEMO_MARKER("Widgets/Disable Blocks");
1520
if (ImGui::TreeNode("Disable Blocks"))
1521
{
1522
ImGui::Checkbox("Disable entire section above", &demo_data->DisableSections);
1523
ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across other sections.");
1524
ImGui::TreePop();
1525
}
1526
}
1527
1528
//-----------------------------------------------------------------------------
1529
// [SECTION] DemoWindowWidgetsDragAndDrop()
1530
//-----------------------------------------------------------------------------
1531
1532
static void DemoWindowWidgetsDragAndDrop()
1533
{
1534
IMGUI_DEMO_MARKER("Widgets/Drag and drop");
1535
if (ImGui::TreeNode("Drag and Drop"))
1536
{
1537
IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets");
1538
if (ImGui::TreeNode("Drag and drop in standard widgets"))
1539
{
1540
// ColorEdit widgets automatically act as drag source and drag target.
1541
// They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
1542
// to allow your own widgets to use colors in their drag and drop interaction.
1543
// Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
1544
HelpMarker("You can drag from the color squares.");
1545
static float col1[3] = { 1.0f, 0.0f, 0.2f };
1546
static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1547
ImGui::ColorEdit3("color 1", col1);
1548
ImGui::ColorEdit4("color 2", col2);
1549
ImGui::TreePop();
1550
}
1551
1552
IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items");
1553
if (ImGui::TreeNode("Drag and drop to copy/swap items"))
1554
{
1555
enum Mode
1556
{
1557
Mode_Copy,
1558
Mode_Move,
1559
Mode_Swap
1560
};
1561
static int mode = 0;
1562
if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();
1563
if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();
1564
if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; }
1565
static const char* names[9] =
1566
{
1567
"Bobby", "Beatrice", "Betty",
1568
"Brianna", "Barry", "Bernard",
1569
"Bibi", "Blaine", "Bryn"
1570
};
1571
for (int n = 0; n < IM_ARRAYSIZE(names); n++)
1572
{
1573
ImGui::PushID(n);
1574
if ((n % 3) != 0)
1575
ImGui::SameLine();
1576
ImGui::Button(names[n], ImVec2(60, 60));
1577
1578
// Our buttons are both drag sources and drag targets here!
1579
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
1580
{
1581
// Set payload to carry the index of our item (could be anything)
1582
ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int));
1583
1584
// Display preview (could be anything, e.g. when dragging an image we could decide to display
1585
// the filename and a small preview of the image, etc.)
1586
if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); }
1587
if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); }
1588
if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); }
1589
ImGui::EndDragDropSource();
1590
}
1591
if (ImGui::BeginDragDropTarget())
1592
{
1593
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL"))
1594
{
1595
IM_ASSERT(payload->DataSize == sizeof(int));
1596
int payload_n = *(const int*)payload->Data;
1597
if (mode == Mode_Copy)
1598
{
1599
names[n] = names[payload_n];
1600
}
1601
if (mode == Mode_Move)
1602
{
1603
names[n] = names[payload_n];
1604
names[payload_n] = "";
1605
}
1606
if (mode == Mode_Swap)
1607
{
1608
const char* tmp = names[n];
1609
names[n] = names[payload_n];
1610
names[payload_n] = tmp;
1611
}
1612
}
1613
ImGui::EndDragDropTarget();
1614
}
1615
ImGui::PopID();
1616
}
1617
ImGui::TreePop();
1618
}
1619
1620
IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)");
1621
if (ImGui::TreeNode("Drag to reorder items (simple)"))
1622
{
1623
// FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
1624
// This code was always slightly faulty but in a way which was not easily noticeable.
1625
// Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
1626
ImGui::PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true);
1627
1628
// Simple reordering
1629
HelpMarker(
1630
"We don't use the drag and drop api at all here! "
1631
"Instead we query when the item is held but not hovered, and order items accordingly.");
1632
static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
1633
for (int n = 0; n < IM_ARRAYSIZE(item_names); n++)
1634
{
1635
const char* item = item_names[n];
1636
ImGui::Selectable(item);
1637
1638
if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
1639
{
1640
int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1);
1641
if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names))
1642
{
1643
item_names[n] = item_names[n_next];
1644
item_names[n_next] = item;
1645
ImGui::ResetMouseDragDelta();
1646
}
1647
}
1648
}
1649
1650
ImGui::PopItemFlag();
1651
ImGui::TreePop();
1652
}
1653
1654
IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location");
1655
if (ImGui::TreeNode("Tooltip at target location"))
1656
{
1657
for (int n = 0; n < 2; n++)
1658
{
1659
// Drop targets
1660
ImGui::Button(n ? "drop here##1" : "drop here##0");
1661
if (ImGui::BeginDragDropTarget())
1662
{
1663
ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip;
1664
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags))
1665
{
1666
IM_UNUSED(payload);
1667
ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
1668
ImGui::SetTooltip("Cannot drop here!");
1669
}
1670
ImGui::EndDragDropTarget();
1671
}
1672
1673
// Drop source
1674
static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f };
1675
if (n == 0)
1676
ImGui::ColorButton("drag me", col4);
1677
1678
}
1679
ImGui::TreePop();
1680
}
1681
1682
ImGui::TreePop();
1683
}
1684
}
1685
1686
//-----------------------------------------------------------------------------
1687
// [SECTION] DemoWindowWidgetsDragsAndSliders()
1688
//-----------------------------------------------------------------------------
1689
1690
static void DemoWindowWidgetsDragsAndSliders()
1691
{
1692
IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags");
1693
if (ImGui::TreeNode("Drag/Slider Flags"))
1694
{
1695
// Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
1696
static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
1697
ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp);
1698
ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput);
1699
ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.");
1700
ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange);
1701
ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
1702
ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic);
1703
ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values).");
1704
ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat);
1705
ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
1706
ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput);
1707
ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
1708
ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks);
1709
ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
1710
ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround);
1711
ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)");
1712
1713
// Drags
1714
static float drag_f = 0.5f;
1715
static int drag_i = 50;
1716
ImGui::Text("Underlying float value: %f", drag_f);
1717
ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags);
1718
ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags);
1719
ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags);
1720
ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags);
1721
//ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange
1722
//ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags);
1723
ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags);
1724
1725
// Sliders
1726
static float slider_f = 0.5f;
1727
static int slider_i = 50;
1728
const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround;
1729
ImGui::Text("Underlying float value: %f", slider_f);
1730
ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders);
1731
ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders);
1732
1733
ImGui::TreePop();
1734
}
1735
}
1736
1737
//-----------------------------------------------------------------------------
1738
// [SECTION] DemoWindowWidgetsFonts()
1739
//-----------------------------------------------------------------------------
1740
1741
// Forward declare ShowFontAtlas() which isn't worth putting in public API yet
1742
namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }
1743
1744
static void DemoWindowWidgetsFonts()
1745
{
1746
IMGUI_DEMO_MARKER("Widgets/Fonts");
1747
if (ImGui::TreeNode("Fonts"))
1748
{
1749
ImFontAtlas* atlas = ImGui::GetIO().Fonts;
1750
ImGui::ShowFontAtlas(atlas);
1751
// FIXME-NEWATLAS: Provide a demo to add/create a procedural font?
1752
ImGui::TreePop();
1753
}
1754
}
1755
1756
//-----------------------------------------------------------------------------
1757
// [SECTION] DemoWindowWidgetsImages()
1758
//-----------------------------------------------------------------------------
1759
1760
static void DemoWindowWidgetsImages()
1761
{
1762
IMGUI_DEMO_MARKER("Widgets/Images");
1763
if (ImGui::TreeNode("Images"))
1764
{
1765
ImGuiIO& io = ImGui::GetIO();
1766
ImGui::TextWrapped(
1767
"Below we are displaying the font texture (which is the only texture we have access to in this demo). "
1768
"Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. "
1769
"Hover the texture for a zoomed view!");
1770
1771
// Below we are displaying the font texture because it is the only texture we have access to inside the demo!
1772
// Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers.
1773
// If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
1774
// of their respective source file to specify what they are using as texture identifier, for example:
1775
// - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer.
1776
// - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
1777
// So with the DirectX11 backend, you call ImGui::Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID.
1778
// - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
1779
// to ImGui::Image(), and gather width/height through your own functions, etc.
1780
// - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
1781
// it will help you debug issues if you are confused about it.
1782
// - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
1783
// - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
1784
// - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1785
1786
// Grab the current texture identifier used by the font atlas.
1787
ImTextureRef my_tex_id = io.Fonts->TexRef;
1788
1789
// Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.
1790
float my_tex_w = (float)io.Fonts->TexData->Width;
1791
float my_tex_h = (float)io.Fonts->TexData->Height;
1792
1793
{
1794
ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
1795
ImVec2 pos = ImGui::GetCursorScreenPos();
1796
ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
1797
ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
1798
ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
1799
ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
1800
if (ImGui::BeginItemTooltip())
1801
{
1802
float region_sz = 32.0f;
1803
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
1804
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
1805
float zoom = 4.0f;
1806
if (region_x < 0.0f) { region_x = 0.0f; }
1807
else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }
1808
if (region_y < 0.0f) { region_y = 0.0f; }
1809
else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }
1810
ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y);
1811
ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
1812
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
1813
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
1814
ImGui::ImageWithBg(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
1815
ImGui::EndTooltip();
1816
}
1817
ImGui::PopStyleVar();
1818
}
1819
1820
IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons");
1821
ImGui::TextWrapped("And now some textured buttons..");
1822
static int pressed_count = 0;
1823
for (int i = 0; i < 8; i++)
1824
{
1825
// UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.
1826
// Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.
1827
// Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1828
ImGui::PushID(i);
1829
if (i > 0)
1830
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(i - 1.0f, i - 1.0f));
1831
ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible
1832
ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left
1833
ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h); // UV coordinates for (32,32) in our texture
1834
ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background
1835
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
1836
if (ImGui::ImageButton("", my_tex_id, size, uv0, uv1, bg_col, tint_col))
1837
pressed_count += 1;
1838
if (i > 0)
1839
ImGui::PopStyleVar();
1840
ImGui::PopID();
1841
ImGui::SameLine();
1842
}
1843
ImGui::NewLine();
1844
ImGui::Text("Pressed %d times.", pressed_count);
1845
ImGui::TreePop();
1846
}
1847
}
1848
1849
//-----------------------------------------------------------------------------
1850
// [SECTION] DemoWindowWidgetsListBoxes()
1851
//-----------------------------------------------------------------------------
1852
1853
static void DemoWindowWidgetsListBoxes()
1854
{
1855
IMGUI_DEMO_MARKER("Widgets/List Boxes");
1856
if (ImGui::TreeNode("List Boxes"))
1857
{
1858
// BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild()
1859
// using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
1860
// You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild()
1861
// to always be called (inconsistent with BeginListBox()/EndListBox()).
1862
1863
// Using the generic BeginListBox() API, you have full control over how to display the combo contents.
1864
// (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1865
// stored in the object itself, etc.)
1866
const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1867
static int item_selected_idx = 0; // Here we store our selected data as an index.
1868
1869
static bool item_highlight = false;
1870
int item_highlighted_idx = -1; // Here we store our highlighted data as an index.
1871
ImGui::Checkbox("Highlight hovered item in second listbox", &item_highlight);
1872
1873
if (ImGui::BeginListBox("listbox 1"))
1874
{
1875
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1876
{
1877
const bool is_selected = (item_selected_idx == n);
1878
if (ImGui::Selectable(items[n], is_selected))
1879
item_selected_idx = n;
1880
1881
if (item_highlight && ImGui::IsItemHovered())
1882
item_highlighted_idx = n;
1883
1884
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1885
if (is_selected)
1886
ImGui::SetItemDefaultFocus();
1887
}
1888
ImGui::EndListBox();
1889
}
1890
ImGui::SameLine(); HelpMarker("Here we are sharing selection state between both boxes.");
1891
1892
// Custom size: use all width, 5 items tall
1893
ImGui::Text("Full-width:");
1894
if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
1895
{
1896
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1897
{
1898
bool is_selected = (item_selected_idx == n);
1899
ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0;
1900
if (ImGui::Selectable(items[n], is_selected, flags))
1901
item_selected_idx = n;
1902
1903
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1904
if (is_selected)
1905
ImGui::SetItemDefaultFocus();
1906
}
1907
ImGui::EndListBox();
1908
}
1909
1910
ImGui::TreePop();
1911
}
1912
}
1913
1914
//-----------------------------------------------------------------------------
1915
// [SECTION] DemoWindowWidgetsMultiComponents()
1916
//-----------------------------------------------------------------------------
1917
1918
static void DemoWindowWidgetsMultiComponents()
1919
{
1920
IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets");
1921
if (ImGui::TreeNode("Multi-component Widgets"))
1922
{
1923
static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
1924
static int vec4i[4] = { 1, 5, 100, 255 };
1925
1926
ImGui::SeparatorText("2-wide");
1927
ImGui::InputFloat2("input float2", vec4f);
1928
ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f);
1929
ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f);
1930
ImGui::InputInt2("input int2", vec4i);
1931
ImGui::DragInt2("drag int2", vec4i, 1, 0, 255);
1932
ImGui::SliderInt2("slider int2", vec4i, 0, 255);
1933
1934
ImGui::SeparatorText("3-wide");
1935
ImGui::InputFloat3("input float3", vec4f);
1936
ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f);
1937
ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f);
1938
ImGui::InputInt3("input int3", vec4i);
1939
ImGui::DragInt3("drag int3", vec4i, 1, 0, 255);
1940
ImGui::SliderInt3("slider int3", vec4i, 0, 255);
1941
1942
ImGui::SeparatorText("4-wide");
1943
ImGui::InputFloat4("input float4", vec4f);
1944
ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f);
1945
ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f);
1946
ImGui::InputInt4("input int4", vec4i);
1947
ImGui::DragInt4("drag int4", vec4i, 1, 0, 255);
1948
ImGui::SliderInt4("slider int4", vec4i, 0, 255);
1949
1950
ImGui::SeparatorText("Ranges");
1951
static float begin = 10, end = 90;
1952
static int begin_i = 100, end_i = 1000;
1953
ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_AlwaysClamp);
1954
ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units");
1955
ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units");
1956
1957
ImGui::TreePop();
1958
}
1959
}
1960
1961
//-----------------------------------------------------------------------------
1962
// [SECTION] DemoWindowWidgetsPlotting()
1963
//-----------------------------------------------------------------------------
1964
1965
static void DemoWindowWidgetsPlotting()
1966
{
1967
// Plot/Graph widgets are not very good.
1968
// Consider using a third-party library such as ImPlot: https://github.com/epezent/implot
1969
// (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)
1970
IMGUI_DEMO_MARKER("Widgets/Plotting");
1971
if (ImGui::TreeNode("Plotting"))
1972
{
1973
ImGui::Text("Need better plotting and graphing? Consider using ImPlot:");
1974
ImGui::TextLinkOpenURL("https://github.com/epezent/implot");
1975
ImGui::Separator();
1976
1977
static bool animate = true;
1978
ImGui::Checkbox("Animate", &animate);
1979
1980
// Plot as lines and plot as histogram
1981
static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
1982
ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr));
1983
ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));
1984
//ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!");
1985
1986
// Fill an array of contiguous float values to plot
1987
// Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
1988
// and the sizeof() of your structure in the "stride" parameter.
1989
static float values[90] = {};
1990
static int values_offset = 0;
1991
static double refresh_time = 0.0;
1992
if (!animate || refresh_time == 0.0)
1993
refresh_time = ImGui::GetTime();
1994
while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
1995
{
1996
static float phase = 0.0f;
1997
values[values_offset] = cosf(phase);
1998
values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
1999
phase += 0.10f * values_offset;
2000
refresh_time += 1.0f / 60.0f;
2001
}
2002
2003
// Plots can display overlay texts
2004
// (in this example, we will display an average value)
2005
{
2006
float average = 0.0f;
2007
for (int n = 0; n < IM_ARRAYSIZE(values); n++)
2008
average += values[n];
2009
average /= (float)IM_ARRAYSIZE(values);
2010
char overlay[32];
2011
sprintf(overlay, "avg %f", average);
2012
ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f));
2013
}
2014
2015
// Use functions to generate output
2016
// FIXME: This is actually VERY awkward because current plot API only pass in indices.
2017
// We probably want an API passing floats and user provide sample rate/count.
2018
struct Funcs
2019
{
2020
static float Sin(void*, int i) { return sinf(i * 0.1f); }
2021
static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }
2022
};
2023
static int func_type = 0, display_count = 70;
2024
ImGui::SeparatorText("Functions");
2025
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
2026
ImGui::Combo("func", &func_type, "Sin\0Saw\0");
2027
ImGui::SameLine();
2028
ImGui::SliderInt("Sample count", &display_count, 1, 400);
2029
float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
2030
ImGui::PlotLines("Lines##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
2031
ImGui::PlotHistogram("Histogram##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
2032
2033
ImGui::TreePop();
2034
}
2035
}
2036
2037
//-----------------------------------------------------------------------------
2038
// [SECTION] DemoWindowWidgetsProgressBars()
2039
//-----------------------------------------------------------------------------
2040
2041
static void DemoWindowWidgetsProgressBars()
2042
{
2043
IMGUI_DEMO_MARKER("Widgets/Progress Bars");
2044
if (ImGui::TreeNode("Progress Bars"))
2045
{
2046
// Animate a simple progress bar
2047
static float progress = 0.0f, progress_dir = 1.0f;
2048
progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
2049
if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; }
2050
if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; }
2051
2052
// Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
2053
// or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
2054
ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));
2055
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
2056
ImGui::Text("Progress Bar");
2057
2058
float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f);
2059
char buf[32];
2060
sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753);
2061
ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);
2062
2063
// Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value.
2064
// Adjust the factor if you want to adjust the animation speed.
2065
ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), "Searching..");
2066
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
2067
ImGui::Text("Indeterminate");
2068
2069
ImGui::TreePop();
2070
}
2071
}
2072
2073
//-----------------------------------------------------------------------------
2074
// [SECTION] DemoWindowWidgetsQueryingStatuses()
2075
//-----------------------------------------------------------------------------
2076
2077
static void DemoWindowWidgetsQueryingStatuses()
2078
{
2079
IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)");
2080
if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)"))
2081
{
2082
// Select an item type
2083
const char* item_names[] =
2084
{
2085
"Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat",
2086
"InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox"
2087
};
2088
static int item_type = 4;
2089
static bool item_disabled = false;
2090
ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names));
2091
ImGui::SameLine();
2092
HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered().");
2093
ImGui::Checkbox("Item Disabled", &item_disabled);
2094
2095
// Submit selected items so we can query their status in the code following it.
2096
bool ret = false;
2097
static bool b = false;
2098
static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
2099
static char str[16] = {};
2100
if (item_disabled)
2101
ImGui::BeginDisabled(true);
2102
if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction
2103
if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button
2104
if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater)
2105
if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox
2106
if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item
2107
if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing)
2108
if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window)
2109
if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input
2110
if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2111
if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2112
if (item_type == 10) { ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item
2113
if (item_type == 11) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)
2114
if (item_type == 12) { ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node
2115
if (item_type == 13) { ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.
2116
if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", &current, items, IM_ARRAYSIZE(items)); }
2117
if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", &current, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }
2118
2119
bool hovered_delay_none = ImGui::IsItemHovered();
2120
bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary);
2121
bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort);
2122
bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal);
2123
bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary
2124
2125
// Display the values of IsItemHovered() and other common item state functions.
2126
// Note that the ImGuiHoveredFlags_XXX flags can be combined.
2127
// Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
2128
// we query every state in a single call to avoid storing them and to simplify the code.
2129
ImGui::BulletText(
2130
"Return value = %d\n"
2131
"IsItemFocused() = %d\n"
2132
"IsItemHovered() = %d\n"
2133
"IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
2134
"IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2135
"IsItemHovered(_AllowWhenOverlappedByItem) = %d\n"
2136
"IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n"
2137
"IsItemHovered(_AllowWhenDisabled) = %d\n"
2138
"IsItemHovered(_RectOnly) = %d\n"
2139
"IsItemActive() = %d\n"
2140
"IsItemEdited() = %d\n"
2141
"IsItemActivated() = %d\n"
2142
"IsItemDeactivated() = %d\n"
2143
"IsItemDeactivatedAfterEdit() = %d\n"
2144
"IsItemVisible() = %d\n"
2145
"IsItemClicked() = %d\n"
2146
"IsItemToggledOpen() = %d\n"
2147
"GetItemRectMin() = (%.1f, %.1f)\n"
2148
"GetItemRectMax() = (%.1f, %.1f)\n"
2149
"GetItemRectSize() = (%.1f, %.1f)",
2150
ret,
2151
ImGui::IsItemFocused(),
2152
ImGui::IsItemHovered(),
2153
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2154
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2155
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem),
2156
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow),
2157
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled),
2158
ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),
2159
ImGui::IsItemActive(),
2160
ImGui::IsItemEdited(),
2161
ImGui::IsItemActivated(),
2162
ImGui::IsItemDeactivated(),
2163
ImGui::IsItemDeactivatedAfterEdit(),
2164
ImGui::IsItemVisible(),
2165
ImGui::IsItemClicked(),
2166
ImGui::IsItemToggledOpen(),
2167
ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y,
2168
ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y,
2169
ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y
2170
);
2171
ImGui::BulletText(
2172
"with Hovering Delay or Stationary test:\n"
2173
"IsItemHovered() = = %d\n"
2174
"IsItemHovered(_Stationary) = %d\n"
2175
"IsItemHovered(_DelayShort) = %d\n"
2176
"IsItemHovered(_DelayNormal) = %d\n"
2177
"IsItemHovered(_Tooltip) = %d",
2178
hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);
2179
2180
if (item_disabled)
2181
ImGui::EndDisabled();
2182
2183
char buf[1] = "";
2184
ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly);
2185
ImGui::SameLine();
2186
HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.");
2187
2188
ImGui::TreePop();
2189
}
2190
2191
IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)");
2192
if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)"))
2193
{
2194
static bool embed_all_inside_a_child_window = false;
2195
ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window);
2196
if (embed_all_inside_a_child_window)
2197
ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Borders);
2198
2199
// Testing IsWindowFocused() function with its various flags.
2200
ImGui::BulletText(
2201
"IsWindowFocused() = %d\n"
2202
"IsWindowFocused(_ChildWindows) = %d\n"
2203
"IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n"
2204
"IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
2205
"IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2206
"IsWindowFocused(_RootWindow) = %d\n"
2207
"IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n"
2208
"IsWindowFocused(_AnyWindow) = %d\n",
2209
ImGui::IsWindowFocused(),
2210
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),
2211
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),
2212
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
2213
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2214
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow),
2215
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2216
ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));
2217
2218
// Testing IsWindowHovered() function with its various flags.
2219
ImGui::BulletText(
2220
"IsWindowHovered() = %d\n"
2221
"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
2222
"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2223
"IsWindowHovered(_ChildWindows) = %d\n"
2224
"IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n"
2225
"IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
2226
"IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2227
"IsWindowHovered(_RootWindow) = %d\n"
2228
"IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
2229
"IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
2230
"IsWindowHovered(_AnyWindow) = %d\n"
2231
"IsWindowHovered(_Stationary) = %d\n",
2232
ImGui::IsWindowHovered(),
2233
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2234
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2235
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),
2236
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),
2237
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
2238
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2239
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),
2240
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2241
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2242
ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow),
2243
ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary));
2244
2245
ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Borders);
2246
ImGui::Text("This is another child window for testing the _ChildWindows flag.");
2247
ImGui::EndChild();
2248
if (embed_all_inside_a_child_window)
2249
ImGui::EndChild();
2250
2251
// Calling IsItemHovered() after begin returns the hovered status of the title bar.
2252
// This is useful in particular if you want to create a context menu associated to the title bar of a window.
2253
static bool test_window = false;
2254
ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window);
2255
if (test_window)
2256
{
2257
ImGui::Begin("Title bar Hovered/Active tests", &test_window);
2258
if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()
2259
{
2260
if (ImGui::MenuItem("Close")) { test_window = false; }
2261
ImGui::EndPopup();
2262
}
2263
ImGui::Text(
2264
"IsItemHovered() after begin = %d (== is title bar hovered)\n"
2265
"IsItemActive() after begin = %d (== is window being clicked/moved)\n",
2266
ImGui::IsItemHovered(), ImGui::IsItemActive());
2267
ImGui::End();
2268
}
2269
2270
ImGui::TreePop();
2271
}
2272
}
2273
2274
//-----------------------------------------------------------------------------
2275
// [SECTION] DemoWindowWidgetsSelectables()
2276
//-----------------------------------------------------------------------------
2277
2278
static void DemoWindowWidgetsSelectables()
2279
{
2280
IMGUI_DEMO_MARKER("Widgets/Selectables");
2281
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
2282
if (ImGui::TreeNode("Selectables"))
2283
{
2284
// Selectable() has 2 overloads:
2285
// - The one taking "bool selected" as a read-only selection information.
2286
// When Selectable() has been clicked it returns true and you can alter selection state accordingly.
2287
// - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
2288
// The earlier is more flexible, as in real application your selection may be stored in many different ways
2289
// and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
2290
IMGUI_DEMO_MARKER("Widgets/Selectables/Basic");
2291
if (ImGui::TreeNode("Basic"))
2292
{
2293
static bool selection[5] = { false, true, false, false };
2294
ImGui::Selectable("1. I am selectable", &selection[0]);
2295
ImGui::Selectable("2. I am selectable", &selection[1]);
2296
ImGui::Selectable("3. I am selectable", &selection[2]);
2297
if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick))
2298
if (ImGui::IsMouseDoubleClicked(0))
2299
selection[3] = !selection[3];
2300
ImGui::TreePop();
2301
}
2302
2303
IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line");
2304
if (ImGui::TreeNode("Rendering more items on the same line"))
2305
{
2306
// (1) Using SetNextItemAllowOverlap()
2307
// (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically.
2308
static bool selected[3] = { false, false, false };
2309
ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1");
2310
ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2");
2311
ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3");
2312
ImGui::TreePop();
2313
}
2314
2315
IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables");
2316
if (ImGui::TreeNode("In Tables"))
2317
{
2318
static bool selected[10] = {};
2319
2320
if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2321
{
2322
for (int i = 0; i < 10; i++)
2323
{
2324
char label[32];
2325
sprintf(label, "Item %d", i);
2326
ImGui::TableNextColumn();
2327
ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap
2328
}
2329
ImGui::EndTable();
2330
}
2331
ImGui::Spacing();
2332
if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2333
{
2334
for (int i = 0; i < 10; i++)
2335
{
2336
char label[32];
2337
sprintf(label, "Item %d", i);
2338
ImGui::TableNextRow();
2339
ImGui::TableNextColumn();
2340
ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns);
2341
ImGui::TableNextColumn();
2342
ImGui::Text("Some other contents");
2343
ImGui::TableNextColumn();
2344
ImGui::Text("123456");
2345
}
2346
ImGui::EndTable();
2347
}
2348
ImGui::TreePop();
2349
}
2350
2351
IMGUI_DEMO_MARKER("Widgets/Selectables/Grid");
2352
if (ImGui::TreeNode("Grid"))
2353
{
2354
static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
2355
2356
// Add in a bit of silly fun...
2357
const float time = (float)ImGui::GetTime();
2358
const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected...
2359
if (winning_state)
2360
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f)));
2361
2362
for (int y = 0; y < 4; y++)
2363
for (int x = 0; x < 4; x++)
2364
{
2365
if (x > 0)
2366
ImGui::SameLine();
2367
ImGui::PushID(y * 4 + x);
2368
if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(50, 50)))
2369
{
2370
// Toggle clicked cell + toggle neighbors
2371
selected[y][x] ^= 1;
2372
if (x > 0) { selected[y][x - 1] ^= 1; }
2373
if (x < 3) { selected[y][x + 1] ^= 1; }
2374
if (y > 0) { selected[y - 1][x] ^= 1; }
2375
if (y < 3) { selected[y + 1][x] ^= 1; }
2376
}
2377
ImGui::PopID();
2378
}
2379
2380
if (winning_state)
2381
ImGui::PopStyleVar();
2382
ImGui::TreePop();
2383
}
2384
IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment");
2385
if (ImGui::TreeNode("Alignment"))
2386
{
2387
HelpMarker(
2388
"By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item "
2389
"basis using PushStyleVar(). You'll probably want to always keep your default situation to "
2390
"left-align otherwise it becomes difficult to layout multiple items on a same line");
2391
static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };
2392
for (int y = 0; y < 3; y++)
2393
{
2394
for (int x = 0; x < 3; x++)
2395
{
2396
ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);
2397
char name[32];
2398
sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y);
2399
if (x > 0) ImGui::SameLine();
2400
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);
2401
ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80));
2402
ImGui::PopStyleVar();
2403
}
2404
}
2405
ImGui::TreePop();
2406
}
2407
ImGui::TreePop();
2408
}
2409
}
2410
2411
//-----------------------------------------------------------------------------
2412
// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
2413
//-----------------------------------------------------------------------------
2414
// Multi-selection demos
2415
// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select
2416
//-----------------------------------------------------------------------------
2417
2418
static const char* ExampleNames[] =
2419
{
2420
"Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper",
2421
"Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava",
2422
"Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
2423
};
2424
2425
// Extra functions to add deletion support to ImGuiSelectionBasicStorage
2426
struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage
2427
{
2428
// Find which item should be Focused after deletion.
2429
// Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
2430
// The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
2431
// - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
2432
// - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.
2433
// - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr.
2434
// FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset.
2435
int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count)
2436
{
2437
if (Size == 0)
2438
return -1;
2439
2440
// If focused item is not selected...
2441
const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item
2442
if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx))
2443
{
2444
ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even when NavIdSelected==true, but it would take an extra frame to recover RangeSrc when deleting a selected item.
2445
return focused_idx; // Request to focus same item after deletion.
2446
}
2447
2448
// If focused item is selected: land on first unselected item after focused item.
2449
for (int idx = focused_idx + 1; idx < items_count; idx++)
2450
if (!Contains(GetStorageIdFromIndex(idx)))
2451
return idx;
2452
2453
// If focused item is selected: otherwise return last unselected item before focused item.
2454
for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--)
2455
if (!Contains(GetStorageIdFromIndex(idx)))
2456
return idx;
2457
2458
return -1;
2459
}
2460
2461
// Rewrite item list (delete items) + update selection.
2462
// - Call after EndMultiSelect()
2463
// - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data.
2464
template<typename ITEM_TYPE>
2465
void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& items, int item_curr_idx_to_select)
2466
{
2467
// Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection).
2468
// If NavId was not part of selection, we will stay on same item.
2469
ImVector<ITEM_TYPE> new_items;
2470
new_items.reserve(items.Size - Size);
2471
int item_next_idx_to_select = -1;
2472
for (int idx = 0; idx < items.Size; idx++)
2473
{
2474
if (!Contains(GetStorageIdFromIndex(idx)))
2475
new_items.push_back(items[idx]);
2476
if (item_curr_idx_to_select == idx)
2477
item_next_idx_to_select = new_items.Size - 1;
2478
}
2479
items.swap(new_items);
2480
2481
// Update selection
2482
Clear();
2483
if (item_next_idx_to_select != -1 && ms_io->NavIdSelected)
2484
SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true);
2485
}
2486
};
2487
2488
// Example: Implement dual list box storage and interface
2489
struct ExampleDualListBox
2490
{
2491
ImVector<ImGuiID> Items[2]; // ID is index into ExampleName[]
2492
ImGuiSelectionBasicStorage Selections[2]; // Store ExampleItemId into selection
2493
bool OptKeepSorted = true;
2494
2495
void MoveAll(int src, int dst)
2496
{
2497
IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0));
2498
for (ImGuiID item_id : Items[src])
2499
Items[dst].push_back(item_id);
2500
Items[src].clear();
2501
SortItems(dst);
2502
Selections[src].Swap(Selections[dst]);
2503
Selections[src].Clear();
2504
}
2505
void MoveSelected(int src, int dst)
2506
{
2507
for (int src_n = 0; src_n < Items[src].Size; src_n++)
2508
{
2509
ImGuiID item_id = Items[src][src_n];
2510
if (!Selections[src].Contains(item_id))
2511
continue;
2512
Items[src].erase(&Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap)
2513
Items[dst].push_back(item_id);
2514
src_n--;
2515
}
2516
if (OptKeepSorted)
2517
SortItems(dst);
2518
Selections[src].Swap(Selections[dst]);
2519
Selections[src].Clear();
2520
}
2521
void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side)
2522
{
2523
// In this example we store item id in selection (instead of item index)
2524
Selections[side].UserData = Items[side].Data;
2525
Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; };
2526
Selections[side].ApplyRequests(ms_io);
2527
}
2528
static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)
2529
{
2530
const int* a = (const int*)lhs;
2531
const int* b = (const int*)rhs;
2532
return (*a - *b);
2533
}
2534
void SortItems(int n)
2535
{
2536
qsort(Items[n].Data, (size_t)Items[n].Size, sizeof(Items[n][0]), CompareItemsByValue);
2537
}
2538
void Show()
2539
{
2540
//if (ImGui::Checkbox("Sorted", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); }
2541
if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None))
2542
{
2543
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side
2544
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Buttons
2545
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Right side
2546
ImGui::TableNextRow();
2547
2548
int request_move_selected = -1;
2549
int request_move_all = -1;
2550
float child_height_0 = 0.0f;
2551
for (int side = 0; side < 2; side++)
2552
{
2553
// FIXME-MULTISELECT: Dual List Box: Add context menus
2554
// FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.
2555
ImVector<ImGuiID>& items = Items[side];
2556
ImGuiSelectionBasicStorage& selection = Selections[side];
2557
2558
ImGui::TableSetColumnIndex((side == 0) ? 0 : 2);
2559
ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size);
2560
2561
// Submit scrolling range to avoid glitches on moving/deletion
2562
const float items_height = ImGui::GetTextLineHeightWithSpacing();
2563
ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2564
2565
bool child_visible;
2566
if (side == 0)
2567
{
2568
// Left child is resizable
2569
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), ImVec2(FLT_MAX, FLT_MAX));
2570
child_visible = ImGui::BeginChild("0", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY);
2571
child_height_0 = ImGui::GetWindowSize().y;
2572
}
2573
else
2574
{
2575
// Right child use same height as left one
2576
child_visible = ImGui::BeginChild("1", ImVec2(-FLT_MIN, child_height_0), ImGuiChildFlags_FrameStyle);
2577
}
2578
if (child_visible)
2579
{
2580
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None;
2581
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
2582
ApplySelectionRequests(ms_io, side);
2583
2584
for (int item_n = 0; item_n < items.Size; item_n++)
2585
{
2586
ImGuiID item_id = items[item_n];
2587
bool item_is_selected = selection.Contains(item_id);
2588
ImGui::SetNextItemSelectionUserData(item_n);
2589
ImGui::Selectable(ExampleNames[item_id], item_is_selected, ImGuiSelectableFlags_AllowDoubleClick);
2590
if (ImGui::IsItemFocused())
2591
{
2592
// FIXME-MULTISELECT: Dual List Box: Transfer focus
2593
if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))
2594
request_move_selected = side;
2595
if (ImGui::IsMouseDoubleClicked(0)) // FIXME-MULTISELECT: Double-click on multi-selection?
2596
request_move_selected = side;
2597
}
2598
}
2599
2600
ms_io = ImGui::EndMultiSelect();
2601
ApplySelectionRequests(ms_io, side);
2602
}
2603
ImGui::EndChild();
2604
}
2605
2606
// Buttons columns
2607
ImGui::TableSetColumnIndex(1);
2608
ImGui::NewLine();
2609
//ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f };
2610
ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() };
2611
2612
// (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized)
2613
if (ImGui::Button(">>", button_sz))
2614
request_move_all = 0;
2615
if (ImGui::Button(">", button_sz))
2616
request_move_selected = 0;
2617
if (ImGui::Button("<", button_sz))
2618
request_move_selected = 1;
2619
if (ImGui::Button("<<", button_sz))
2620
request_move_all = 1;
2621
2622
// Process requests
2623
if (request_move_all != -1)
2624
MoveAll(request_move_all, request_move_all ^ 1);
2625
if (request_move_selected != -1)
2626
MoveSelected(request_move_selected, request_move_selected ^ 1);
2627
2628
// FIXME-MULTISELECT: Support action from outside
2629
/*
2630
if (OptKeepSorted == false)
2631
{
2632
ImGui::NewLine();
2633
if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {}
2634
if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {}
2635
}
2636
*/
2637
2638
ImGui::EndTable();
2639
}
2640
}
2641
};
2642
2643
static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_data)
2644
{
2645
IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select");
2646
if (ImGui::TreeNode("Selection State & Multi-Select"))
2647
{
2648
HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
2649
2650
// Without any fancy API: manage single-selection yourself.
2651
IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
2652
if (ImGui::TreeNode("Single-Select"))
2653
{
2654
static int selected = -1;
2655
for (int n = 0; n < 5; n++)
2656
{
2657
char buf[32];
2658
sprintf(buf, "Object %d", n);
2659
if (ImGui::Selectable(buf, selected == n))
2660
selected = n;
2661
}
2662
ImGui::TreePop();
2663
}
2664
2665
// Demonstrate implementation a most-basic form of multi-selection manually
2666
// This doesn't support the SHIFT modifier which requires BeginMultiSelect()!
2667
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)");
2668
if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)"))
2669
{
2670
HelpMarker("Hold CTRL and click to select multiple items.");
2671
static bool selection[5] = { false, false, false, false, false };
2672
for (int n = 0; n < 5; n++)
2673
{
2674
char buf[32];
2675
sprintf(buf, "Object %d", n);
2676
if (ImGui::Selectable(buf, selection[n]))
2677
{
2678
if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held
2679
memset(selection, 0, sizeof(selection));
2680
selection[n] ^= 1; // Toggle current item
2681
}
2682
}
2683
ImGui::TreePop();
2684
}
2685
2686
// Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.
2687
// SHIFT+Click w/ CTRL and other standard features are supported.
2688
// We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.
2689
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select");
2690
if (ImGui::TreeNode("Multi-Select"))
2691
{
2692
ImGui::Text("Supported features:");
2693
ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space).");
2694
ImGui::BulletText("Ctrl modifier to preserve and toggle selection.");
2695
ImGui::BulletText("Shift modifier for range selection.");
2696
ImGui::BulletText("CTRL+A to select all.");
2697
ImGui::BulletText("Escape to clear selection.");
2698
ImGui::BulletText("Click and drag to box-select.");
2699
ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.");
2700
2701
// Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2702
const int ITEMS_COUNT = 50;
2703
static ImGuiSelectionBasicStorage selection;
2704
ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2705
2706
// The BeginChild() has no purpose for selection logic, other that offering a scrolling region.
2707
if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2708
{
2709
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2710
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2711
selection.ApplyRequests(ms_io);
2712
2713
for (int n = 0; n < ITEMS_COUNT; n++)
2714
{
2715
char label[64];
2716
sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2717
bool item_is_selected = selection.Contains((ImGuiID)n);
2718
ImGui::SetNextItemSelectionUserData(n);
2719
ImGui::Selectable(label, item_is_selected);
2720
}
2721
2722
ms_io = ImGui::EndMultiSelect();
2723
selection.ApplyRequests(ms_io);
2724
}
2725
ImGui::EndChild();
2726
ImGui::TreePop();
2727
}
2728
2729
// Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2730
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)");
2731
if (ImGui::TreeNode("Multi-Select (with clipper)"))
2732
{
2733
// Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2734
static ImGuiSelectionBasicStorage selection;
2735
2736
ImGui::Text("Added features:");
2737
ImGui::BulletText("Using ImGuiListClipper.");
2738
2739
const int ITEMS_COUNT = 10000;
2740
ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2741
if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2742
{
2743
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2744
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2745
selection.ApplyRequests(ms_io);
2746
2747
ImGuiListClipper clipper;
2748
clipper.Begin(ITEMS_COUNT);
2749
if (ms_io->RangeSrcItem != -1)
2750
clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2751
while (clipper.Step())
2752
{
2753
for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2754
{
2755
char label[64];
2756
sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2757
bool item_is_selected = selection.Contains((ImGuiID)n);
2758
ImGui::SetNextItemSelectionUserData(n);
2759
ImGui::Selectable(label, item_is_selected);
2760
}
2761
}
2762
2763
ms_io = ImGui::EndMultiSelect();
2764
selection.ApplyRequests(ms_io);
2765
}
2766
ImGui::EndChild();
2767
ImGui::TreePop();
2768
}
2769
2770
// Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API.
2771
// In order to support Deletion without any glitches you need to:
2772
// - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling.
2773
// - (2) Items needs to have persistent ID Stack identifier = ID needs to not depends on their index. PushID(index) = KO. PushID(item_id) = OK. This is in order to focus items reliably after a selection.
2774
// - (3) BeginXXXX process
2775
// - (4) Focus process
2776
// - (5) EndXXXX process
2777
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)");
2778
if (ImGui::TreeNode("Multi-Select (with deletion)"))
2779
{
2780
// Storing items data separately from selection data.
2781
// (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items)
2782
// Use a custom selection.Adapter: store item identifier in Selection (instead of index)
2783
static ImVector<ImGuiID> items;
2784
static ExampleSelectionWithDeletion selection;
2785
selection.UserData = (void*)&items;
2786
selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector<ImGuiID>* p_items = (ImVector<ImGuiID>*)self->UserData; return (*p_items)[idx]; }; // Index -> ID
2787
2788
ImGui::Text("Added features:");
2789
ImGui::BulletText("Dynamic list with Delete key support.");
2790
ImGui::Text("Selection size: %d/%d", selection.Size, items.Size);
2791
2792
// Initialize default list with 50 items + button to add/remove items.
2793
static ImGuiID items_next_id = 0;
2794
if (items_next_id == 0)
2795
for (ImGuiID n = 0; n < 50; n++)
2796
items.push_back(items_next_id++);
2797
if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } }
2798
ImGui::SameLine();
2799
if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetItemSelected(items.back(), false); items.pop_back(); } }
2800
2801
// (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
2802
const float items_height = ImGui::GetTextLineHeightWithSpacing();
2803
ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2804
2805
if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2806
{
2807
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2808
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
2809
selection.ApplyRequests(ms_io);
2810
2811
const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0);
2812
const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
2813
2814
for (int n = 0; n < items.Size; n++)
2815
{
2816
const ImGuiID item_id = items[n];
2817
char label[64];
2818
sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]);
2819
2820
bool item_is_selected = selection.Contains(item_id);
2821
ImGui::SetNextItemSelectionUserData(n);
2822
ImGui::Selectable(label, item_is_selected);
2823
if (item_curr_idx_to_focus == n)
2824
ImGui::SetKeyboardFocusHere(-1);
2825
}
2826
2827
// Apply multi-select requests
2828
ms_io = ImGui::EndMultiSelect();
2829
selection.ApplyRequests(ms_io);
2830
if (want_delete)
2831
selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
2832
}
2833
ImGui::EndChild();
2834
ImGui::TreePop();
2835
}
2836
2837
// Implement a Dual List Box (#6648)
2838
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)");
2839
if (ImGui::TreeNode("Multi-Select (dual list box)"))
2840
{
2841
// Init default state
2842
static ExampleDualListBox dlb;
2843
if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0)
2844
for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++)
2845
dlb.Items[0].push_back((ImGuiID)item_id);
2846
2847
// Show
2848
dlb.Show();
2849
2850
ImGui::TreePop();
2851
}
2852
2853
// Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2854
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)");
2855
if (ImGui::TreeNode("Multi-Select (in a table)"))
2856
{
2857
static ImGuiSelectionBasicStorage selection;
2858
2859
const int ITEMS_COUNT = 10000;
2860
ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2861
if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter))
2862
{
2863
ImGui::TableSetupColumn("Object");
2864
ImGui::TableSetupColumn("Action");
2865
ImGui::TableSetupScrollFreeze(0, 1);
2866
ImGui::TableHeadersRow();
2867
2868
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2869
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2870
selection.ApplyRequests(ms_io);
2871
2872
ImGuiListClipper clipper;
2873
clipper.Begin(ITEMS_COUNT);
2874
if (ms_io->RangeSrcItem != -1)
2875
clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2876
while (clipper.Step())
2877
{
2878
for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2879
{
2880
ImGui::TableNextRow();
2881
ImGui::TableNextColumn();
2882
char label[64];
2883
sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2884
bool item_is_selected = selection.Contains((ImGuiID)n);
2885
ImGui::SetNextItemSelectionUserData(n);
2886
ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
2887
ImGui::TableNextColumn();
2888
ImGui::SmallButton("hello");
2889
}
2890
}
2891
2892
ms_io = ImGui::EndMultiSelect();
2893
selection.ApplyRequests(ms_io);
2894
ImGui::EndTable();
2895
}
2896
ImGui::TreePop();
2897
}
2898
2899
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)");
2900
if (ImGui::TreeNode("Multi-Select (checkboxes)"))
2901
{
2902
ImGui::Text("In a list of checkboxes (not selectable):");
2903
ImGui::BulletText("Using _NoAutoSelect + _NoAutoClear flags.");
2904
ImGui::BulletText("Shift+Click to check multiple boxes.");
2905
ImGui::BulletText("Shift+Keyboard to copy current value to other boxes.");
2906
2907
// If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper.
2908
static bool items[20] = {};
2909
static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape;
2910
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
2911
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
2912
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.
2913
2914
if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
2915
{
2916
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items));
2917
ImGuiSelectionExternalStorage storage_wrapper;
2918
storage_wrapper.UserData = (void*)items;
2919
storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; };
2920
storage_wrapper.ApplyRequests(ms_io);
2921
for (int n = 0; n < 20; n++)
2922
{
2923
char label[32];
2924
sprintf(label, "Item %d", n);
2925
ImGui::SetNextItemSelectionUserData(n);
2926
ImGui::Checkbox(label, &items[n]);
2927
}
2928
ms_io = ImGui::EndMultiSelect();
2929
storage_wrapper.ApplyRequests(ms_io);
2930
}
2931
ImGui::EndChild();
2932
2933
ImGui::TreePop();
2934
}
2935
2936
// Demonstrate individual selection scopes in same window
2937
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)");
2938
if (ImGui::TreeNode("Multi-Select (multiple scopes)"))
2939
{
2940
// Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection
2941
const int SCOPES_COUNT = 3;
2942
const int ITEMS_COUNT = 8; // Per scope
2943
static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT];
2944
2945
// Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.
2946
static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid;
2947
if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
2948
flags &= ~ImGuiMultiSelectFlags_ScopeRect;
2949
if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
2950
flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
2951
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);
2952
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
2953
2954
for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++)
2955
{
2956
ImGui::PushID(selection_scope_n);
2957
ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];
2958
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT);
2959
selection->ApplyRequests(ms_io);
2960
2961
ImGui::SeparatorText("Selection scope");
2962
ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT);
2963
2964
for (int n = 0; n < ITEMS_COUNT; n++)
2965
{
2966
char label[64];
2967
sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2968
bool item_is_selected = selection->Contains((ImGuiID)n);
2969
ImGui::SetNextItemSelectionUserData(n);
2970
ImGui::Selectable(label, item_is_selected);
2971
}
2972
2973
// Apply multi-select requests
2974
ms_io = ImGui::EndMultiSelect();
2975
selection->ApplyRequests(ms_io);
2976
ImGui::PopID();
2977
}
2978
ImGui::TreePop();
2979
}
2980
2981
// See ShowExampleAppAssetsBrowser()
2982
if (ImGui::TreeNode("Multi-Select (tiled assets browser)"))
2983
{
2984
ImGui::Checkbox("Assets Browser", &demo_data->ShowAppAssetsBrowser);
2985
ImGui::Text("(also access from 'Examples->Assets Browser' in menu)");
2986
ImGui::TreePop();
2987
}
2988
2989
// Demonstrate supporting multiple-selection in a tree.
2990
// - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly!
2991
// This showcase how SetNextItemSelectionUserData() never assume indices!
2992
// - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request.
2993
// We want this interpolation to match what the user sees: in visible order, skipping closed nodes.
2994
// This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper.
2995
// - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you
2996
// are more likely to build an array mapping sequential indices to visible tree nodes, since your
2997
// filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier.
2998
// - Consider this a prototype: we are working toward simplifying some of it.
2999
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)");
3000
if (ImGui::TreeNode("Multi-Select (trees)"))
3001
{
3002
HelpMarker(
3003
"This is rather advanced and experimental. If you are getting started with multi-select, "
3004
"please don't start by looking at how to use it for a tree!\n\n"
3005
"Future versions will try to simplify and formalize some of this.");
3006
3007
struct ExampleTreeFuncs
3008
{
3009
static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection)
3010
{
3011
ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3012
tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Enable pressing left to jump to parent
3013
if (node->Childs.Size == 0)
3014
tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf;
3015
if (selection->Contains((ImGuiID)node->UID))
3016
tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3017
3018
// Using SetNextItemStorageID() to specify storage id, so we can easily peek into
3019
// the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions.
3020
ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node);
3021
ImGui::SetNextItemStorageID((ImGuiID)node->UID);
3022
if (ImGui::TreeNodeEx(node->Name, tree_node_flags))
3023
{
3024
for (ExampleTreeNode* child : node->Childs)
3025
DrawNode(child, selection);
3026
ImGui::TreePop();
3027
}
3028
else if (ImGui::IsItemToggledOpen())
3029
{
3030
TreeCloseAndUnselectChildNodes(node, selection);
3031
}
3032
}
3033
3034
static bool TreeNodeGetOpen(ExampleTreeNode* node)
3035
{
3036
return ImGui::GetStateStorage()->GetBool((ImGuiID)node->UID);
3037
}
3038
3039
static void TreeNodeSetOpen(ExampleTreeNode* node, bool open)
3040
{
3041
ImGui::GetStateStorage()->SetBool((ImGuiID)node->UID, open);
3042
}
3043
3044
// When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected.
3045
// FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node
3046
// features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc.
3047
static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0)
3048
{
3049
// Recursive close (the test for depth == 0 is because we call this on a node that was just closed!)
3050
int unselected_count = selection->Contains((ImGuiID)node->UID) ? 1 : 0;
3051
if (depth == 0 || TreeNodeGetOpen(node))
3052
{
3053
for (ExampleTreeNode* child : node->Childs)
3054
unselected_count += TreeCloseAndUnselectChildNodes(child, selection, depth + 1);
3055
TreeNodeSetOpen(node, false);
3056
}
3057
3058
// Select root node if any of its child was selected, otherwise unselect
3059
selection->SetItemSelected((ImGuiID)node->UID, (depth == 0 && unselected_count > 0));
3060
return unselected_count;
3061
}
3062
3063
// Apply multi-selection requests
3064
static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection)
3065
{
3066
for (ImGuiSelectionRequest& req : ms_io->Requests)
3067
{
3068
if (req.Type == ImGuiSelectionRequestType_SetAll)
3069
{
3070
if (req.Selected)
3071
TreeSetAllInOpenNodes(tree, selection, req.Selected);
3072
else
3073
selection->Clear();
3074
}
3075
else if (req.Type == ImGuiSelectionRequestType_SetRange)
3076
{
3077
ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem;
3078
ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem;
3079
for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(node, last_node))
3080
selection->SetItemSelected((ImGuiID)node->UID, req.Selected);
3081
}
3082
}
3083
}
3084
3085
static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected)
3086
{
3087
if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme
3088
selection->SetItemSelected((ImGuiID)node->UID, selected);
3089
if (node->Parent == NULL || TreeNodeGetOpen(node))
3090
for (ExampleTreeNode* child : node->Childs)
3091
TreeSetAllInOpenNodes(child, selection, selected);
3092
}
3093
3094
// Interpolate in *user-visible order* AND only *over opened nodes*.
3095
// If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler.
3096
// Here the tricks are that:
3097
// - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion.
3098
// this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)'
3099
// which would only be called when crossing from child to a parent, aka not too much.
3100
// - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack,
3101
// making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location.
3102
static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node)
3103
{
3104
// Reached last node
3105
if (curr_node == last_node)
3106
return NULL;
3107
3108
// Recurse into childs. Query storage to tell if the node is open.
3109
if (curr_node->Childs.Size > 0 && TreeNodeGetOpen(curr_node))
3110
return curr_node->Childs[0];
3111
3112
// Next sibling, then into our own parent
3113
while (curr_node->Parent != NULL)
3114
{
3115
if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size)
3116
return curr_node->Parent->Childs[curr_node->IndexInParent + 1];
3117
curr_node = curr_node->Parent;
3118
}
3119
return NULL;
3120
}
3121
3122
}; // ExampleTreeFuncs
3123
3124
static ImGuiSelectionBasicStorage selection;
3125
if (demo_data->DemoTree == NULL)
3126
demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once
3127
ImGui::Text("Selection size: %d", selection.Size);
3128
3129
if (ImGui::BeginChild("##Tree", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3130
{
3131
ExampleTreeNode* tree = demo_data->DemoTree;
3132
ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d;
3133
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1);
3134
ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);
3135
for (ExampleTreeNode* node : tree->Childs)
3136
ExampleTreeFuncs::DrawNode(node, &selection);
3137
ms_io = ImGui::EndMultiSelect();
3138
ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);
3139
}
3140
ImGui::EndChild();
3141
3142
ImGui::TreePop();
3143
}
3144
3145
// Advanced demonstration of BeginMultiSelect()
3146
// - Showcase clipping.
3147
// - Showcase deletion.
3148
// - Showcase basic drag and drop.
3149
// - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
3150
// - Showcase using inside a table.
3151
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)");
3152
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
3153
if (ImGui::TreeNode("Multi-Select (advanced)"))
3154
{
3155
// Options
3156
enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
3157
static bool use_clipper = true;
3158
static bool use_deletion = true;
3159
static bool use_drag_drop = true;
3160
static bool show_in_table = false;
3161
static bool show_color_button = true;
3162
static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
3163
static WidgetType widget_type = WidgetType_Selectable;
3164
3165
if (ImGui::TreeNode("Options"))
3166
{
3167
if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; }
3168
ImGui::SameLine();
3169
if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; }
3170
ImGui::SameLine();
3171
HelpMarker("TreeNode() is technically supported but... using this correctly is more complicated (you need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this.\n\nFor now the tree demo is actually a little bit meaningless because it is an empty tree with only root nodes.");
3172
ImGui::Checkbox("Enable clipper", &use_clipper);
3173
ImGui::Checkbox("Enable deletion", &use_deletion);
3174
ImGui::Checkbox("Enable drag & drop", &use_drag_drop);
3175
ImGui::Checkbox("Show in a table", &show_in_table);
3176
ImGui::Checkbox("Show color button", &show_color_button);
3177
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect);
3178
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll);
3179
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect);
3180
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
3181
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
3182
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect);
3183
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
3184
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d);
3185
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll);
3186
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape);
3187
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);
3188
if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
3189
flags &= ~ImGuiMultiSelectFlags_ScopeRect;
3190
if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
3191
flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
3192
if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick))
3193
flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease;
3194
if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease))
3195
flags &= ~ImGuiMultiSelectFlags_SelectOnClick;
3196
ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection.");
3197
ImGui::TreePop();
3198
}
3199
3200
// Initialize default list with 1000 items.
3201
// Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
3202
static ImVector<int> items;
3203
static int items_next_id = 0;
3204
if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } }
3205
static ExampleSelectionWithDeletion selection;
3206
static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu
3207
3208
ImGui::Text("Selection size: %d/%d", selection.Size, items.Size);
3209
3210
const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing();
3211
ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
3212
if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3213
{
3214
ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());
3215
if (widget_type == WidgetType_TreeNode)
3216
ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
3217
3218
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
3219
selection.ApplyRequests(ms_io);
3220
3221
const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu;
3222
const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
3223
request_deletion_from_menu = false;
3224
3225
if (show_in_table)
3226
{
3227
if (widget_type == WidgetType_TreeNode)
3228
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f));
3229
ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
3230
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f);
3231
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f);
3232
//ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f);
3233
}
3234
3235
ImGuiListClipper clipper;
3236
if (use_clipper)
3237
{
3238
clipper.Begin(items.Size);
3239
if (item_curr_idx_to_focus != -1)
3240
clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped.
3241
if (ms_io->RangeSrcItem != -1)
3242
clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
3243
}
3244
3245
while (!use_clipper || clipper.Step())
3246
{
3247
const int item_begin = use_clipper ? clipper.DisplayStart : 0;
3248
const int item_end = use_clipper ? clipper.DisplayEnd : items.Size;
3249
for (int n = item_begin; n < item_end; n++)
3250
{
3251
if (show_in_table)
3252
ImGui::TableNextColumn();
3253
3254
const int item_id = items[n];
3255
const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)];
3256
char label[64];
3257
sprintf(label, "Object %05d: %s", item_id, item_category);
3258
3259
// IMPORTANT: for deletion refocus to work we need object ID to be stable,
3260
// aka not depend on their index in the list. Here we use our persistent item_id
3261
// instead of index to build a unique ID that will persist.
3262
// (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).
3263
ImGui::PushID(item_id);
3264
3265
// Emit a color button, to test that Shift+LeftArrow landing on an item that is not part
3266
// of the selection scope doesn't erroneously alter our selection.
3267
if (show_color_button)
3268
{
3269
ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK;
3270
ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz);
3271
ImGui::SameLine();
3272
}
3273
3274
// Submit item
3275
bool item_is_selected = selection.Contains((ImGuiID)n);
3276
bool item_is_open = false;
3277
ImGui::SetNextItemSelectionUserData(n);
3278
if (widget_type == WidgetType_Selectable)
3279
{
3280
ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None);
3281
}
3282
else if (widget_type == WidgetType_TreeNode)
3283
{
3284
ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3285
if (item_is_selected)
3286
tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3287
item_is_open = ImGui::TreeNodeEx(label, tree_node_flags);
3288
}
3289
3290
// Focus (for after deletion)
3291
if (item_curr_idx_to_focus == n)
3292
ImGui::SetKeyboardFocusHere(-1);
3293
3294
// Drag and Drop
3295
if (use_drag_drop && ImGui::BeginDragDropSource())
3296
{
3297
// Create payload with full selection OR single unselected item.
3298
// (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
3299
if (ImGui::GetDragDropPayload() == NULL)
3300
{
3301
ImVector<int> payload_items;
3302
void* it = NULL;
3303
ImGuiID id = 0;
3304
if (!item_is_selected)
3305
payload_items.push_back(item_id);
3306
else
3307
while (selection.GetNextSelectedItem(&it, &id))
3308
payload_items.push_back((int)id);
3309
ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
3310
}
3311
3312
// Display payload content in tooltip
3313
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
3314
const int* payload_items = (int*)payload->Data;
3315
const int payload_count = (int)payload->DataSize / (int)sizeof(int);
3316
if (payload_count == 1)
3317
ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]);
3318
else
3319
ImGui::Text("Dragging %d objects", payload_count);
3320
3321
ImGui::EndDragDropSource();
3322
}
3323
3324
if (widget_type == WidgetType_TreeNode && item_is_open)
3325
ImGui::TreePop();
3326
3327
// Right-click: context menu
3328
if (ImGui::BeginPopupContextItem())
3329
{
3330
ImGui::BeginDisabled(!use_deletion || selection.Size == 0);
3331
sprintf(label, "Delete %d item(s)###DeleteSelected", selection.Size);
3332
if (ImGui::Selectable(label))
3333
request_deletion_from_menu = true;
3334
ImGui::EndDisabled();
3335
ImGui::Selectable("Close");
3336
ImGui::EndPopup();
3337
}
3338
3339
// Demo content within a table
3340
if (show_in_table)
3341
{
3342
ImGui::TableNextColumn();
3343
ImGui::SetNextItemWidth(-FLT_MIN);
3344
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
3345
ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly);
3346
ImGui::PopStyleVar();
3347
}
3348
3349
ImGui::PopID();
3350
}
3351
if (!use_clipper)
3352
break;
3353
}
3354
3355
if (show_in_table)
3356
{
3357
ImGui::EndTable();
3358
if (widget_type == WidgetType_TreeNode)
3359
ImGui::PopStyleVar();
3360
}
3361
3362
// Apply multi-select requests
3363
ms_io = ImGui::EndMultiSelect();
3364
selection.ApplyRequests(ms_io);
3365
if (want_delete)
3366
selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
3367
3368
if (widget_type == WidgetType_TreeNode)
3369
ImGui::PopStyleVar();
3370
}
3371
ImGui::EndChild();
3372
ImGui::TreePop();
3373
}
3374
ImGui::TreePop();
3375
}
3376
}
3377
3378
//-----------------------------------------------------------------------------
3379
// [SECTION] DemoWindowWidgetsTabs()
3380
//-----------------------------------------------------------------------------
3381
3382
static void DemoWindowWidgetsTabs()
3383
{
3384
IMGUI_DEMO_MARKER("Widgets/Tabs");
3385
if (ImGui::TreeNode("Tabs"))
3386
{
3387
IMGUI_DEMO_MARKER("Widgets/Tabs/Basic");
3388
if (ImGui::TreeNode("Basic"))
3389
{
3390
ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
3391
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3392
{
3393
if (ImGui::BeginTabItem("Avocado"))
3394
{
3395
ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah");
3396
ImGui::EndTabItem();
3397
}
3398
if (ImGui::BeginTabItem("Broccoli"))
3399
{
3400
ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah");
3401
ImGui::EndTabItem();
3402
}
3403
if (ImGui::BeginTabItem("Cucumber"))
3404
{
3405
ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah");
3406
ImGui::EndTabItem();
3407
}
3408
ImGui::EndTabBar();
3409
}
3410
ImGui::Separator();
3411
ImGui::TreePop();
3412
}
3413
3414
IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button");
3415
if (ImGui::TreeNode("Advanced & Close Button"))
3416
{
3417
// Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).
3418
static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;
3419
ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", &tab_bar_flags, ImGuiTabBarFlags_Reorderable);
3420
ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs);
3421
ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);
3422
ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);
3423
ImGui::CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline);
3424
if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
3425
tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
3426
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown))
3427
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
3428
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll))
3429
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
3430
3431
// Tab Bar
3432
ImGui::AlignTextToFramePadding();
3433
ImGui::Text("Opened:");
3434
const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" };
3435
static bool opened[4] = { true, true, true, true }; // Persistent user state
3436
for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3437
{
3438
ImGui::SameLine();
3439
ImGui::Checkbox(names[n], &opened[n]);
3440
}
3441
3442
// Passing a bool* to BeginTabItem() is similar to passing one to Begin():
3443
// the underlying bool will be set to false when the tab is closed.
3444
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3445
{
3446
for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3447
if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None))
3448
{
3449
ImGui::Text("This is the %s tab!", names[n]);
3450
if (n & 1)
3451
ImGui::Text("I am an odd tab.");
3452
ImGui::EndTabItem();
3453
}
3454
ImGui::EndTabBar();
3455
}
3456
ImGui::Separator();
3457
ImGui::TreePop();
3458
}
3459
3460
IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags");
3461
if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags"))
3462
{
3463
static ImVector<int> active_tabs;
3464
static int next_tab_id = 0;
3465
if (next_tab_id == 0) // Initialize with some default tabs
3466
for (int i = 0; i < 3; i++)
3467
active_tabs.push_back(next_tab_id++);
3468
3469
// TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.
3470
// (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...
3471
// but they tend to make more sense together)
3472
static bool show_leading_button = true;
3473
static bool show_trailing_button = true;
3474
ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button);
3475
ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button);
3476
3477
// Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
3478
static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown;
3479
ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);
3480
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown))
3481
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
3482
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll))
3483
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
3484
3485
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3486
{
3487
// Demo a Leading TabItemButton(): click the "?" button to open a menu
3488
if (show_leading_button)
3489
if (ImGui::TabItemButton("?", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))
3490
ImGui::OpenPopup("MyHelpMenu");
3491
if (ImGui::BeginPopup("MyHelpMenu"))
3492
{
3493
ImGui::Selectable("Hello!");
3494
ImGui::EndPopup();
3495
}
3496
3497
// Demo Trailing Tabs: click the "+" button to add a new tab.
3498
// (In your app you may want to use a font icon instead of the "+")
3499
// We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end.
3500
if (show_trailing_button)
3501
if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))
3502
active_tabs.push_back(next_tab_id++); // Add new tab
3503
3504
// Submit our regular tabs
3505
for (int n = 0; n < active_tabs.Size; )
3506
{
3507
bool open = true;
3508
char name[16];
3509
snprintf(name, IM_ARRAYSIZE(name), "%04d", active_tabs[n]);
3510
if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None))
3511
{
3512
ImGui::Text("This is the %s tab!", name);
3513
ImGui::EndTabItem();
3514
}
3515
3516
if (!open)
3517
active_tabs.erase(active_tabs.Data + n);
3518
else
3519
n++;
3520
}
3521
3522
ImGui::EndTabBar();
3523
}
3524
ImGui::Separator();
3525
ImGui::TreePop();
3526
}
3527
ImGui::TreePop();
3528
}
3529
}
3530
3531
//-----------------------------------------------------------------------------
3532
// [SECTION] DemoWindowWidgetsText()
3533
//-----------------------------------------------------------------------------
3534
3535
static void DemoWindowWidgetsText()
3536
{
3537
IMGUI_DEMO_MARKER("Widgets/Text");
3538
if (ImGui::TreeNode("Text"))
3539
{
3540
IMGUI_DEMO_MARKER("Widgets/Text/Colored Text");
3541
if (ImGui::TreeNode("Colorful Text"))
3542
{
3543
// Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
3544
ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Pink");
3545
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Yellow");
3546
ImGui::TextDisabled("Disabled");
3547
ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle.");
3548
ImGui::TreePop();
3549
}
3550
3551
IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping");
3552
if (ImGui::TreeNode("Word Wrapping"))
3553
{
3554
// Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
3555
ImGui::TextWrapped(
3556
"This text should automatically wrap on the edge of the window. The current implementation "
3557
"for text wrapping follows simple rules suitable for English and possibly other languages.");
3558
ImGui::Spacing();
3559
3560
static float wrap_width = 200.0f;
3561
ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f");
3562
3563
ImDrawList* draw_list = ImGui::GetWindowDrawList();
3564
for (int n = 0; n < 2; n++)
3565
{
3566
ImGui::Text("Test paragraph %d:", n);
3567
ImVec2 pos = ImGui::GetCursorScreenPos();
3568
ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);
3569
ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());
3570
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
3571
if (n == 0)
3572
ImGui::Text("The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width);
3573
else
3574
ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh");
3575
3576
// Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
3577
draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));
3578
draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255));
3579
ImGui::PopTextWrapPos();
3580
}
3581
3582
ImGui::TreePop();
3583
}
3584
3585
IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text");
3586
if (ImGui::TreeNode("UTF-8 Text"))
3587
{
3588
// UTF-8 test with Japanese characters
3589
// (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
3590
// - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
3591
// - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
3592
// can save your source files as 'UTF-8 without signature').
3593
// - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
3594
// CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
3595
// Don't do this in your application! Please use u8"text in any language" in your application!
3596
// Note that characters values are preserved even by InputText() if the font cannot be displayed,
3597
// so you can safely copy & paste garbled characters into another application.
3598
ImGui::TextWrapped(
3599
"CJK text will only appear if the font was loaded with the appropriate CJK character ranges. "
3600
"Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. "
3601
"Read docs/FONTS.md for details.");
3602
ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
3603
ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
3604
static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
3605
//static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis
3606
ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf));
3607
ImGui::TreePop();
3608
}
3609
ImGui::TreePop();
3610
}
3611
}
3612
3613
//-----------------------------------------------------------------------------
3614
// [SECTION] DemoWindowWidgetsTextFilter()
3615
//-----------------------------------------------------------------------------
3616
3617
static void DemoWindowWidgetsTextFilter()
3618
{
3619
IMGUI_DEMO_MARKER("Widgets/Text Filter");
3620
if (ImGui::TreeNode("Text Filter"))
3621
{
3622
// Helper class to easy setup a text filter.
3623
// You may want to implement a more feature-full filtering scheme in your own application.
3624
HelpMarker("Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.");
3625
static ImGuiTextFilter filter;
3626
ImGui::Text("Filter usage:\n"
3627
" \"\" display all lines\n"
3628
" \"xxx\" display lines containing \"xxx\"\n"
3629
" \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n"
3630
" \"-xxx\" hide lines containing \"xxx\"");
3631
filter.Draw();
3632
const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" };
3633
for (int i = 0; i < IM_ARRAYSIZE(lines); i++)
3634
if (filter.PassFilter(lines[i]))
3635
ImGui::BulletText("%s", lines[i]);
3636
ImGui::TreePop();
3637
}
3638
}
3639
3640
//-----------------------------------------------------------------------------
3641
// [SECTION] DemoWindowWidgetsTextInput()
3642
//-----------------------------------------------------------------------------
3643
3644
static void DemoWindowWidgetsTextInput()
3645
{
3646
// To wire InputText() with std::string or any other custom string type,
3647
// see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
3648
IMGUI_DEMO_MARKER("Widgets/Text Input");
3649
if (ImGui::TreeNode("Text Input"))
3650
{
3651
IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input");
3652
if (ImGui::TreeNode("Multi-line Text Input"))
3653
{
3654
// Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize
3655
// and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.
3656
static char text[1024 * 16] =
3657
"/*\n"
3658
" The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"
3659
" the hexadecimal encoding of one offending instruction,\n"
3660
" more formally, the invalid operand with locked CMPXCHG8B\n"
3661
" instruction bug, is a design flaw in the majority of\n"
3662
" Intel Pentium, Pentium MMX, and Pentium OverDrive\n"
3663
" processors (all in the P5 microarchitecture).\n"
3664
"*/\n\n"
3665
"label:\n"
3666
"\tlock cmpxchg8b eax\n";
3667
3668
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
3669
HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)");
3670
ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
3671
ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput);
3672
ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets.");
3673
ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);
3674
ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
3675
ImGui::TreePop();
3676
}
3677
3678
IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input");
3679
if (ImGui::TreeNode("Filtered Text Input"))
3680
{
3681
struct TextFilters
3682
{
3683
// Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)
3684
static int FilterCasingSwap(ImGuiInputTextCallbackData* data)
3685
{
3686
if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase
3687
else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase
3688
return 0;
3689
}
3690
3691
// Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)
3692
static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)
3693
{
3694
if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar))
3695
return 0;
3696
return 1;
3697
}
3698
};
3699
3700
static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_ARRAYSIZE(buf1));
3701
static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CharsDecimal);
3702
static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
3703
static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_ARRAYSIZE(buf4), ImGuiInputTextFlags_CharsUppercase);
3704
static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_ARRAYSIZE(buf5), ImGuiInputTextFlags_CharsNoBlank);
3705
static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_ARRAYSIZE(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.
3706
static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_ARRAYSIZE(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.
3707
ImGui::TreePop();
3708
}
3709
3710
IMGUI_DEMO_MARKER("Widgets/Text Input/Password input");
3711
if (ImGui::TreeNode("Password Input"))
3712
{
3713
static char password[64] = "password123";
3714
ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
3715
ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
3716
ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
3717
ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password));
3718
ImGui::TreePop();
3719
}
3720
3721
IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks");
3722
if (ImGui::TreeNode("Completion, History, Edit Callbacks"))
3723
{
3724
struct Funcs
3725
{
3726
static int MyCallback(ImGuiInputTextCallbackData* data)
3727
{
3728
if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)
3729
{
3730
data->InsertChars(data->CursorPos, "..");
3731
}
3732
else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)
3733
{
3734
if (data->EventKey == ImGuiKey_UpArrow)
3735
{
3736
data->DeleteChars(0, data->BufTextLen);
3737
data->InsertChars(0, "Pressed Up!");
3738
data->SelectAll();
3739
}
3740
else if (data->EventKey == ImGuiKey_DownArrow)
3741
{
3742
data->DeleteChars(0, data->BufTextLen);
3743
data->InsertChars(0, "Pressed Down!");
3744
data->SelectAll();
3745
}
3746
}
3747
else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)
3748
{
3749
// Toggle casing of first character
3750
char c = data->Buf[0];
3751
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32;
3752
data->BufDirty = true;
3753
3754
// Increment a counter
3755
int* p_int = (int*)data->UserData;
3756
*p_int = *p_int + 1;
3757
}
3758
return 0;
3759
}
3760
};
3761
static char buf1[64];
3762
ImGui::InputText("Completion", buf1, IM_ARRAYSIZE(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);
3763
ImGui::SameLine(); HelpMarker(
3764
"Here we append \"..\" each time Tab is pressed. "
3765
"See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3766
3767
static char buf2[64];
3768
ImGui::InputText("History", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);
3769
ImGui::SameLine(); HelpMarker(
3770
"Here we replace and select text each time Up/Down are pressed. "
3771
"See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3772
3773
static char buf3[64];
3774
static int edit_count = 0;
3775
ImGui::InputText("Edit", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);
3776
ImGui::SameLine(); HelpMarker(
3777
"Here we toggle the casing of the first character on every edit + count edits.");
3778
ImGui::SameLine(); ImGui::Text("(%d)", edit_count);
3779
3780
ImGui::TreePop();
3781
}
3782
3783
IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback");
3784
if (ImGui::TreeNode("Resize Callback"))
3785
{
3786
// To wire InputText() with std::string or any other custom string type,
3787
// you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
3788
// using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
3789
HelpMarker(
3790
"Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n"
3791
"See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
3792
struct Funcs
3793
{
3794
static int MyResizeCallback(ImGuiInputTextCallbackData* data)
3795
{
3796
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
3797
{
3798
ImVector<char>* my_str = (ImVector<char>*)data->UserData;
3799
IM_ASSERT(my_str->begin() == data->Buf);
3800
my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
3801
data->Buf = my_str->begin();
3802
}
3803
return 0;
3804
}
3805
3806
// Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
3807
// For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
3808
static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
3809
{
3810
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
3811
return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str);
3812
}
3813
};
3814
3815
// For this demo we are using ImVector as a string container.
3816
// Note that because we need to store a terminating zero character, our size/capacity are 1 more
3817
// than usually reported by a typical string class.
3818
static ImVector<char> my_str;
3819
if (my_str.empty())
3820
my_str.push_back(0);
3821
Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));
3822
ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
3823
ImGui::TreePop();
3824
}
3825
3826
IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment");
3827
if (ImGui::TreeNode("Eliding, Alignment"))
3828
{
3829
static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp";
3830
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft;
3831
ImGui::CheckboxFlags("ImGuiInputTextFlags_ElideLeft", &flags, ImGuiInputTextFlags_ElideLeft);
3832
ImGui::InputText("Path", buf1, IM_ARRAYSIZE(buf1), flags);
3833
ImGui::TreePop();
3834
}
3835
3836
IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous");
3837
if (ImGui::TreeNode("Miscellaneous"))
3838
{
3839
static char buf1[16];
3840
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll;
3841
ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll);
3842
ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
3843
ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo);
3844
ImGui::InputText("Hello", buf1, IM_ARRAYSIZE(buf1), flags);
3845
ImGui::TreePop();
3846
}
3847
3848
ImGui::TreePop();
3849
}
3850
3851
}
3852
3853
//-----------------------------------------------------------------------------
3854
// [SECTION] DemoWindowWidgetsTooltips()
3855
//-----------------------------------------------------------------------------
3856
3857
static void DemoWindowWidgetsTooltips()
3858
{
3859
IMGUI_DEMO_MARKER("Widgets/Tooltips");
3860
if (ImGui::TreeNode("Tooltips"))
3861
{
3862
// Tooltips are windows following the mouse. They do not take focus away.
3863
ImGui::SeparatorText("General");
3864
3865
// Typical use cases:
3866
// - Short-form (text only): SetItemTooltip("Hello");
3867
// - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); }
3868
3869
// - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); }
3870
// - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); }
3871
3872
HelpMarker(
3873
"Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n"
3874
"We provide a helper SetItemTooltip() function to perform the two with standards flags.");
3875
3876
ImVec2 sz = ImVec2(-FLT_MIN, 0.0f);
3877
3878
ImGui::Button("Basic", sz);
3879
ImGui::SetItemTooltip("I am a tooltip");
3880
3881
ImGui::Button("Fancy", sz);
3882
if (ImGui::BeginItemTooltip())
3883
{
3884
ImGui::Text("I am a fancy tooltip");
3885
static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
3886
ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr));
3887
ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime()));
3888
ImGui::EndTooltip();
3889
}
3890
3891
ImGui::SeparatorText("Always On");
3892
3893
// Showcase NOT relying on a IsItemHovered() to emit a tooltip.
3894
// Here the tooltip is always emitted when 'always_on == true'.
3895
static int always_on = 0;
3896
ImGui::RadioButton("Off", &always_on, 0);
3897
ImGui::SameLine();
3898
ImGui::RadioButton("Always On (Simple)", &always_on, 1);
3899
ImGui::SameLine();
3900
ImGui::RadioButton("Always On (Advanced)", &always_on, 2);
3901
if (always_on == 1)
3902
ImGui::SetTooltip("I am following you around.");
3903
else if (always_on == 2 && ImGui::BeginTooltip())
3904
{
3905
ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f));
3906
ImGui::EndTooltip();
3907
}
3908
3909
ImGui::SeparatorText("Custom");
3910
3911
HelpMarker(
3912
"Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize "
3913
"tooltip activation details across your application. You may however decide to use custom "
3914
"flags for a specific tooltip instance.");
3915
3916
// The following examples are passed for documentation purpose but may not be useful to most users.
3917
// Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from
3918
// 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.
3919
// With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.
3920
ImGui::Button("Manual", sz);
3921
if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
3922
ImGui::SetTooltip("I am a manually emitted tooltip.");
3923
3924
ImGui::Button("DelayNone", sz);
3925
if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone))
3926
ImGui::SetTooltip("I am a tooltip with no delay.");
3927
3928
ImGui::Button("DelayShort", sz);
3929
if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))
3930
ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort);
3931
3932
ImGui::Button("DelayLong", sz);
3933
if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay))
3934
ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal);
3935
3936
ImGui::Button("Stationary", sz);
3937
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
3938
ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating.");
3939
3940
// Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',
3941
// which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.
3942
ImGui::BeginDisabled();
3943
ImGui::Button("Disabled item", sz);
3944
if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
3945
ImGui::SetTooltip("I am a a tooltip for a disabled item.");
3946
ImGui::EndDisabled();
3947
3948
ImGui::TreePop();
3949
}
3950
}
3951
3952
//-----------------------------------------------------------------------------
3953
// [SECTION] DemoWindowWidgetsTreeNodes()
3954
//-----------------------------------------------------------------------------
3955
3956
static void DemoWindowWidgetsTreeNodes()
3957
{
3958
IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
3959
if (ImGui::TreeNode("Tree Nodes"))
3960
{
3961
// See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
3962
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees");
3963
if (ImGui::TreeNode("Basic trees"))
3964
{
3965
for (int i = 0; i < 5; i++)
3966
{
3967
// Use SetNextItemOpen() so set the default state of a node to be open. We could
3968
// also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
3969
if (i == 0)
3970
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
3971
3972
// Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict.
3973
// An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)',
3974
// aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine.
3975
ImGui::PushID(i);
3976
if (ImGui::TreeNode("", "Child %d", i))
3977
{
3978
ImGui::Text("blah blah");
3979
ImGui::SameLine();
3980
if (ImGui::SmallButton("button")) {}
3981
ImGui::TreePop();
3982
}
3983
ImGui::PopID();
3984
}
3985
ImGui::TreePop();
3986
}
3987
3988
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines");
3989
if (ImGui::TreeNode("Hierarchy lines"))
3990
{
3991
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;
3992
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
3993
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
3994
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
3995
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
3996
3997
if (ImGui::TreeNodeEx("Parent", base_flags))
3998
{
3999
if (ImGui::TreeNodeEx("Child 1", base_flags))
4000
{
4001
ImGui::Button("Button for Child 1");
4002
ImGui::TreePop();
4003
}
4004
if (ImGui::TreeNodeEx("Child 2", base_flags))
4005
{
4006
ImGui::Button("Button for Child 2");
4007
ImGui::TreePop();
4008
}
4009
ImGui::Text("Remaining contents");
4010
ImGui::Text("Remaining contents");
4011
ImGui::TreePop();
4012
}
4013
4014
ImGui::TreePop();
4015
}
4016
4017
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
4018
if (ImGui::TreeNode("Advanced, with Selectable nodes"))
4019
{
4020
HelpMarker(
4021
"This is a more typical looking tree with selectable nodes.\n"
4022
"Click to select, CTRL+Click to toggle, click on arrows or double-click to open.");
4023
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
4024
static bool align_label_with_current_x_position = false;
4025
static bool test_drag_and_drop = false;
4026
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
4027
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);
4028
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node.");
4029
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth);
4030
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &base_flags, ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::SameLine(); HelpMarker("Reduce hit area to the text label and a bit of margin.");
4031
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only.");
4032
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap);
4033
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)");
4034
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent);
4035
4036
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
4037
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
4038
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
4039
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
4040
4041
ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
4042
ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop);
4043
ImGui::Text("Hello!");
4044
if (align_label_with_current_x_position)
4045
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
4046
4047
// 'selection_mask' is dumb representation of what may be user-side selection state.
4048
// You may retain selection state inside or outside your objects in whatever format you see fit.
4049
// 'node_clicked' is temporary storage of what node we have clicked to process selection at the end
4050
/// of the loop. May be a pointer to your own node type, etc.
4051
static int selection_mask = (1 << 2);
4052
int node_clicked = -1;
4053
for (int i = 0; i < 6; i++)
4054
{
4055
// Disable the default "open on single-click behavior" + set Selected flag according to our selection.
4056
// To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
4057
ImGuiTreeNodeFlags node_flags = base_flags;
4058
const bool is_selected = (selection_mask & (1 << i)) != 0;
4059
if (is_selected)
4060
node_flags |= ImGuiTreeNodeFlags_Selected;
4061
if (i < 3)
4062
{
4063
// Items 0..2 are Tree Node
4064
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i);
4065
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4066
node_clicked = i;
4067
if (test_drag_and_drop && ImGui::BeginDragDropSource())
4068
{
4069
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
4070
ImGui::Text("This is a drag and drop source");
4071
ImGui::EndDragDropSource();
4072
}
4073
if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
4074
{
4075
// Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
4076
ImGui::SameLine();
4077
if (ImGui::SmallButton("button")) {}
4078
}
4079
if (node_open)
4080
{
4081
ImGui::BulletText("Blah blah\nBlah Blah");
4082
ImGui::SameLine();
4083
ImGui::SmallButton("Button");
4084
ImGui::TreePop();
4085
}
4086
}
4087
else
4088
{
4089
// Items 3..5 are Tree Leaves
4090
// The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
4091
// use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
4092
node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
4093
ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i);
4094
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4095
node_clicked = i;
4096
if (test_drag_and_drop && ImGui::BeginDragDropSource())
4097
{
4098
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
4099
ImGui::Text("This is a drag and drop source");
4100
ImGui::EndDragDropSource();
4101
}
4102
}
4103
}
4104
if (node_clicked != -1)
4105
{
4106
// Update selection state
4107
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
4108
if (ImGui::GetIO().KeyCtrl)
4109
selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
4110
else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
4111
selection_mask = (1 << node_clicked); // Click to single-select
4112
}
4113
if (align_label_with_current_x_position)
4114
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
4115
ImGui::TreePop();
4116
}
4117
ImGui::TreePop();
4118
}
4119
}
4120
4121
//-----------------------------------------------------------------------------
4122
// [SECTION] DemoWindowWidgetsVerticalSliders()
4123
//-----------------------------------------------------------------------------
4124
4125
static void DemoWindowWidgetsVerticalSliders()
4126
{
4127
IMGUI_DEMO_MARKER("Widgets/Vertical Sliders");
4128
if (ImGui::TreeNode("Vertical Sliders"))
4129
{
4130
const float spacing = 4;
4131
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
4132
4133
static int int_value = 0;
4134
ImGui::VSliderInt("##int", ImVec2(18, 160), &int_value, 0, 5);
4135
ImGui::SameLine();
4136
4137
static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };
4138
ImGui::PushID("set1");
4139
for (int i = 0; i < 7; i++)
4140
{
4141
if (i > 0) ImGui::SameLine();
4142
ImGui::PushID(i);
4143
ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f));
4144
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f));
4145
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f));
4146
ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f));
4147
ImGui::VSliderFloat("##v", ImVec2(18, 160), &values[i], 0.0f, 1.0f, "");
4148
if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4149
ImGui::SetTooltip("%.3f", values[i]);
4150
ImGui::PopStyleColor(4);
4151
ImGui::PopID();
4152
}
4153
ImGui::PopID();
4154
4155
ImGui::SameLine();
4156
ImGui::PushID("set2");
4157
static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f };
4158
const int rows = 3;
4159
const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));
4160
for (int nx = 0; nx < 4; nx++)
4161
{
4162
if (nx > 0) ImGui::SameLine();
4163
ImGui::BeginGroup();
4164
for (int ny = 0; ny < rows; ny++)
4165
{
4166
ImGui::PushID(nx * rows + ny);
4167
ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, "");
4168
if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4169
ImGui::SetTooltip("%.3f", values2[nx]);
4170
ImGui::PopID();
4171
}
4172
ImGui::EndGroup();
4173
}
4174
ImGui::PopID();
4175
4176
ImGui::SameLine();
4177
ImGui::PushID("set3");
4178
for (int i = 0; i < 4; i++)
4179
{
4180
if (i > 0) ImGui::SameLine();
4181
ImGui::PushID(i);
4182
ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40);
4183
ImGui::VSliderFloat("##v", ImVec2(40, 160), &values[i], 0.0f, 1.0f, "%.2f\nsec");
4184
ImGui::PopStyleVar();
4185
ImGui::PopID();
4186
}
4187
ImGui::PopID();
4188
ImGui::PopStyleVar();
4189
ImGui::TreePop();
4190
}
4191
}
4192
4193
//-----------------------------------------------------------------------------
4194
// [SECTION] DemoWindowWidgets()
4195
//-----------------------------------------------------------------------------
4196
4197
static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)
4198
{
4199
IMGUI_DEMO_MARKER("Widgets");
4200
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
4201
if (!ImGui::CollapsingHeader("Widgets"))
4202
return;
4203
4204
const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom
4205
if (disable_all)
4206
ImGui::BeginDisabled();
4207
4208
DemoWindowWidgetsBasic();
4209
DemoWindowWidgetsBullets();
4210
DemoWindowWidgetsCollapsingHeaders();
4211
DemoWindowWidgetsComboBoxes();
4212
DemoWindowWidgetsColorAndPickers();
4213
DemoWindowWidgetsDataTypes();
4214
4215
if (disable_all)
4216
ImGui::EndDisabled();
4217
DemoWindowWidgetsDisableBlocks(demo_data);
4218
if (disable_all)
4219
ImGui::BeginDisabled();
4220
4221
DemoWindowWidgetsDragAndDrop();
4222
DemoWindowWidgetsDragsAndSliders();
4223
DemoWindowWidgetsFonts();
4224
DemoWindowWidgetsImages();
4225
DemoWindowWidgetsListBoxes();
4226
DemoWindowWidgetsMultiComponents();
4227
DemoWindowWidgetsPlotting();
4228
DemoWindowWidgetsProgressBars();
4229
DemoWindowWidgetsQueryingStatuses();
4230
DemoWindowWidgetsSelectables();
4231
DemoWindowWidgetsSelectionAndMultiSelect(demo_data);
4232
DemoWindowWidgetsTabs();
4233
DemoWindowWidgetsText();
4234
DemoWindowWidgetsTextFilter();
4235
DemoWindowWidgetsTextInput();
4236
DemoWindowWidgetsTooltips();
4237
DemoWindowWidgetsTreeNodes();
4238
DemoWindowWidgetsVerticalSliders();
4239
4240
if (disable_all)
4241
ImGui::EndDisabled();
4242
}
4243
4244
//-----------------------------------------------------------------------------
4245
// [SECTION] DemoWindowLayout()
4246
//-----------------------------------------------------------------------------
4247
4248
static void DemoWindowLayout()
4249
{
4250
IMGUI_DEMO_MARKER("Layout");
4251
if (!ImGui::CollapsingHeader("Layout & Scrolling"))
4252
return;
4253
4254
IMGUI_DEMO_MARKER("Layout/Child windows");
4255
if (ImGui::TreeNode("Child windows"))
4256
{
4257
ImGui::SeparatorText("Child windows");
4258
4259
HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.");
4260
static bool disable_mouse_wheel = false;
4261
static bool disable_menu = false;
4262
ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel);
4263
ImGui::Checkbox("Disable Menu", &disable_menu);
4264
4265
// Child 1: no border, enable horizontal scrollbar
4266
{
4267
ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
4268
if (disable_mouse_wheel)
4269
window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4270
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags);
4271
for (int i = 0; i < 100; i++)
4272
ImGui::Text("%04d: scrollable region", i);
4273
ImGui::EndChild();
4274
}
4275
4276
ImGui::SameLine();
4277
4278
// Child 2: rounded border
4279
{
4280
ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
4281
if (disable_mouse_wheel)
4282
window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4283
if (!disable_menu)
4284
window_flags |= ImGuiWindowFlags_MenuBar;
4285
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
4286
ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Borders, window_flags);
4287
if (!disable_menu && ImGui::BeginMenuBar())
4288
{
4289
if (ImGui::BeginMenu("Menu"))
4290
{
4291
ShowExampleMenuFile();
4292
ImGui::EndMenu();
4293
}
4294
ImGui::EndMenuBar();
4295
}
4296
if (ImGui::BeginTable("split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))
4297
{
4298
for (int i = 0; i < 100; i++)
4299
{
4300
char buf[32];
4301
sprintf(buf, "%03d", i);
4302
ImGui::TableNextColumn();
4303
ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
4304
}
4305
ImGui::EndTable();
4306
}
4307
ImGui::EndChild();
4308
ImGui::PopStyleVar();
4309
}
4310
4311
// Child 3: manual-resize
4312
ImGui::SeparatorText("Manual-resize");
4313
{
4314
HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
4315
//if (ImGui::Button("Set Height to 200"))
4316
// ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
4317
4318
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
4319
if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
4320
for (int n = 0; n < 10; n++)
4321
ImGui::Text("Line %04d", n);
4322
ImGui::PopStyleColor();
4323
ImGui::EndChild();
4324
}
4325
4326
// Child 4: auto-resizing height with a limit
4327
ImGui::SeparatorText("Auto-resize with constraints");
4328
{
4329
static int draw_lines = 3;
4330
static int max_height_in_lines = 10;
4331
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4332
ImGui::DragInt("Lines Count", &draw_lines, 0.2f);
4333
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4334
ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f);
4335
4336
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));
4337
if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY))
4338
for (int n = 0; n < draw_lines; n++)
4339
ImGui::Text("Line %04d", n);
4340
ImGui::EndChild();
4341
}
4342
4343
ImGui::SeparatorText("Misc/Advanced");
4344
4345
// Demonstrate a few extra things
4346
// - Changing ImGuiCol_ChildBg (which is transparent black in default styles)
4347
// - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
4348
// You can also call SetNextWindowPos() to position the child window. The parent window will effectively
4349
// layout from this position.
4350
// - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
4351
// the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.
4352
{
4353
static int offset_x = 0;
4354
static bool override_bg_color = true;
4355
static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;
4356
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4357
ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000);
4358
ImGui::Checkbox("Override ChildBg color", &override_bg_color);
4359
ImGui::CheckboxFlags("ImGuiChildFlags_Borders", &child_flags, ImGuiChildFlags_Borders);
4360
ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding);
4361
ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX);
4362
ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY);
4363
ImGui::CheckboxFlags("ImGuiChildFlags_FrameStyle", &child_flags, ImGuiChildFlags_FrameStyle);
4364
ImGui::SameLine(); HelpMarker("Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.");
4365
if (child_flags & ImGuiChildFlags_FrameStyle)
4366
override_bg_color = false;
4367
4368
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
4369
if (override_bg_color)
4370
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
4371
ImGui::BeginChild("Red", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None);
4372
if (override_bg_color)
4373
ImGui::PopStyleColor();
4374
4375
for (int n = 0; n < 50; n++)
4376
ImGui::Text("Some test %d", n);
4377
ImGui::EndChild();
4378
bool child_is_hovered = ImGui::IsItemHovered();
4379
ImVec2 child_rect_min = ImGui::GetItemRectMin();
4380
ImVec2 child_rect_max = ImGui::GetItemRectMax();
4381
ImGui::Text("Hovered: %d", child_is_hovered);
4382
ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y);
4383
}
4384
4385
ImGui::TreePop();
4386
}
4387
4388
IMGUI_DEMO_MARKER("Layout/Widgets Width");
4389
if (ImGui::TreeNode("Widgets Width"))
4390
{
4391
static float f = 0.0f;
4392
static bool show_indented_items = true;
4393
ImGui::Checkbox("Show indented items", &show_indented_items);
4394
4395
// Use SetNextItemWidth() to set the width of a single upcoming item.
4396
// Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
4397
// In real code use you'll probably want to choose width values that are proportional to your font size
4398
// e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
4399
4400
ImGui::Text("SetNextItemWidth/PushItemWidth(100)");
4401
ImGui::SameLine(); HelpMarker("Fixed width.");
4402
ImGui::PushItemWidth(100);
4403
ImGui::DragFloat("float##1b", &f);
4404
if (show_indented_items)
4405
{
4406
ImGui::Indent();
4407
ImGui::DragFloat("float (indented)##1b", &f);
4408
ImGui::Unindent();
4409
}
4410
ImGui::PopItemWidth();
4411
4412
ImGui::Text("SetNextItemWidth/PushItemWidth(-100)");
4413
ImGui::SameLine(); HelpMarker("Align to right edge minus 100");
4414
ImGui::PushItemWidth(-100);
4415
ImGui::DragFloat("float##2a", &f);
4416
if (show_indented_items)
4417
{
4418
ImGui::Indent();
4419
ImGui::DragFloat("float (indented)##2b", &f);
4420
ImGui::Unindent();
4421
}
4422
ImGui::PopItemWidth();
4423
4424
ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)");
4425
ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)");
4426
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f);
4427
ImGui::DragFloat("float##3a", &f);
4428
if (show_indented_items)
4429
{
4430
ImGui::Indent();
4431
ImGui::DragFloat("float (indented)##3b", &f);
4432
ImGui::Unindent();
4433
}
4434
ImGui::PopItemWidth();
4435
4436
ImGui::Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)");
4437
ImGui::SameLine(); HelpMarker("Align to right edge minus half");
4438
ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
4439
ImGui::DragFloat("float##4a", &f);
4440
if (show_indented_items)
4441
{
4442
ImGui::Indent();
4443
ImGui::DragFloat("float (indented)##4b", &f);
4444
ImGui::Unindent();
4445
}
4446
ImGui::PopItemWidth();
4447
4448
ImGui::Text("SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))");
4449
ImGui::PushItemWidth(-IM_MIN(ImGui::GetFontSize() * 12, ImGui::GetContentRegionAvail().x * 0.40f));
4450
ImGui::DragFloat("float##5a", &f);
4451
if (show_indented_items)
4452
{
4453
ImGui::Indent();
4454
ImGui::DragFloat("float (indented)##5b", &f);
4455
ImGui::Unindent();
4456
}
4457
ImGui::PopItemWidth();
4458
4459
// Demonstrate using PushItemWidth to surround three items.
4460
// Calling SetNextItemWidth() before each of them would have the same effect.
4461
ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)");
4462
ImGui::SameLine(); HelpMarker("Align to right edge");
4463
ImGui::PushItemWidth(-FLT_MIN);
4464
ImGui::DragFloat("##float6a", &f);
4465
if (show_indented_items)
4466
{
4467
ImGui::Indent();
4468
ImGui::DragFloat("float (indented)##6b", &f);
4469
ImGui::Unindent();
4470
}
4471
ImGui::PopItemWidth();
4472
4473
ImGui::TreePop();
4474
}
4475
4476
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout");
4477
if (ImGui::TreeNode("Basic Horizontal Layout"))
4478
{
4479
ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)");
4480
4481
// Text
4482
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine");
4483
ImGui::Text("Two items: Hello"); ImGui::SameLine();
4484
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
4485
4486
// Adjust spacing
4487
ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20);
4488
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
4489
4490
// Button
4491
ImGui::AlignTextToFramePadding();
4492
ImGui::Text("Normal buttons"); ImGui::SameLine();
4493
ImGui::Button("Banana"); ImGui::SameLine();
4494
ImGui::Button("Apple"); ImGui::SameLine();
4495
ImGui::Button("Corniflower");
4496
4497
// Button
4498
ImGui::Text("Small buttons"); ImGui::SameLine();
4499
ImGui::SmallButton("Like this one"); ImGui::SameLine();
4500
ImGui::Text("can fit within a text block.");
4501
4502
// Aligned to arbitrary position. Easy/cheap column.
4503
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)");
4504
ImGui::Text("Aligned");
4505
ImGui::SameLine(150); ImGui::Text("x=150");
4506
ImGui::SameLine(300); ImGui::Text("x=300");
4507
ImGui::Text("Aligned");
4508
ImGui::SameLine(150); ImGui::SmallButton("x=150");
4509
ImGui::SameLine(300); ImGui::SmallButton("x=300");
4510
4511
// Checkbox
4512
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)");
4513
static bool c1 = false, c2 = false, c3 = false, c4 = false;
4514
ImGui::Checkbox("My", &c1); ImGui::SameLine();
4515
ImGui::Checkbox("Tailor", &c2); ImGui::SameLine();
4516
ImGui::Checkbox("Is", &c3); ImGui::SameLine();
4517
ImGui::Checkbox("Rich", &c4);
4518
4519
// Various
4520
static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;
4521
ImGui::PushItemWidth(80);
4522
const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" };
4523
static int item = -1;
4524
ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine();
4525
ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine();
4526
ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine();
4527
ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f);
4528
ImGui::PopItemWidth();
4529
4530
ImGui::PushItemWidth(80);
4531
ImGui::Text("Lists:");
4532
static int selection[4] = { 0, 1, 2, 3 };
4533
for (int i = 0; i < 4; i++)
4534
{
4535
if (i > 0) ImGui::SameLine();
4536
ImGui::PushID(i);
4537
ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items));
4538
ImGui::PopID();
4539
//ImGui::SetItemTooltip("ListBox %d hovered", i);
4540
}
4541
ImGui::PopItemWidth();
4542
4543
// Dummy
4544
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy");
4545
ImVec2 button_sz(40, 40);
4546
ImGui::Button("A", button_sz); ImGui::SameLine();
4547
ImGui::Dummy(button_sz); ImGui::SameLine();
4548
ImGui::Button("B", button_sz);
4549
4550
// Manually wrapping
4551
// (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
4552
IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping");
4553
ImGui::Text("Manual wrapping:");
4554
ImGuiStyle& style = ImGui::GetStyle();
4555
int buttons_count = 20;
4556
float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
4557
for (int n = 0; n < buttons_count; n++)
4558
{
4559
ImGui::PushID(n);
4560
ImGui::Button("Box", button_sz);
4561
float last_button_x2 = ImGui::GetItemRectMax().x;
4562
float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line
4563
if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)
4564
ImGui::SameLine();
4565
ImGui::PopID();
4566
}
4567
4568
ImGui::TreePop();
4569
}
4570
4571
IMGUI_DEMO_MARKER("Layout/Groups");
4572
if (ImGui::TreeNode("Groups"))
4573
{
4574
HelpMarker(
4575
"BeginGroup() basically locks the horizontal position for new line. "
4576
"EndGroup() bundles the whole group so that you can use \"item\" functions such as "
4577
"IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
4578
ImGui::BeginGroup();
4579
{
4580
ImGui::BeginGroup();
4581
ImGui::Button("AAA");
4582
ImGui::SameLine();
4583
ImGui::Button("BBB");
4584
ImGui::SameLine();
4585
ImGui::BeginGroup();
4586
ImGui::Button("CCC");
4587
ImGui::Button("DDD");
4588
ImGui::EndGroup();
4589
ImGui::SameLine();
4590
ImGui::Button("EEE");
4591
ImGui::EndGroup();
4592
ImGui::SetItemTooltip("First group hovered");
4593
}
4594
// Capture the group size and create widgets using the same size
4595
ImVec2 size = ImGui::GetItemRectSize();
4596
const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };
4597
ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size);
4598
4599
ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4600
ImGui::SameLine();
4601
ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4602
ImGui::EndGroup();
4603
ImGui::SameLine();
4604
4605
ImGui::Button("LEVERAGE\nBUZZWORD", size);
4606
ImGui::SameLine();
4607
4608
if (ImGui::BeginListBox("List", size))
4609
{
4610
ImGui::Selectable("Selected", true);
4611
ImGui::Selectable("Not Selected", false);
4612
ImGui::EndListBox();
4613
}
4614
4615
ImGui::TreePop();
4616
}
4617
4618
IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment");
4619
if (ImGui::TreeNode("Text Baseline Alignment"))
4620
{
4621
{
4622
ImGui::BulletText("Text baseline:");
4623
ImGui::SameLine(); HelpMarker(
4624
"This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. "
4625
"Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets.");
4626
ImGui::Indent();
4627
4628
ImGui::Text("KO Blahblah"); ImGui::SameLine();
4629
ImGui::Button("Some framed item"); ImGui::SameLine();
4630
HelpMarker("Baseline of button will look misaligned with text..");
4631
4632
// If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4633
// (because we don't know what's coming after the Text() statement, we need to move the text baseline
4634
// down by FramePadding.y ahead of time)
4635
ImGui::AlignTextToFramePadding();
4636
ImGui::Text("OK Blahblah"); ImGui::SameLine();
4637
ImGui::Button("Some framed item##2"); ImGui::SameLine();
4638
HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y");
4639
4640
// SmallButton() uses the same vertical padding as Text
4641
ImGui::Button("TEST##1"); ImGui::SameLine();
4642
ImGui::Text("TEST"); ImGui::SameLine();
4643
ImGui::SmallButton("TEST##2");
4644
4645
// If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4646
ImGui::AlignTextToFramePadding();
4647
ImGui::Text("Text aligned to framed item"); ImGui::SameLine();
4648
ImGui::Button("Item##1"); ImGui::SameLine();
4649
ImGui::Text("Item"); ImGui::SameLine();
4650
ImGui::SmallButton("Item##2"); ImGui::SameLine();
4651
ImGui::Button("Item##3");
4652
4653
ImGui::Unindent();
4654
}
4655
4656
ImGui::Spacing();
4657
4658
{
4659
ImGui::BulletText("Multi-line text:");
4660
ImGui::Indent();
4661
ImGui::Text("One\nTwo\nThree"); ImGui::SameLine();
4662
ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4663
ImGui::Text("Banana");
4664
4665
ImGui::Text("Banana"); ImGui::SameLine();
4666
ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4667
ImGui::Text("One\nTwo\nThree");
4668
4669
ImGui::Button("HOP##1"); ImGui::SameLine();
4670
ImGui::Text("Banana"); ImGui::SameLine();
4671
ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4672
ImGui::Text("Banana");
4673
4674
ImGui::Button("HOP##2"); ImGui::SameLine();
4675
ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4676
ImGui::Text("Banana");
4677
ImGui::Unindent();
4678
}
4679
4680
ImGui::Spacing();
4681
4682
{
4683
ImGui::BulletText("Misc items:");
4684
ImGui::Indent();
4685
4686
// SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
4687
ImGui::Button("80x80", ImVec2(80, 80));
4688
ImGui::SameLine();
4689
ImGui::Button("50x50", ImVec2(50, 50));
4690
ImGui::SameLine();
4691
ImGui::Button("Button()");
4692
ImGui::SameLine();
4693
ImGui::SmallButton("SmallButton()");
4694
4695
// Tree
4696
// (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline)
4697
const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
4698
ImGui::Button("Button##1");
4699
ImGui::SameLine(0.0f, spacing);
4700
if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone))
4701
{
4702
// Placeholder tree data
4703
for (int i = 0; i < 6; i++)
4704
ImGui::BulletText("Item %d..", i);
4705
ImGui::TreePop();
4706
}
4707
4708
// Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
4709
// Otherwise you can use SmallButton() (smaller fit).
4710
ImGui::AlignTextToFramePadding();
4711
4712
// Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
4713
// other contents below the node.
4714
bool node_open = ImGui::TreeNode("Node##2");
4715
ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2");
4716
if (node_open)
4717
{
4718
// Placeholder tree data
4719
for (int i = 0; i < 6; i++)
4720
ImGui::BulletText("Item %d..", i);
4721
ImGui::TreePop();
4722
}
4723
4724
// Bullet
4725
ImGui::Button("Button##3");
4726
ImGui::SameLine(0.0f, spacing);
4727
ImGui::BulletText("Bullet text");
4728
4729
ImGui::AlignTextToFramePadding();
4730
ImGui::BulletText("Node");
4731
ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4");
4732
ImGui::Unindent();
4733
}
4734
4735
ImGui::TreePop();
4736
}
4737
4738
IMGUI_DEMO_MARKER("Layout/Scrolling");
4739
if (ImGui::TreeNode("Scrolling"))
4740
{
4741
// Vertical scroll functions
4742
IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical");
4743
HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.");
4744
4745
static int track_item = 50;
4746
static bool enable_track = true;
4747
static bool enable_extra_decorations = false;
4748
static float scroll_to_off_px = 0.0f;
4749
static float scroll_to_pos_px = 200.0f;
4750
4751
ImGui::Checkbox("Decoration", &enable_extra_decorations);
4752
4753
ImGui::Checkbox("Track", &enable_track);
4754
ImGui::PushItemWidth(100);
4755
ImGui::SameLine(140); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d");
4756
4757
bool scroll_to_off = ImGui::Button("Scroll Offset");
4758
ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px");
4759
4760
bool scroll_to_pos = ImGui::Button("Scroll To Pos");
4761
ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");
4762
ImGui::PopItemWidth();
4763
4764
if (scroll_to_off || scroll_to_pos)
4765
enable_track = false;
4766
4767
ImGuiStyle& style = ImGui::GetStyle();
4768
float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;
4769
if (child_w < 1.0f)
4770
child_w = 1.0f;
4771
ImGui::PushID("##VerticalScrolling");
4772
for (int i = 0; i < 5; i++)
4773
{
4774
if (i > 0) ImGui::SameLine();
4775
ImGui::BeginGroup();
4776
const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" };
4777
ImGui::TextUnformatted(names[i]);
4778
4779
const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
4780
const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
4781
const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Borders, child_flags);
4782
if (ImGui::BeginMenuBar())
4783
{
4784
ImGui::TextUnformatted("abc");
4785
ImGui::EndMenuBar();
4786
}
4787
if (scroll_to_off)
4788
ImGui::SetScrollY(scroll_to_off_px);
4789
if (scroll_to_pos)
4790
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);
4791
if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4792
{
4793
for (int item = 0; item < 100; item++)
4794
{
4795
if (enable_track && item == track_item)
4796
{
4797
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
4798
ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom
4799
}
4800
else
4801
{
4802
ImGui::Text("Item %d", item);
4803
}
4804
}
4805
}
4806
float scroll_y = ImGui::GetScrollY();
4807
float scroll_max_y = ImGui::GetScrollMaxY();
4808
ImGui::EndChild();
4809
ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y);
4810
ImGui::EndGroup();
4811
}
4812
ImGui::PopID();
4813
4814
// Horizontal scroll functions
4815
IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal");
4816
ImGui::Spacing();
4817
HelpMarker(
4818
"Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
4819
"Because the clipping rectangle of most window hides half worth of WindowPadding on the "
4820
"left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the "
4821
"equivalent SetScrollFromPosY(+1) wouldn't.");
4822
ImGui::PushID("##HorizontalScrolling");
4823
for (int i = 0; i < 5; i++)
4824
{
4825
float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
4826
ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
4827
ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
4828
bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Borders, child_flags);
4829
if (scroll_to_off)
4830
ImGui::SetScrollX(scroll_to_off_px);
4831
if (scroll_to_pos)
4832
ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f);
4833
if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4834
{
4835
for (int item = 0; item < 100; item++)
4836
{
4837
if (item > 0)
4838
ImGui::SameLine();
4839
if (enable_track && item == track_item)
4840
{
4841
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
4842
ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right
4843
}
4844
else
4845
{
4846
ImGui::Text("Item %d", item);
4847
}
4848
}
4849
}
4850
float scroll_x = ImGui::GetScrollX();
4851
float scroll_max_x = ImGui::GetScrollMaxX();
4852
ImGui::EndChild();
4853
ImGui::SameLine();
4854
const char* names[] = { "Left", "25%", "Center", "75%", "Right" };
4855
ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x);
4856
ImGui::Spacing();
4857
}
4858
ImGui::PopID();
4859
4860
// Miscellaneous Horizontal Scrolling Demo
4861
IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)");
4862
HelpMarker(
4863
"Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n"
4864
"You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().");
4865
static int lines = 7;
4866
ImGui::SliderInt("Lines", &lines, 1, 15);
4867
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
4868
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
4869
ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
4870
ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar);
4871
for (int line = 0; line < lines; line++)
4872
{
4873
// Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
4874
// If you want to create your own time line for a real application you may be better off manipulating
4875
// the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
4876
// yourself. You may also want to use the lower-level ImDrawList API.
4877
int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);
4878
for (int n = 0; n < num_buttons; n++)
4879
{
4880
if (n > 0) ImGui::SameLine();
4881
ImGui::PushID(n + line * 1000);
4882
char num_buf[16];
4883
sprintf(num_buf, "%d", n);
4884
const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf;
4885
float hue = n * 0.05f;
4886
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f));
4887
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f));
4888
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f));
4889
ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f));
4890
ImGui::PopStyleColor(3);
4891
ImGui::PopID();
4892
}
4893
}
4894
float scroll_x = ImGui::GetScrollX();
4895
float scroll_max_x = ImGui::GetScrollMaxX();
4896
ImGui::EndChild();
4897
ImGui::PopStyleVar(2);
4898
float scroll_x_delta = 0.0f;
4899
ImGui::SmallButton("<<");
4900
if (ImGui::IsItemActive())
4901
scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;
4902
ImGui::SameLine();
4903
ImGui::Text("Scroll from code"); ImGui::SameLine();
4904
ImGui::SmallButton(">>");
4905
if (ImGui::IsItemActive())
4906
scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;
4907
ImGui::SameLine();
4908
ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x);
4909
if (scroll_x_delta != 0.0f)
4910
{
4911
// Demonstrate a trick: you can use Begin to set yourself in the context of another window
4912
// (here we are already out of your child window)
4913
ImGui::BeginChild("scrolling");
4914
ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
4915
ImGui::EndChild();
4916
}
4917
ImGui::Spacing();
4918
4919
static bool show_horizontal_contents_size_demo_window = false;
4920
ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window);
4921
4922
if (show_horizontal_contents_size_demo_window)
4923
{
4924
static bool show_h_scrollbar = true;
4925
static bool show_button = true;
4926
static bool show_tree_nodes = true;
4927
static bool show_text_wrapped = false;
4928
static bool show_columns = true;
4929
static bool show_tab_bar = true;
4930
static bool show_child = false;
4931
static bool explicit_content_size = false;
4932
static float contents_size_x = 300.0f;
4933
if (explicit_content_size)
4934
ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));
4935
ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);
4936
IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window");
4937
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0));
4938
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0));
4939
HelpMarker(
4940
"Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n"
4941
"Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles.");
4942
ImGui::Checkbox("H-scrollbar", &show_h_scrollbar);
4943
ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten)
4944
ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width
4945
ImGui::Checkbox("Text wrapped", &show_text_wrapped);// Will grow and use contents size
4946
ImGui::Checkbox("Columns", &show_columns); // Will use contents size
4947
ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size
4948
ImGui::Checkbox("Child", &show_child); // Will grow and use contents size
4949
ImGui::Checkbox("Explicit content size", &explicit_content_size);
4950
ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY());
4951
if (explicit_content_size)
4952
{
4953
ImGui::SameLine();
4954
ImGui::SetNextItemWidth(100);
4955
ImGui::DragFloat("##csx", &contents_size_x);
4956
ImVec2 p = ImGui::GetCursorScreenPos();
4957
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);
4958
ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);
4959
ImGui::Dummy(ImVec2(0, 10));
4960
}
4961
ImGui::PopStyleVar(2);
4962
ImGui::Separator();
4963
if (show_button)
4964
{
4965
ImGui::Button("this is a 300-wide button", ImVec2(300, 0));
4966
}
4967
if (show_tree_nodes)
4968
{
4969
bool open = true;
4970
if (ImGui::TreeNode("this is a tree node"))
4971
{
4972
if (ImGui::TreeNode("another one of those tree node..."))
4973
{
4974
ImGui::Text("Some tree contents");
4975
ImGui::TreePop();
4976
}
4977
ImGui::TreePop();
4978
}
4979
ImGui::CollapsingHeader("CollapsingHeader", &open);
4980
}
4981
if (show_text_wrapped)
4982
{
4983
ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle.");
4984
}
4985
if (show_columns)
4986
{
4987
ImGui::Text("Tables:");
4988
if (ImGui::BeginTable("table", 4, ImGuiTableFlags_Borders))
4989
{
4990
for (int n = 0; n < 4; n++)
4991
{
4992
ImGui::TableNextColumn();
4993
ImGui::Text("Width %.2f", ImGui::GetContentRegionAvail().x);
4994
}
4995
ImGui::EndTable();
4996
}
4997
ImGui::Text("Columns:");
4998
ImGui::Columns(4);
4999
for (int n = 0; n < 4; n++)
5000
{
5001
ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
5002
ImGui::NextColumn();
5003
}
5004
ImGui::Columns(1);
5005
}
5006
if (show_tab_bar && ImGui::BeginTabBar("Hello"))
5007
{
5008
if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); }
5009
if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); }
5010
if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); }
5011
if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); }
5012
ImGui::EndTabBar();
5013
}
5014
if (show_child)
5015
{
5016
ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Borders);
5017
ImGui::EndChild();
5018
}
5019
ImGui::End();
5020
}
5021
5022
ImGui::TreePop();
5023
}
5024
5025
IMGUI_DEMO_MARKER("Layout/Text Clipping");
5026
if (ImGui::TreeNode("Text Clipping"))
5027
{
5028
static ImVec2 size(100.0f, 100.0f);
5029
static ImVec2 offset(30.0f, 30.0f);
5030
ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f");
5031
ImGui::TextWrapped("(Click and drag to scroll)");
5032
5033
HelpMarker(
5034
"(Left) Using ImGui::PushClipRect():\n"
5035
"Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
5036
"(use this if you want your clipping rectangle to affect interactions)\n\n"
5037
"(Center) Using ImDrawList::PushClipRect():\n"
5038
"Will alter ImDrawList rendering only.\n"
5039
"(use this as a shortcut if you are only using ImDrawList calls)\n\n"
5040
"(Right) Using ImDrawList::AddText() with a fine ClipRect:\n"
5041
"Will alter only this specific ImDrawList::AddText() rendering.\n"
5042
"This is often used internally to avoid altering the clipping rectangle and minimize draw calls.");
5043
5044
for (int n = 0; n < 3; n++)
5045
{
5046
if (n > 0)
5047
ImGui::SameLine();
5048
5049
ImGui::PushID(n);
5050
ImGui::InvisibleButton("##canvas", size);
5051
if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))
5052
{
5053
offset.x += ImGui::GetIO().MouseDelta.x;
5054
offset.y += ImGui::GetIO().MouseDelta.y;
5055
}
5056
ImGui::PopID();
5057
if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.
5058
continue;
5059
5060
const ImVec2 p0 = ImGui::GetItemRectMin();
5061
const ImVec2 p1 = ImGui::GetItemRectMax();
5062
const char* text_str = "Line 1 hello\nLine 2 clip me!";
5063
const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
5064
ImDrawList* draw_list = ImGui::GetWindowDrawList();
5065
switch (n)
5066
{
5067
case 0:
5068
ImGui::PushClipRect(p0, p1, true);
5069
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5070
draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
5071
ImGui::PopClipRect();
5072
break;
5073
case 1:
5074
draw_list->PushClipRect(p0, p1, true);
5075
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5076
draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
5077
draw_list->PopClipRect();
5078
break;
5079
case 2:
5080
ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
5081
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5082
draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), ImGui::GetFontWeight(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
5083
break;
5084
}
5085
}
5086
5087
ImGui::TreePop();
5088
}
5089
5090
IMGUI_DEMO_MARKER("Layout/Overlap Mode");
5091
if (ImGui::TreeNode("Overlap Mode"))
5092
{
5093
static bool enable_allow_overlap = true;
5094
5095
HelpMarker(
5096
"Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n"
5097
"By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. "
5098
"Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.");
5099
ImGui::Checkbox("Enable AllowOverlap", &enable_allow_overlap);
5100
5101
ImVec2 button1_pos = ImGui::GetCursorScreenPos();
5102
ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f);
5103
if (enable_allow_overlap)
5104
ImGui::SetNextItemAllowOverlap();
5105
ImGui::Button("Button 1", ImVec2(80, 80));
5106
ImGui::SetCursorScreenPos(button2_pos);
5107
ImGui::Button("Button 2", ImVec2(80, 80));
5108
5109
// This is typically used with width-spanning items.
5110
// (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut
5111
// for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)
5112
if (enable_allow_overlap)
5113
ImGui::SetNextItemAllowOverlap();
5114
ImGui::Selectable("Some Selectable", false);
5115
ImGui::SameLine();
5116
ImGui::SmallButton("++");
5117
5118
ImGui::TreePop();
5119
}
5120
}
5121
5122
//-----------------------------------------------------------------------------
5123
// [SECTION] DemoWindowPopups()
5124
//-----------------------------------------------------------------------------
5125
5126
static void DemoWindowPopups()
5127
{
5128
IMGUI_DEMO_MARKER("Popups");
5129
if (!ImGui::CollapsingHeader("Popups & Modal windows"))
5130
return;
5131
5132
// The properties of popups windows are:
5133
// - They block normal mouse hovering detection outside them. (*)
5134
// - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
5135
// - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
5136
// we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
5137
// (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even
5138
// when normally blocked by a popup.
5139
// Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
5140
// popups at any time.
5141
5142
// Typical use for regular windows:
5143
// bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End();
5144
// Typical use for popups:
5145
// if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); }
5146
5147
// With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
5148
// This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
5149
5150
IMGUI_DEMO_MARKER("Popups/Popups");
5151
if (ImGui::TreeNode("Popups"))
5152
{
5153
ImGui::TextWrapped(
5154
"When a popup is active, it inhibits interacting with windows that are behind the popup. "
5155
"Clicking outside the popup closes it.");
5156
5157
static int selected_fish = -1;
5158
const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" };
5159
static bool toggles[] = { true, false, false, false, false };
5160
5161
// Simple selection popup (if you want to show the current selection inside the Button itself,
5162
// you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
5163
if (ImGui::Button("Select.."))
5164
ImGui::OpenPopup("my_select_popup");
5165
ImGui::SameLine();
5166
ImGui::TextUnformatted(selected_fish == -1 ? "<None>" : names[selected_fish]);
5167
if (ImGui::BeginPopup("my_select_popup"))
5168
{
5169
ImGui::SeparatorText("Aquarium");
5170
for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5171
if (ImGui::Selectable(names[i]))
5172
selected_fish = i;
5173
ImGui::EndPopup();
5174
}
5175
5176
// Showing a menu with toggles
5177
if (ImGui::Button("Toggle.."))
5178
ImGui::OpenPopup("my_toggle_popup");
5179
if (ImGui::BeginPopup("my_toggle_popup"))
5180
{
5181
for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5182
ImGui::MenuItem(names[i], "", &toggles[i]);
5183
if (ImGui::BeginMenu("Sub-menu"))
5184
{
5185
ImGui::MenuItem("Click me");
5186
ImGui::EndMenu();
5187
}
5188
5189
ImGui::Separator();
5190
ImGui::Text("Tooltip here");
5191
ImGui::SetItemTooltip("I am a tooltip over a popup");
5192
5193
if (ImGui::Button("Stacked Popup"))
5194
ImGui::OpenPopup("another popup");
5195
if (ImGui::BeginPopup("another popup"))
5196
{
5197
for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5198
ImGui::MenuItem(names[i], "", &toggles[i]);
5199
if (ImGui::BeginMenu("Sub-menu"))
5200
{
5201
ImGui::MenuItem("Click me");
5202
if (ImGui::Button("Stacked Popup"))
5203
ImGui::OpenPopup("another popup");
5204
if (ImGui::BeginPopup("another popup"))
5205
{
5206
ImGui::Text("I am the last one here.");
5207
ImGui::EndPopup();
5208
}
5209
ImGui::EndMenu();
5210
}
5211
ImGui::EndPopup();
5212
}
5213
ImGui::EndPopup();
5214
}
5215
5216
// Call the more complete ShowExampleMenuFile which we use in various places of this demo
5217
if (ImGui::Button("With a menu.."))
5218
ImGui::OpenPopup("my_file_popup");
5219
if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar))
5220
{
5221
if (ImGui::BeginMenuBar())
5222
{
5223
if (ImGui::BeginMenu("File"))
5224
{
5225
ShowExampleMenuFile();
5226
ImGui::EndMenu();
5227
}
5228
if (ImGui::BeginMenu("Edit"))
5229
{
5230
ImGui::MenuItem("Dummy");
5231
ImGui::EndMenu();
5232
}
5233
ImGui::EndMenuBar();
5234
}
5235
ImGui::Text("Hello from popup!");
5236
ImGui::Button("This is a dummy button..");
5237
ImGui::EndPopup();
5238
}
5239
5240
ImGui::TreePop();
5241
}
5242
5243
IMGUI_DEMO_MARKER("Popups/Context menus");
5244
if (ImGui::TreeNode("Context menus"))
5245
{
5246
HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.");
5247
5248
// BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
5249
// if (id == 0)
5250
// id = GetItemID(); // Use last item id
5251
// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
5252
// OpenPopup(id);
5253
// return BeginPopup(id);
5254
// For advanced uses you may want to replicate and customize this code.
5255
// See more details in BeginPopupContextItem().
5256
5257
// Example 1
5258
// When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
5259
// and BeginPopupContextItem() will use the last item ID as the popup ID.
5260
{
5261
const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" };
5262
static int selected = -1;
5263
for (int n = 0; n < 5; n++)
5264
{
5265
if (ImGui::Selectable(names[n], selected == n))
5266
selected = n;
5267
if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
5268
{
5269
selected = n;
5270
ImGui::Text("This a popup for \"%s\"!", names[n]);
5271
if (ImGui::Button("Close"))
5272
ImGui::CloseCurrentPopup();
5273
ImGui::EndPopup();
5274
}
5275
ImGui::SetItemTooltip("Right-click to open popup");
5276
}
5277
}
5278
5279
// Example 2
5280
// Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().
5281
// Using an explicit identifier is also convenient if you want to activate the popups from different locations.
5282
{
5283
HelpMarker("Text() elements don't have stable identifiers so we need to provide one.");
5284
static float value = 0.5f;
5285
ImGui::Text("Value = %.3f <-- (1) right-click this text", value);
5286
if (ImGui::BeginPopupContextItem("my popup"))
5287
{
5288
if (ImGui::Selectable("Set to zero")) value = 0.0f;
5289
if (ImGui::Selectable("Set to PI")) value = 3.1415f;
5290
ImGui::SetNextItemWidth(-FLT_MIN);
5291
ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f);
5292
ImGui::EndPopup();
5293
}
5294
5295
// We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
5296
// Here we make it that right-clicking this other text element opens the same popup as above.
5297
// The popup itself will be submitted by the code above.
5298
ImGui::Text("(2) Or right-click this text");
5299
ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight);
5300
5301
// Back to square one: manually open the same popup.
5302
if (ImGui::Button("(3) Or click this button"))
5303
ImGui::OpenPopup("my popup");
5304
}
5305
5306
// Example 3
5307
// When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),
5308
// we need to make sure your item identifier is stable.
5309
// In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).
5310
{
5311
HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.");
5312
static char name[32] = "Label1";
5313
char buf[64];
5314
sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
5315
ImGui::Button(buf);
5316
if (ImGui::BeginPopupContextItem())
5317
{
5318
ImGui::Text("Edit name:");
5319
ImGui::InputText("##edit", name, IM_ARRAYSIZE(name));
5320
if (ImGui::Button("Close"))
5321
ImGui::CloseCurrentPopup();
5322
ImGui::EndPopup();
5323
}
5324
ImGui::SameLine(); ImGui::Text("(<-- right-click here)");
5325
}
5326
5327
ImGui::TreePop();
5328
}
5329
5330
IMGUI_DEMO_MARKER("Popups/Modals");
5331
if (ImGui::TreeNode("Modals"))
5332
{
5333
ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside.");
5334
5335
if (ImGui::Button("Delete.."))
5336
ImGui::OpenPopup("Delete?");
5337
5338
// Always center this window when appearing
5339
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
5340
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
5341
5342
if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
5343
{
5344
ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!");
5345
ImGui::Separator();
5346
5347
//static int unused_i = 0;
5348
//ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0");
5349
5350
static bool dont_ask_me_next_time = false;
5351
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
5352
ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time);
5353
ImGui::PopStyleVar();
5354
5355
if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5356
ImGui::SetItemDefaultFocus();
5357
ImGui::SameLine();
5358
if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5359
ImGui::EndPopup();
5360
}
5361
5362
if (ImGui::Button("Stacked modals.."))
5363
ImGui::OpenPopup("Stacked 1");
5364
if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar))
5365
{
5366
if (ImGui::BeginMenuBar())
5367
{
5368
if (ImGui::BeginMenu("File"))
5369
{
5370
if (ImGui::MenuItem("Some menu item")) {}
5371
ImGui::EndMenu();
5372
}
5373
ImGui::EndMenuBar();
5374
}
5375
ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.");
5376
5377
// Testing behavior of widgets stacking their own regular popups over the modal.
5378
static int item = 1;
5379
static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
5380
ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
5381
ImGui::ColorEdit4("Color", color);
5382
5383
if (ImGui::Button("Add another modal.."))
5384
ImGui::OpenPopup("Stacked 2");
5385
5386
// Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
5387
// will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
5388
// of the bool actually doesn't matter here.
5389
bool unused_open = true;
5390
if (ImGui::BeginPopupModal("Stacked 2", &unused_open))
5391
{
5392
ImGui::Text("Hello from Stacked The Second!");
5393
ImGui::ColorEdit4("Color", color); // Allow opening another nested popup
5394
if (ImGui::Button("Close"))
5395
ImGui::CloseCurrentPopup();
5396
ImGui::EndPopup();
5397
}
5398
5399
if (ImGui::Button("Close"))
5400
ImGui::CloseCurrentPopup();
5401
ImGui::EndPopup();
5402
}
5403
5404
ImGui::TreePop();
5405
}
5406
5407
IMGUI_DEMO_MARKER("Popups/Menus inside a regular window");
5408
if (ImGui::TreeNode("Menus inside a regular window"))
5409
{
5410
ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
5411
ImGui::Separator();
5412
5413
ImGui::MenuItem("Menu item", "CTRL+M");
5414
if (ImGui::BeginMenu("Menu inside a regular window"))
5415
{
5416
ShowExampleMenuFile();
5417
ImGui::EndMenu();
5418
}
5419
ImGui::Separator();
5420
ImGui::TreePop();
5421
}
5422
}
5423
5424
// Dummy data structure that we use for the Table demo.
5425
// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)
5426
namespace
5427
{
5428
// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.
5429
// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.
5430
// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)
5431
// If you don't use sorting, you will generally never care about giving column an ID!
5432
enum MyItemColumnID
5433
{
5434
MyItemColumnID_ID,
5435
MyItemColumnID_Name,
5436
MyItemColumnID_Action,
5437
MyItemColumnID_Quantity,
5438
MyItemColumnID_Description
5439
};
5440
5441
struct MyItem
5442
{
5443
int ID;
5444
const char* Name;
5445
int Quantity;
5446
5447
// We have a problem which is affecting _only this demo_ and should not affect your code:
5448
// As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
5449
// however qsort doesn't allow passing user data to comparing function.
5450
// As a workaround, we are storing the sort specs in a static/global for the comparing function to access.
5451
// In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.
5452
// We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called
5453
// very often by the sorting algorithm it would be a little wasteful.
5454
static const ImGuiTableSortSpecs* s_current_sort_specs;
5455
5456
static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count)
5457
{
5458
s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
5459
if (items_count > 1)
5460
qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs);
5461
s_current_sort_specs = NULL;
5462
}
5463
5464
// Compare function to be used by qsort()
5465
static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
5466
{
5467
const MyItem* a = (const MyItem*)lhs;
5468
const MyItem* b = (const MyItem*)rhs;
5469
for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
5470
{
5471
// Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()
5472
// We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!
5473
const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
5474
int delta = 0;
5475
switch (sort_spec->ColumnUserID)
5476
{
5477
case MyItemColumnID_ID: delta = (a->ID - b->ID); break;
5478
case MyItemColumnID_Name: delta = (strcmp(a->Name, b->Name)); break;
5479
case MyItemColumnID_Quantity: delta = (a->Quantity - b->Quantity); break;
5480
case MyItemColumnID_Description: delta = (strcmp(a->Name, b->Name)); break;
5481
default: IM_ASSERT(0); break;
5482
}
5483
if (delta > 0)
5484
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
5485
if (delta < 0)
5486
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
5487
}
5488
5489
// qsort() is instable so always return a way to differentiate items.
5490
// Your own compare function may want to avoid fallback on implicit sort specs.
5491
// e.g. a Name compare if it wasn't already part of the sort specs.
5492
return (a->ID - b->ID);
5493
}
5494
};
5495
const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;
5496
}
5497
5498
// Make the UI compact because there are so many fields
5499
static void PushStyleCompact()
5500
{
5501
ImGuiStyle& style = ImGui::GetStyle();
5502
ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, (float)(int)(style.FramePadding.y * 0.60f));
5503
ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, (float)(int)(style.ItemSpacing.y * 0.60f));
5504
}
5505
5506
static void PopStyleCompact()
5507
{
5508
ImGui::PopStyleVar(2);
5509
}
5510
5511
// Show a combo box with a choice of sizing policies
5512
static void EditTableSizingFlags(ImGuiTableFlags* p_flags)
5513
{
5514
struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };
5515
static const EnumDesc policies[] =
5516
{
5517
{ ImGuiTableFlags_None, "Default", "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." },
5518
{ ImGuiTableFlags_SizingFixedFit, "ImGuiTableFlags_SizingFixedFit", "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." },
5519
{ ImGuiTableFlags_SizingFixedSame, "ImGuiTableFlags_SizingFixedSame", "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." },
5520
{ ImGuiTableFlags_SizingStretchProp, "ImGuiTableFlags_SizingStretchProp", "Columns default to _WidthStretch with weights proportional to their widths." },
5521
{ ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." }
5522
};
5523
int idx;
5524
for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++)
5525
if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))
5526
break;
5527
const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : "";
5528
if (ImGui::BeginCombo("Sizing Policy", preview_text))
5529
{
5530
for (int n = 0; n < IM_ARRAYSIZE(policies); n++)
5531
if (ImGui::Selectable(policies[n].Name, idx == n))
5532
*p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;
5533
ImGui::EndCombo();
5534
}
5535
ImGui::SameLine();
5536
ImGui::TextDisabled("(?)");
5537
if (ImGui::BeginItemTooltip())
5538
{
5539
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f);
5540
for (int m = 0; m < IM_ARRAYSIZE(policies); m++)
5541
{
5542
ImGui::Separator();
5543
ImGui::Text("%s:", policies[m].Name);
5544
ImGui::Separator();
5545
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);
5546
ImGui::TextUnformatted(policies[m].Tooltip);
5547
}
5548
ImGui::PopTextWrapPos();
5549
ImGui::EndTooltip();
5550
}
5551
}
5552
5553
static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)
5554
{
5555
ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)");
5556
ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide);
5557
ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort);
5558
if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch))
5559
*p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);
5560
if (ImGui::CheckboxFlags("_WidthFixed", p_flags, ImGuiTableColumnFlags_WidthFixed))
5561
*p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);
5562
ImGui::CheckboxFlags("_NoResize", p_flags, ImGuiTableColumnFlags_NoResize);
5563
ImGui::CheckboxFlags("_NoReorder", p_flags, ImGuiTableColumnFlags_NoReorder);
5564
ImGui::CheckboxFlags("_NoHide", p_flags, ImGuiTableColumnFlags_NoHide);
5565
ImGui::CheckboxFlags("_NoClip", p_flags, ImGuiTableColumnFlags_NoClip);
5566
ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort);
5567
ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending);
5568
ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending);
5569
ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel);
5570
ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth);
5571
ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending);
5572
ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending);
5573
ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0");
5574
ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0");
5575
ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader);
5576
}
5577
5578
static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)
5579
{
5580
ImGui::CheckboxFlags("_IsEnabled", &flags, ImGuiTableColumnFlags_IsEnabled);
5581
ImGui::CheckboxFlags("_IsVisible", &flags, ImGuiTableColumnFlags_IsVisible);
5582
ImGui::CheckboxFlags("_IsSorted", &flags, ImGuiTableColumnFlags_IsSorted);
5583
ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered);
5584
}
5585
5586
//-----------------------------------------------------------------------------
5587
// [SECTION] DemoWindowTables()
5588
//-----------------------------------------------------------------------------
5589
5590
static void DemoWindowTables()
5591
{
5592
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
5593
IMGUI_DEMO_MARKER("Tables");
5594
if (!ImGui::CollapsingHeader("Tables & Columns"))
5595
return;
5596
5597
// Using those as a base value to create width/height that are factor of the size of our font
5598
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
5599
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
5600
5601
ImGui::PushID("Tables");
5602
5603
int open_action = -1;
5604
if (ImGui::Button("Expand all"))
5605
open_action = 1;
5606
ImGui::SameLine();
5607
if (ImGui::Button("Collapse all"))
5608
open_action = 0;
5609
ImGui::SameLine();
5610
5611
// Options
5612
static bool disable_indent = false;
5613
ImGui::Checkbox("Disable tree indentation", &disable_indent);
5614
ImGui::SameLine();
5615
HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width.");
5616
ImGui::Separator();
5617
if (disable_indent)
5618
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f);
5619
5620
// About Styling of tables
5621
// Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.
5622
// There are however a few settings that a shared and part of the ImGuiStyle structure:
5623
// style.CellPadding // Padding within each cell
5624
// style.Colors[ImGuiCol_TableHeaderBg] // Table header background
5625
// style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders
5626
// style.Colors[ImGuiCol_TableBorderLight] // Table inner borders
5627
// style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even rows)
5628
// style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)
5629
5630
// Demos
5631
if (open_action != -1)
5632
ImGui::SetNextItemOpen(open_action != 0);
5633
IMGUI_DEMO_MARKER("Tables/Basic");
5634
if (ImGui::TreeNode("Basic"))
5635
{
5636
// Here we will showcase three different ways to output a table.
5637
// They are very simple variations of a same thing!
5638
5639
// [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
5640
// In many situations, this is the most flexible and easy to use pattern.
5641
HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
5642
if (ImGui::BeginTable("table1", 3))
5643
{
5644
for (int row = 0; row < 4; row++)
5645
{
5646
ImGui::TableNextRow();
5647
for (int column = 0; column < 3; column++)
5648
{
5649
ImGui::TableSetColumnIndex(column);
5650
ImGui::Text("Row %d Column %d", row, column);
5651
}
5652
}
5653
ImGui::EndTable();
5654
}
5655
5656
// [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().
5657
// This is generally more convenient when you have code manually submitting the contents of each column.
5658
HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.");
5659
if (ImGui::BeginTable("table2", 3))
5660
{
5661
for (int row = 0; row < 4; row++)
5662
{
5663
ImGui::TableNextRow();
5664
ImGui::TableNextColumn();
5665
ImGui::Text("Row %d", row);
5666
ImGui::TableNextColumn();
5667
ImGui::Text("Some contents");
5668
ImGui::TableNextColumn();
5669
ImGui::Text("123.456");
5670
}
5671
ImGui::EndTable();
5672
}
5673
5674
// [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),
5675
// as TableNextColumn() will automatically wrap around and create new rows as needed.
5676
// This is generally more convenient when your cells all contains the same type of data.
5677
HelpMarker(
5678
"Only using TableNextColumn(), which tends to be convenient for tables where every cell contains "
5679
"the same type of contents.\n This is also more similar to the old NextColumn() function of the "
5680
"Columns API, and provided to facilitate the Columns->Tables API transition.");
5681
if (ImGui::BeginTable("table3", 3))
5682
{
5683
for (int item = 0; item < 14; item++)
5684
{
5685
ImGui::TableNextColumn();
5686
ImGui::Text("Item %d", item);
5687
}
5688
ImGui::EndTable();
5689
}
5690
5691
ImGui::TreePop();
5692
}
5693
5694
if (open_action != -1)
5695
ImGui::SetNextItemOpen(open_action != 0);
5696
IMGUI_DEMO_MARKER("Tables/Borders, background");
5697
if (ImGui::TreeNode("Borders, background"))
5698
{
5699
// Expose a few Borders related flags interactively
5700
enum ContentsType { CT_Text, CT_FillButton };
5701
static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
5702
static bool display_headers = false;
5703
static int contents_type = CT_Text;
5704
5705
PushStyleCompact();
5706
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
5707
ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
5708
ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH");
5709
ImGui::Indent();
5710
5711
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
5712
ImGui::Indent();
5713
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
5714
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
5715
ImGui::Unindent();
5716
5717
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
5718
ImGui::Indent();
5719
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
5720
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
5721
ImGui::Unindent();
5722
5723
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter);
5724
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner);
5725
ImGui::Unindent();
5726
5727
ImGui::AlignTextToFramePadding(); ImGui::Text("Cell contents:");
5728
ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text);
5729
ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton);
5730
ImGui::Checkbox("Display headers", &display_headers);
5731
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers");
5732
PopStyleCompact();
5733
5734
if (ImGui::BeginTable("table1", 3, flags))
5735
{
5736
// Display headers so we can inspect their interaction with borders
5737
// (Headers are not the main purpose of this section of the demo, so we are not elaborating on them now. See other sections for details)
5738
if (display_headers)
5739
{
5740
ImGui::TableSetupColumn("One");
5741
ImGui::TableSetupColumn("Two");
5742
ImGui::TableSetupColumn("Three");
5743
ImGui::TableHeadersRow();
5744
}
5745
5746
for (int row = 0; row < 5; row++)
5747
{
5748
ImGui::TableNextRow();
5749
for (int column = 0; column < 3; column++)
5750
{
5751
ImGui::TableSetColumnIndex(column);
5752
char buf[32];
5753
sprintf(buf, "Hello %d,%d", column, row);
5754
if (contents_type == CT_Text)
5755
ImGui::TextUnformatted(buf);
5756
else if (contents_type == CT_FillButton)
5757
ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
5758
}
5759
}
5760
ImGui::EndTable();
5761
}
5762
ImGui::TreePop();
5763
}
5764
5765
if (open_action != -1)
5766
ImGui::SetNextItemOpen(open_action != 0);
5767
IMGUI_DEMO_MARKER("Tables/Resizable, stretch");
5768
if (ImGui::TreeNode("Resizable, stretch"))
5769
{
5770
// By default, if we don't enable ScrollX the sizing policy for each column is "Stretch"
5771
// All columns maintain a sizing weight, and they will occupy all available width.
5772
static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5773
PushStyleCompact();
5774
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
5775
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
5776
ImGui::SameLine(); HelpMarker(
5777
"Using the _Resizable flag automatically enables the _BordersInnerV flag as well, "
5778
"this is why the resize borders are still showing when unchecking this.");
5779
PopStyleCompact();
5780
5781
if (ImGui::BeginTable("table1", 3, flags))
5782
{
5783
for (int row = 0; row < 5; row++)
5784
{
5785
ImGui::TableNextRow();
5786
for (int column = 0; column < 3; column++)
5787
{
5788
ImGui::TableSetColumnIndex(column);
5789
ImGui::Text("Hello %d,%d", column, row);
5790
}
5791
}
5792
ImGui::EndTable();
5793
}
5794
ImGui::TreePop();
5795
}
5796
5797
if (open_action != -1)
5798
ImGui::SetNextItemOpen(open_action != 0);
5799
IMGUI_DEMO_MARKER("Tables/Resizable, fixed");
5800
if (ImGui::TreeNode("Resizable, fixed"))
5801
{
5802
// Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)
5803
// So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small)
5804
// If there is not enough available width to fit all columns, they will however be resized down.
5805
// FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings
5806
HelpMarker(
5807
"Using _Resizable + _SizingFixedFit flags.\n"
5808
"Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n"
5809
"Double-click a column border to auto-fit the column to its contents.");
5810
PushStyleCompact();
5811
static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5812
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
5813
PopStyleCompact();
5814
5815
if (ImGui::BeginTable("table1", 3, flags))
5816
{
5817
for (int row = 0; row < 5; row++)
5818
{
5819
ImGui::TableNextRow();
5820
for (int column = 0; column < 3; column++)
5821
{
5822
ImGui::TableSetColumnIndex(column);
5823
ImGui::Text("Hello %d,%d", column, row);
5824
}
5825
}
5826
ImGui::EndTable();
5827
}
5828
ImGui::TreePop();
5829
}
5830
5831
if (open_action != -1)
5832
ImGui::SetNextItemOpen(open_action != 0);
5833
IMGUI_DEMO_MARKER("Tables/Resizable, mixed");
5834
if (ImGui::TreeNode("Resizable, mixed"))
5835
{
5836
HelpMarker(
5837
"Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n"
5838
"When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.");
5839
static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
5840
5841
if (ImGui::BeginTable("table1", 3, flags))
5842
{
5843
ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
5844
ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
5845
ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthStretch);
5846
ImGui::TableHeadersRow();
5847
for (int row = 0; row < 5; row++)
5848
{
5849
ImGui::TableNextRow();
5850
for (int column = 0; column < 3; column++)
5851
{
5852
ImGui::TableSetColumnIndex(column);
5853
ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row);
5854
}
5855
}
5856
ImGui::EndTable();
5857
}
5858
if (ImGui::BeginTable("table2", 6, flags))
5859
{
5860
ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
5861
ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
5862
ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);
5863
ImGui::TableSetupColumn("DDD", ImGuiTableColumnFlags_WidthStretch);
5864
ImGui::TableSetupColumn("EEE", ImGuiTableColumnFlags_WidthStretch);
5865
ImGui::TableSetupColumn("FFF", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);
5866
ImGui::TableHeadersRow();
5867
for (int row = 0; row < 5; row++)
5868
{
5869
ImGui::TableNextRow();
5870
for (int column = 0; column < 6; column++)
5871
{
5872
ImGui::TableSetColumnIndex(column);
5873
ImGui::Text("%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row);
5874
}
5875
}
5876
ImGui::EndTable();
5877
}
5878
ImGui::TreePop();
5879
}
5880
5881
if (open_action != -1)
5882
ImGui::SetNextItemOpen(open_action != 0);
5883
IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers");
5884
if (ImGui::TreeNode("Reorderable, hideable, with headers"))
5885
{
5886
HelpMarker(
5887
"Click and drag column headers to reorder columns.\n\n"
5888
"Right-click on a header to open a context menu.");
5889
static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;
5890
PushStyleCompact();
5891
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
5892
ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
5893
ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
5894
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody);
5895
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
5896
ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
5897
PopStyleCompact();
5898
5899
if (ImGui::BeginTable("table1", 3, flags))
5900
{
5901
// Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.
5902
// (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)
5903
ImGui::TableSetupColumn("One");
5904
ImGui::TableSetupColumn("Two");
5905
ImGui::TableSetupColumn("Three");
5906
ImGui::TableHeadersRow();
5907
for (int row = 0; row < 6; row++)
5908
{
5909
ImGui::TableNextRow();
5910
for (int column = 0; column < 3; column++)
5911
{
5912
ImGui::TableSetColumnIndex(column);
5913
ImGui::Text("Hello %d,%d", column, row);
5914
}
5915
}
5916
ImGui::EndTable();
5917
}
5918
5919
// Use outer_size.x == 0.0f instead of default to make the table as tight as possible
5920
// (only valid when no scrolling and no stretch column)
5921
if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f)))
5922
{
5923
ImGui::TableSetupColumn("One");
5924
ImGui::TableSetupColumn("Two");
5925
ImGui::TableSetupColumn("Three");
5926
ImGui::TableHeadersRow();
5927
for (int row = 0; row < 6; row++)
5928
{
5929
ImGui::TableNextRow();
5930
for (int column = 0; column < 3; column++)
5931
{
5932
ImGui::TableSetColumnIndex(column);
5933
ImGui::Text("Fixed %d,%d", column, row);
5934
}
5935
}
5936
ImGui::EndTable();
5937
}
5938
ImGui::TreePop();
5939
}
5940
5941
if (open_action != -1)
5942
ImGui::SetNextItemOpen(open_action != 0);
5943
IMGUI_DEMO_MARKER("Tables/Padding");
5944
if (ImGui::TreeNode("Padding"))
5945
{
5946
// First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.
5947
// We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.
5948
HelpMarker(
5949
"We often want outer padding activated when any using features which makes the edges of a column visible:\n"
5950
"e.g.:\n"
5951
"- BorderOuterV\n"
5952
"- any form of row selection\n"
5953
"Because of this, activating BorderOuterV sets the default to PadOuterX. "
5954
"Using PadOuterX or NoPadOuterX you can override the default.\n\n"
5955
"Actual padding values are using style.CellPadding.\n\n"
5956
"In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.");
5957
5958
static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;
5959
PushStyleCompact();
5960
ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX);
5961
ImGui::SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)");
5962
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX);
5963
ImGui::SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)");
5964
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX);
5965
ImGui::SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)");
5966
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV);
5967
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV);
5968
static bool show_headers = false;
5969
ImGui::Checkbox("show_headers", &show_headers);
5970
PopStyleCompact();
5971
5972
if (ImGui::BeginTable("table_padding", 3, flags1))
5973
{
5974
if (show_headers)
5975
{
5976
ImGui::TableSetupColumn("One");
5977
ImGui::TableSetupColumn("Two");
5978
ImGui::TableSetupColumn("Three");
5979
ImGui::TableHeadersRow();
5980
}
5981
5982
for (int row = 0; row < 5; row++)
5983
{
5984
ImGui::TableNextRow();
5985
for (int column = 0; column < 3; column++)
5986
{
5987
ImGui::TableSetColumnIndex(column);
5988
if (row == 0)
5989
{
5990
ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
5991
}
5992
else
5993
{
5994
char buf[32];
5995
sprintf(buf, "Hello %d,%d", column, row);
5996
ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
5997
}
5998
//if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)
5999
// ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));
6000
}
6001
}
6002
ImGui::EndTable();
6003
}
6004
6005
// Second example: set style.CellPadding to (0.0) or a custom value.
6006
// FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...
6007
HelpMarker("Setting style.CellPadding to (0,0) or a custom value.");
6008
static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
6009
static ImVec2 cell_padding(0.0f, 0.0f);
6010
static bool show_widget_frame_bg = true;
6011
6012
PushStyleCompact();
6013
ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders);
6014
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH);
6015
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV);
6016
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner);
6017
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter);
6018
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg);
6019
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable);
6020
ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg);
6021
ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f");
6022
PopStyleCompact();
6023
6024
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);
6025
if (ImGui::BeginTable("table_padding_2", 3, flags2))
6026
{
6027
static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells
6028
static bool init = true;
6029
if (!show_widget_frame_bg)
6030
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
6031
for (int cell = 0; cell < 3 * 5; cell++)
6032
{
6033
ImGui::TableNextColumn();
6034
if (init)
6035
strcpy(text_bufs[cell], "edit me");
6036
ImGui::SetNextItemWidth(-FLT_MIN);
6037
ImGui::PushID(cell);
6038
ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell]));
6039
ImGui::PopID();
6040
}
6041
if (!show_widget_frame_bg)
6042
ImGui::PopStyleColor();
6043
init = false;
6044
ImGui::EndTable();
6045
}
6046
ImGui::PopStyleVar();
6047
6048
ImGui::TreePop();
6049
}
6050
6051
if (open_action != -1)
6052
ImGui::SetNextItemOpen(open_action != 0);
6053
IMGUI_DEMO_MARKER("Tables/Explicit widths");
6054
if (ImGui::TreeNode("Sizing policies"))
6055
{
6056
static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6057
PushStyleCompact();
6058
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
6059
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags1, ImGuiTableFlags_NoHostExtendX);
6060
PopStyleCompact();
6061
6062
static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame };
6063
for (int table_n = 0; table_n < 4; table_n++)
6064
{
6065
ImGui::PushID(table_n);
6066
ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);
6067
EditTableSizingFlags(&sizing_policy_flags[table_n]);
6068
6069
// To make it easier to understand the different sizing policy,
6070
// For each policy: we display one table where the columns have equal contents width,
6071
// and one where the columns have different contents width.
6072
if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1))
6073
{
6074
for (int row = 0; row < 3; row++)
6075
{
6076
ImGui::TableNextRow();
6077
ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6078
ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6079
ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6080
}
6081
ImGui::EndTable();
6082
}
6083
if (ImGui::BeginTable("table2", 3, sizing_policy_flags[table_n] | flags1))
6084
{
6085
for (int row = 0; row < 3; row++)
6086
{
6087
ImGui::TableNextRow();
6088
ImGui::TableNextColumn(); ImGui::Text("AAAA");
6089
ImGui::TableNextColumn(); ImGui::Text("BBBBBBBB");
6090
ImGui::TableNextColumn(); ImGui::Text("CCCCCCCCCCCC");
6091
}
6092
ImGui::EndTable();
6093
}
6094
ImGui::PopID();
6095
}
6096
6097
ImGui::Spacing();
6098
ImGui::TextUnformatted("Advanced");
6099
ImGui::SameLine();
6100
HelpMarker(
6101
"This section allows you to interact and see the effect of various sizing policies "
6102
"depending on whether Scroll is enabled and the contents of your columns.");
6103
6104
enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };
6105
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
6106
static int contents_type = CT_ShowWidth;
6107
static int column_count = 3;
6108
6109
PushStyleCompact();
6110
ImGui::PushID("Advanced");
6111
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
6112
EditTableSizingFlags(&flags);
6113
ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0");
6114
if (contents_type == CT_FillButton)
6115
{
6116
ImGui::SameLine();
6117
HelpMarker(
6118
"Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop "
6119
"where contents width can feed into auto-column width can feed into contents width.");
6120
}
6121
ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp);
6122
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6123
ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
6124
ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.");
6125
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
6126
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6127
ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
6128
ImGui::PopItemWidth();
6129
ImGui::PopID();
6130
PopStyleCompact();
6131
6132
if (ImGui::BeginTable("table2", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))
6133
{
6134
for (int cell = 0; cell < 10 * column_count; cell++)
6135
{
6136
ImGui::TableNextColumn();
6137
int column = ImGui::TableGetColumnIndex();
6138
int row = ImGui::TableGetRowIndex();
6139
6140
ImGui::PushID(cell);
6141
char label[32];
6142
static char text_buf[32] = "";
6143
sprintf(label, "Hello %d,%d", column, row);
6144
switch (contents_type)
6145
{
6146
case CT_ShortText: ImGui::TextUnformatted(label); break;
6147
case CT_LongText: ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break;
6148
case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break;
6149
case CT_Button: ImGui::Button(label); break;
6150
case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break;
6151
case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break;
6152
}
6153
ImGui::PopID();
6154
}
6155
ImGui::EndTable();
6156
}
6157
ImGui::TreePop();
6158
}
6159
6160
if (open_action != -1)
6161
ImGui::SetNextItemOpen(open_action != 0);
6162
IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping");
6163
if (ImGui::TreeNode("Vertical scrolling, with clipping"))
6164
{
6165
HelpMarker(
6166
"Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n"
6167
"We also demonstrate using ImGuiListClipper to virtualize the submission of many items.");
6168
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6169
6170
PushStyleCompact();
6171
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6172
PopStyleCompact();
6173
6174
// When using ScrollX or ScrollY we need to specify a size for our table container!
6175
// Otherwise by default the table will fit all available space, like a BeginChild() call.
6176
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6177
if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size))
6178
{
6179
ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
6180
ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None);
6181
ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None);
6182
ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None);
6183
ImGui::TableHeadersRow();
6184
6185
// Demonstrate using clipper for large vertical lists
6186
ImGuiListClipper clipper;
6187
clipper.Begin(1000);
6188
while (clipper.Step())
6189
{
6190
for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)
6191
{
6192
ImGui::TableNextRow();
6193
for (int column = 0; column < 3; column++)
6194
{
6195
ImGui::TableSetColumnIndex(column);
6196
ImGui::Text("Hello %d,%d", column, row);
6197
}
6198
}
6199
}
6200
ImGui::EndTable();
6201
}
6202
ImGui::TreePop();
6203
}
6204
6205
if (open_action != -1)
6206
ImGui::SetNextItemOpen(open_action != 0);
6207
IMGUI_DEMO_MARKER("Tables/Horizontal scrolling");
6208
if (ImGui::TreeNode("Horizontal scrolling"))
6209
{
6210
HelpMarker(
6211
"When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, "
6212
"as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n"
6213
"Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, "
6214
"because the container window won't automatically extend vertically to fix contents "
6215
"(this may be improved in future versions).");
6216
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6217
static int freeze_cols = 1;
6218
static int freeze_rows = 1;
6219
6220
PushStyleCompact();
6221
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6222
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
6223
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6224
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6225
ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
6226
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6227
ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
6228
PopStyleCompact();
6229
6230
// When using ScrollX or ScrollY we need to specify a size for our table container!
6231
// Otherwise by default the table will fit all available space, like a BeginChild() call.
6232
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6233
if (ImGui::BeginTable("table_scrollx", 7, flags, outer_size))
6234
{
6235
ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
6236
ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()
6237
ImGui::TableSetupColumn("One");
6238
ImGui::TableSetupColumn("Two");
6239
ImGui::TableSetupColumn("Three");
6240
ImGui::TableSetupColumn("Four");
6241
ImGui::TableSetupColumn("Five");
6242
ImGui::TableSetupColumn("Six");
6243
ImGui::TableHeadersRow();
6244
for (int row = 0; row < 20; row++)
6245
{
6246
ImGui::TableNextRow();
6247
for (int column = 0; column < 7; column++)
6248
{
6249
// Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.
6250
// Because here we know that:
6251
// - A) all our columns are contributing the same to row height
6252
// - B) column 0 is always visible,
6253
// We only always submit this one column and can skip others.
6254
// More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().
6255
if (!ImGui::TableSetColumnIndex(column) && column > 0)
6256
continue;
6257
if (column == 0)
6258
ImGui::Text("Line %d", row);
6259
else
6260
ImGui::Text("Hello world %d,%d", column, row);
6261
}
6262
}
6263
ImGui::EndTable();
6264
}
6265
6266
ImGui::Spacing();
6267
ImGui::TextUnformatted("Stretch + ScrollX");
6268
ImGui::SameLine();
6269
HelpMarker(
6270
"Showcase using Stretch columns + ScrollX together: "
6271
"this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n"
6272
"Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns "
6273
"along with ScrollX doesn't make sense.");
6274
static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6275
static float inner_width = 1000.0f;
6276
PushStyleCompact();
6277
ImGui::PushID("flags3");
6278
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
6279
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX);
6280
ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f");
6281
ImGui::PopItemWidth();
6282
ImGui::PopID();
6283
PopStyleCompact();
6284
if (ImGui::BeginTable("table2", 7, flags2, outer_size, inner_width))
6285
{
6286
for (int cell = 0; cell < 20 * 7; cell++)
6287
{
6288
ImGui::TableNextColumn();
6289
ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());
6290
}
6291
ImGui::EndTable();
6292
}
6293
ImGui::TreePop();
6294
}
6295
6296
if (open_action != -1)
6297
ImGui::SetNextItemOpen(open_action != 0);
6298
IMGUI_DEMO_MARKER("Tables/Columns flags");
6299
if (ImGui::TreeNode("Columns flags"))
6300
{
6301
// Create a first table just to show all the options/flags we want to make visible in our example!
6302
const int column_count = 3;
6303
const char* column_names[column_count] = { "One", "Two", "Three" };
6304
static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide };
6305
static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags()
6306
6307
if (ImGui::BeginTable("table_columns_flags_checkboxes", column_count, ImGuiTableFlags_None))
6308
{
6309
PushStyleCompact();
6310
for (int column = 0; column < column_count; column++)
6311
{
6312
ImGui::TableNextColumn();
6313
ImGui::PushID(column);
6314
ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns
6315
ImGui::Text("'%s'", column_names[column]);
6316
ImGui::Spacing();
6317
ImGui::Text("Input flags:");
6318
EditTableColumnsFlags(&column_flags[column]);
6319
ImGui::Spacing();
6320
ImGui::Text("Output flags:");
6321
ImGui::BeginDisabled();
6322
ShowTableColumnsStatusFlags(column_flags_out[column]);
6323
ImGui::EndDisabled();
6324
ImGui::PopID();
6325
}
6326
PopStyleCompact();
6327
ImGui::EndTable();
6328
}
6329
6330
// Create the real table we care about for the example!
6331
// We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above,
6332
// otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible
6333
// + resizing the parent window down).
6334
const ImGuiTableFlags flags
6335
= ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
6336
| ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV
6337
| ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;
6338
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);
6339
if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size))
6340
{
6341
bool has_angled_header = false;
6342
for (int column = 0; column < column_count; column++)
6343
{
6344
has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;
6345
ImGui::TableSetupColumn(column_names[column], column_flags[column]);
6346
}
6347
if (has_angled_header)
6348
ImGui::TableAngledHeadersRow();
6349
ImGui::TableHeadersRow();
6350
for (int column = 0; column < column_count; column++)
6351
column_flags_out[column] = ImGui::TableGetColumnFlags(column);
6352
float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);
6353
for (int row = 0; row < 8; row++)
6354
{
6355
// Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.
6356
ImGui::Indent(indent_step);
6357
ImGui::TableNextRow();
6358
for (int column = 0; column < column_count; column++)
6359
{
6360
ImGui::TableSetColumnIndex(column);
6361
ImGui::Text("%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column));
6362
}
6363
}
6364
ImGui::Unindent(indent_step * 8.0f);
6365
6366
ImGui::EndTable();
6367
}
6368
ImGui::TreePop();
6369
}
6370
6371
if (open_action != -1)
6372
ImGui::SetNextItemOpen(open_action != 0);
6373
IMGUI_DEMO_MARKER("Tables/Columns widths");
6374
if (ImGui::TreeNode("Columns widths"))
6375
{
6376
HelpMarker("Using TableSetupColumn() to setup default width.");
6377
6378
static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;
6379
PushStyleCompact();
6380
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
6381
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize);
6382
PopStyleCompact();
6383
if (ImGui::BeginTable("table1", 3, flags1))
6384
{
6385
// We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.
6386
ImGui::TableSetupColumn("one", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f
6387
ImGui::TableSetupColumn("two", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f
6388
ImGui::TableSetupColumn("three", ImGuiTableColumnFlags_WidthFixed); // Default to auto
6389
ImGui::TableHeadersRow();
6390
for (int row = 0; row < 4; row++)
6391
{
6392
ImGui::TableNextRow();
6393
for (int column = 0; column < 3; column++)
6394
{
6395
ImGui::TableSetColumnIndex(column);
6396
if (row == 0)
6397
ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6398
else
6399
ImGui::Text("Hello %d,%d", column, row);
6400
}
6401
}
6402
ImGui::EndTable();
6403
}
6404
6405
HelpMarker(
6406
"Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, "
6407
"fixed columns with set width may still be shrunk down if there's not enough space in the host.");
6408
6409
static ImGuiTableFlags flags2 = ImGuiTableFlags_None;
6410
PushStyleCompact();
6411
ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags2, ImGuiTableFlags_NoKeepColumnsVisible);
6412
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags2, ImGuiTableFlags_BordersInnerV);
6413
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags2, ImGuiTableFlags_BordersOuterV);
6414
PopStyleCompact();
6415
if (ImGui::BeginTable("table2", 4, flags2))
6416
{
6417
// We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns
6418
// will default to ImGuiTableColumnFlags_WidthFixed.
6419
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f);
6420
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
6421
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f);
6422
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
6423
for (int row = 0; row < 5; row++)
6424
{
6425
ImGui::TableNextRow();
6426
for (int column = 0; column < 4; column++)
6427
{
6428
ImGui::TableSetColumnIndex(column);
6429
if (row == 0)
6430
ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6431
else
6432
ImGui::Text("Hello %d,%d", column, row);
6433
}
6434
}
6435
ImGui::EndTable();
6436
}
6437
ImGui::TreePop();
6438
}
6439
6440
if (open_action != -1)
6441
ImGui::SetNextItemOpen(open_action != 0);
6442
IMGUI_DEMO_MARKER("Tables/Nested tables");
6443
if (ImGui::TreeNode("Nested tables"))
6444
{
6445
HelpMarker("This demonstrates embedding a table into another table cell.");
6446
6447
if (ImGui::BeginTable("table_nested1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6448
{
6449
ImGui::TableSetupColumn("A0");
6450
ImGui::TableSetupColumn("A1");
6451
ImGui::TableHeadersRow();
6452
6453
ImGui::TableNextColumn();
6454
ImGui::Text("A0 Row 0");
6455
{
6456
float rows_height = TEXT_BASE_HEIGHT * 2;
6457
if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6458
{
6459
ImGui::TableSetupColumn("B0");
6460
ImGui::TableSetupColumn("B1");
6461
ImGui::TableHeadersRow();
6462
6463
ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
6464
ImGui::TableNextColumn();
6465
ImGui::Text("B0 Row 0");
6466
ImGui::TableNextColumn();
6467
ImGui::Text("B1 Row 0");
6468
ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
6469
ImGui::TableNextColumn();
6470
ImGui::Text("B0 Row 1");
6471
ImGui::TableNextColumn();
6472
ImGui::Text("B1 Row 1");
6473
6474
ImGui::EndTable();
6475
}
6476
}
6477
ImGui::TableNextColumn(); ImGui::Text("A1 Row 0");
6478
ImGui::TableNextColumn(); ImGui::Text("A0 Row 1");
6479
ImGui::TableNextColumn(); ImGui::Text("A1 Row 1");
6480
ImGui::EndTable();
6481
}
6482
ImGui::TreePop();
6483
}
6484
6485
if (open_action != -1)
6486
ImGui::SetNextItemOpen(open_action != 0);
6487
IMGUI_DEMO_MARKER("Tables/Row height");
6488
if (ImGui::TreeNode("Row height"))
6489
{
6490
HelpMarker(
6491
"You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, "
6492
"so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n"
6493
"We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.");
6494
if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_Borders))
6495
{
6496
for (int row = 0; row < 8; row++)
6497
{
6498
float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row);
6499
ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);
6500
ImGui::TableNextColumn();
6501
ImGui::Text("min_row_height = %.2f", min_row_height);
6502
}
6503
ImGui::EndTable();
6504
}
6505
6506
HelpMarker(
6507
"Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n"
6508
"Please note that Tables Row Height is not the same thing as Current Line Height, "
6509
"as a table cell may contains multiple lines.");
6510
if (ImGui::BeginTable("table_share_lineheight", 2, ImGuiTableFlags_Borders))
6511
{
6512
ImGui::TableNextRow();
6513
ImGui::TableNextColumn();
6514
ImGui::ColorButton("##1", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));
6515
ImGui::TableNextColumn();
6516
ImGui::Text("Line 1");
6517
ImGui::Text("Line 2");
6518
6519
ImGui::TableNextRow();
6520
ImGui::TableNextColumn();
6521
ImGui::ColorButton("##2", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));
6522
ImGui::TableNextColumn();
6523
ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column
6524
ImGui::Text("Line 1, with SameLine(0,0)");
6525
ImGui::Text("Line 2");
6526
6527
ImGui::EndTable();
6528
}
6529
6530
HelpMarker("Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.");
6531
if (ImGui::BeginTable("table_changing_cellpadding_y", 1, ImGuiTableFlags_Borders))
6532
{
6533
ImGuiStyle& style = ImGui::GetStyle();
6534
for (int row = 0; row < 8; row++)
6535
{
6536
if ((row % 3) == 2)
6537
ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding, 20.0f);
6538
ImGui::TableNextRow(ImGuiTableRowFlags_None);
6539
ImGui::TableNextColumn();
6540
ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y);
6541
if ((row % 3) == 2)
6542
ImGui::PopStyleVar();
6543
}
6544
ImGui::EndTable();
6545
}
6546
6547
ImGui::TreePop();
6548
}
6549
6550
if (open_action != -1)
6551
ImGui::SetNextItemOpen(open_action != 0);
6552
IMGUI_DEMO_MARKER("Tables/Outer size");
6553
if (ImGui::TreeNode("Outer size"))
6554
{
6555
// Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY
6556
// Important to that note how the two flags have slightly different behaviors!
6557
ImGui::Text("Using NoHostExtendX and NoHostExtendY:");
6558
PushStyleCompact();
6559
static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
6560
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
6561
ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.");
6562
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
6563
ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.");
6564
PopStyleCompact();
6565
6566
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
6567
if (ImGui::BeginTable("table1", 3, flags, outer_size))
6568
{
6569
for (int row = 0; row < 10; row++)
6570
{
6571
ImGui::TableNextRow();
6572
for (int column = 0; column < 3; column++)
6573
{
6574
ImGui::TableNextColumn();
6575
ImGui::Text("Cell %d,%d", column, row);
6576
}
6577
}
6578
ImGui::EndTable();
6579
}
6580
ImGui::SameLine();
6581
ImGui::Text("Hello!");
6582
6583
ImGui::Spacing();
6584
6585
ImGui::Text("Using explicit size:");
6586
if (ImGui::BeginTable("table2", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6587
{
6588
for (int row = 0; row < 5; row++)
6589
{
6590
ImGui::TableNextRow();
6591
for (int column = 0; column < 3; column++)
6592
{
6593
ImGui::TableNextColumn();
6594
ImGui::Text("Cell %d,%d", column, row);
6595
}
6596
}
6597
ImGui::EndTable();
6598
}
6599
ImGui::SameLine();
6600
if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6601
{
6602
for (int row = 0; row < 3; row++)
6603
{
6604
ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f);
6605
for (int column = 0; column < 3; column++)
6606
{
6607
ImGui::TableNextColumn();
6608
ImGui::Text("Cell %d,%d", column, row);
6609
}
6610
}
6611
ImGui::EndTable();
6612
}
6613
6614
ImGui::TreePop();
6615
}
6616
6617
if (open_action != -1)
6618
ImGui::SetNextItemOpen(open_action != 0);
6619
IMGUI_DEMO_MARKER("Tables/Background color");
6620
if (ImGui::TreeNode("Background color"))
6621
{
6622
static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;
6623
static int row_bg_type = 1;
6624
static int row_bg_target = 1;
6625
static int cell_bg_type = 1;
6626
6627
PushStyleCompact();
6628
ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
6629
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
6630
ImGui::SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.");
6631
ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0");
6632
ImGui::Combo("row bg target", (int*)&row_bg_target, "RowBg0\0RowBg1\0"); ImGui::SameLine(); HelpMarker("Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them.");
6633
ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0"); ImGui::SameLine(); HelpMarker("We are colorizing cells to B1->C2 here.");
6634
IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);
6635
IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);
6636
IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);
6637
PopStyleCompact();
6638
6639
if (ImGui::BeginTable("table1", 5, flags))
6640
{
6641
for (int row = 0; row < 6; row++)
6642
{
6643
ImGui::TableNextRow();
6644
6645
// Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'
6646
// We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag.
6647
if (row_bg_type != 0)
6648
{
6649
ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient?
6650
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color);
6651
}
6652
6653
// Fill cells
6654
for (int column = 0; column < 5; column++)
6655
{
6656
ImGui::TableSetColumnIndex(column);
6657
ImGui::Text("%c%c", 'A' + row, '0' + column);
6658
6659
// Change background of Cells B1->C2
6660
// Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'
6661
// (the CellBg color will be blended over the RowBg and ColumnBg colors)
6662
// We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.
6663
if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)
6664
{
6665
ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f));
6666
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);
6667
}
6668
}
6669
}
6670
ImGui::EndTable();
6671
}
6672
ImGui::TreePop();
6673
}
6674
6675
if (open_action != -1)
6676
ImGui::SetNextItemOpen(open_action != 0);
6677
IMGUI_DEMO_MARKER("Tables/Tree view");
6678
if (ImGui::TreeNode("Tree view"))
6679
{
6680
static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
6681
6682
static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull;
6683
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanFullWidth);
6684
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanLabelWidth);
6685
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanAllColumns);
6686
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_LabelSpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_LabelSpanAllColumns);
6687
ImGui::SameLine(); HelpMarker("Useful if you know that you aren't displaying contents in other columns");
6688
6689
HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns.");
6690
if (ImGui::BeginTable("3ways", 3, table_flags))
6691
{
6692
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
6693
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide);
6694
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
6695
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f);
6696
ImGui::TableHeadersRow();
6697
6698
// Simple storage to output a dummy file-system.
6699
struct MyTreeNode
6700
{
6701
const char* Name;
6702
const char* Type;
6703
int Size;
6704
int ChildIdx;
6705
int ChildCount;
6706
static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
6707
{
6708
ImGui::TableNextRow();
6709
ImGui::TableNextColumn();
6710
const bool is_folder = (node->ChildCount > 0);
6711
6712
ImGuiTreeNodeFlags node_flags = tree_node_flags_base;
6713
if (node != &all_nodes[0])
6714
node_flags &= ~ImGuiTreeNodeFlags_LabelSpanAllColumns; // Only demonstrate this on the root node.
6715
6716
if (is_folder)
6717
{
6718
bool open = ImGui::TreeNodeEx(node->Name, node_flags);
6719
if ((node_flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) == 0)
6720
{
6721
ImGui::TableNextColumn();
6722
ImGui::TextDisabled("--");
6723
ImGui::TableNextColumn();
6724
ImGui::TextUnformatted(node->Type);
6725
}
6726
if (open)
6727
{
6728
for (int child_n = 0; child_n < node->ChildCount; child_n++)
6729
DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes);
6730
ImGui::TreePop();
6731
}
6732
}
6733
else
6734
{
6735
ImGui::TreeNodeEx(node->Name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen);
6736
ImGui::TableNextColumn();
6737
ImGui::Text("%d", node->Size);
6738
ImGui::TableNextColumn();
6739
ImGui::TextUnformatted(node->Type);
6740
}
6741
}
6742
};
6743
static const MyTreeNode nodes[] =
6744
{
6745
{ "Root with Long Name", "Folder", -1, 1, 3 }, // 0
6746
{ "Music", "Folder", -1, 4, 2 }, // 1
6747
{ "Textures", "Folder", -1, 6, 3 }, // 2
6748
{ "desktop.ini", "System file", 1024, -1,-1 }, // 3
6749
{ "File1_a.wav", "Audio file", 123000, -1,-1 }, // 4
6750
{ "File1_b.wav", "Audio file", 456000, -1,-1 }, // 5
6751
{ "Image001.png", "Image file", 203128, -1,-1 }, // 6
6752
{ "Copy of Image001.png", "Image file", 203256, -1,-1 }, // 7
6753
{ "Copy of Image001 (Final2).png","Image file", 203512, -1,-1 }, // 8
6754
};
6755
6756
MyTreeNode::DisplayNode(&nodes[0], nodes);
6757
6758
ImGui::EndTable();
6759
}
6760
ImGui::TreePop();
6761
}
6762
6763
if (open_action != -1)
6764
ImGui::SetNextItemOpen(open_action != 0);
6765
IMGUI_DEMO_MARKER("Tables/Item width");
6766
if (ImGui::TreeNode("Item width"))
6767
{
6768
HelpMarker(
6769
"Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n"
6770
"Note that on auto-resizing non-resizable fixed columns, querying the content width for "
6771
"e.g. right-alignment doesn't make sense.");
6772
if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders))
6773
{
6774
ImGui::TableSetupColumn("small");
6775
ImGui::TableSetupColumn("half");
6776
ImGui::TableSetupColumn("right-align");
6777
ImGui::TableHeadersRow();
6778
6779
for (int row = 0; row < 3; row++)
6780
{
6781
ImGui::TableNextRow();
6782
if (row == 0)
6783
{
6784
// Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)
6785
ImGui::TableSetColumnIndex(0);
6786
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small
6787
ImGui::TableSetColumnIndex(1);
6788
ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
6789
ImGui::TableSetColumnIndex(2);
6790
ImGui::PushItemWidth(-FLT_MIN); // Right-aligned
6791
}
6792
6793
// Draw our contents
6794
static float dummy_f = 0.0f;
6795
ImGui::PushID(row);
6796
ImGui::TableSetColumnIndex(0);
6797
ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f);
6798
ImGui::TableSetColumnIndex(1);
6799
ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f);
6800
ImGui::TableSetColumnIndex(2);
6801
ImGui::SliderFloat("##float2", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned
6802
ImGui::PopID();
6803
}
6804
ImGui::EndTable();
6805
}
6806
ImGui::TreePop();
6807
}
6808
6809
// Demonstrate using TableHeader() calls instead of TableHeadersRow()
6810
if (open_action != -1)
6811
ImGui::SetNextItemOpen(open_action != 0);
6812
IMGUI_DEMO_MARKER("Tables/Custom headers");
6813
if (ImGui::TreeNode("Custom headers"))
6814
{
6815
const int COLUMNS_COUNT = 3;
6816
if (ImGui::BeginTable("table_custom_headers", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6817
{
6818
ImGui::TableSetupColumn("Apricot");
6819
ImGui::TableSetupColumn("Banana");
6820
ImGui::TableSetupColumn("Cherry");
6821
6822
// Dummy entire-column selection storage
6823
// FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
6824
static bool column_selected[3] = {};
6825
6826
// Instead of calling TableHeadersRow() we'll submit custom headers ourselves.
6827
// (A different approach is also possible:
6828
// - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.
6829
// - Call TableHeadersRow() normally. This will submit TableHeader() with no name.
6830
// - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)
6831
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
6832
for (int column = 0; column < COLUMNS_COUNT; column++)
6833
{
6834
ImGui::TableSetColumnIndex(column);
6835
const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
6836
ImGui::PushID(column);
6837
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
6838
ImGui::Checkbox("##checkall", &column_selected[column]);
6839
ImGui::PopStyleVar();
6840
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
6841
ImGui::TableHeader(column_name);
6842
ImGui::PopID();
6843
}
6844
6845
// Submit table contents
6846
for (int row = 0; row < 5; row++)
6847
{
6848
ImGui::TableNextRow();
6849
for (int column = 0; column < 3; column++)
6850
{
6851
char buf[32];
6852
sprintf(buf, "Cell %d,%d", column, row);
6853
ImGui::TableSetColumnIndex(column);
6854
ImGui::Selectable(buf, column_selected[column]);
6855
}
6856
}
6857
ImGui::EndTable();
6858
}
6859
ImGui::TreePop();
6860
}
6861
6862
// Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers
6863
if (open_action != -1)
6864
ImGui::SetNextItemOpen(open_action != 0);
6865
IMGUI_DEMO_MARKER("Tables/Angled headers");
6866
if (ImGui::TreeNode("Angled headers"))
6867
{
6868
const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" };
6869
const int columns_count = IM_ARRAYSIZE(column_names);
6870
const int rows_count = 12;
6871
6872
static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;
6873
static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed;
6874
static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage
6875
static int frozen_cols = 1;
6876
static int frozen_rows = 2;
6877
ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
6878
ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
6879
ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable);
6880
ImGui::CheckboxFlags("_Sortable", &table_flags, ImGuiTableFlags_Sortable);
6881
ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody);
6882
ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn);
6883
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6884
ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2);
6885
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6886
ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2);
6887
ImGui::CheckboxFlags("Disable header contributing to column width", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth);
6888
6889
if (ImGui::TreeNode("Style settings"))
6890
{
6891
ImGui::SameLine();
6892
HelpMarker("Giving access to some ImGuiStyle value in this demo for convenience.");
6893
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6894
ImGui::SliderAngle("style.TableAngledHeadersAngle", &ImGui::GetStyle().TableAngledHeadersAngle, -50.0f, +50.0f);
6895
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6896
ImGui::SliderFloat2("style.TableAngledHeadersTextAlign", (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
6897
ImGui::TreePop();
6898
}
6899
6900
if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))
6901
{
6902
ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
6903
for (int n = 1; n < columns_count; n++)
6904
ImGui::TableSetupColumn(column_names[n], column_flags);
6905
ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);
6906
6907
ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.
6908
ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions.
6909
for (int row = 0; row < rows_count; row++)
6910
{
6911
ImGui::PushID(row);
6912
ImGui::TableNextRow();
6913
ImGui::TableSetColumnIndex(0);
6914
ImGui::AlignTextToFramePadding();
6915
ImGui::Text("Track %d", row);
6916
for (int column = 1; column < columns_count; column++)
6917
if (ImGui::TableSetColumnIndex(column))
6918
{
6919
ImGui::PushID(column);
6920
ImGui::Checkbox("", &bools[row * columns_count + column]);
6921
ImGui::PopID();
6922
}
6923
ImGui::PopID();
6924
}
6925
ImGui::EndTable();
6926
}
6927
ImGui::TreePop();
6928
}
6929
6930
// Demonstrate creating custom context menus inside columns,
6931
// while playing it nice with context menus provided by TableHeadersRow()/TableHeader()
6932
if (open_action != -1)
6933
ImGui::SetNextItemOpen(open_action != 0);
6934
IMGUI_DEMO_MARKER("Tables/Context menus");
6935
if (ImGui::TreeNode("Context menus"))
6936
{
6937
HelpMarker(
6938
"By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n"
6939
"Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.");
6940
static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody;
6941
6942
PushStyleCompact();
6943
ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags1, ImGuiTableFlags_ContextMenuInBody);
6944
PopStyleCompact();
6945
6946
// Context Menus: first example
6947
// [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.
6948
// [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)
6949
const int COLUMNS_COUNT = 3;
6950
if (ImGui::BeginTable("table_context_menu", COLUMNS_COUNT, flags1))
6951
{
6952
ImGui::TableSetupColumn("One");
6953
ImGui::TableSetupColumn("Two");
6954
ImGui::TableSetupColumn("Three");
6955
6956
// [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.
6957
ImGui::TableHeadersRow();
6958
6959
// Submit dummy contents
6960
for (int row = 0; row < 4; row++)
6961
{
6962
ImGui::TableNextRow();
6963
for (int column = 0; column < COLUMNS_COUNT; column++)
6964
{
6965
ImGui::TableSetColumnIndex(column);
6966
ImGui::Text("Cell %d,%d", column, row);
6967
}
6968
}
6969
ImGui::EndTable();
6970
}
6971
6972
// Context Menus: second example
6973
// [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
6974
// [2.2] Right-click on the ".." to open a custom popup
6975
// [2.3] Right-click in columns to open another custom popup
6976
HelpMarker(
6977
"Demonstrate mixing table context menu (over header), item context button (over button) "
6978
"and custom per-colunm context menu (over column body).");
6979
ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
6980
if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2))
6981
{
6982
ImGui::TableSetupColumn("One");
6983
ImGui::TableSetupColumn("Two");
6984
ImGui::TableSetupColumn("Three");
6985
6986
// [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
6987
ImGui::TableHeadersRow();
6988
for (int row = 0; row < 4; row++)
6989
{
6990
ImGui::TableNextRow();
6991
for (int column = 0; column < COLUMNS_COUNT; column++)
6992
{
6993
// Submit dummy contents
6994
ImGui::TableSetColumnIndex(column);
6995
ImGui::Text("Cell %d,%d", column, row);
6996
ImGui::SameLine();
6997
6998
// [2.2] Right-click on the ".." to open a custom popup
6999
ImGui::PushID(row * COLUMNS_COUNT + column);
7000
ImGui::SmallButton("..");
7001
if (ImGui::BeginPopupContextItem())
7002
{
7003
ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row);
7004
if (ImGui::Button("Close"))
7005
ImGui::CloseCurrentPopup();
7006
ImGui::EndPopup();
7007
}
7008
ImGui::PopID();
7009
}
7010
}
7011
7012
// [2.3] Right-click anywhere in columns to open another custom popup
7013
// (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup
7014
// to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping)
7015
int hovered_column = -1;
7016
for (int column = 0; column < COLUMNS_COUNT + 1; column++)
7017
{
7018
ImGui::PushID(column);
7019
if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered)
7020
hovered_column = column;
7021
if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1))
7022
ImGui::OpenPopup("MyPopup");
7023
if (ImGui::BeginPopup("MyPopup"))
7024
{
7025
if (column == COLUMNS_COUNT)
7026
ImGui::Text("This is a custom popup for unused space after the last column.");
7027
else
7028
ImGui::Text("This is a custom popup for Column %d", column);
7029
if (ImGui::Button("Close"))
7030
ImGui::CloseCurrentPopup();
7031
ImGui::EndPopup();
7032
}
7033
ImGui::PopID();
7034
}
7035
7036
ImGui::EndTable();
7037
ImGui::Text("Hovered column: %d", hovered_column);
7038
}
7039
ImGui::TreePop();
7040
}
7041
7042
// Demonstrate creating multiple tables with the same ID
7043
if (open_action != -1)
7044
ImGui::SetNextItemOpen(open_action != 0);
7045
IMGUI_DEMO_MARKER("Tables/Synced instances");
7046
if (ImGui::TreeNode("Synced instances"))
7047
{
7048
HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc.");
7049
7050
static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;
7051
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
7052
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
7053
ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit);
7054
ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
7055
for (int n = 0; n < 3; n++)
7056
{
7057
char buf[32];
7058
sprintf(buf, "Synced Table %d", n);
7059
bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen);
7060
if (open && ImGui::BeginTable("Table", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5)))
7061
{
7062
ImGui::TableSetupColumn("One");
7063
ImGui::TableSetupColumn("Two");
7064
ImGui::TableSetupColumn("Three");
7065
ImGui::TableHeadersRow();
7066
const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions.
7067
for (int cell = 0; cell < cell_count; cell++)
7068
{
7069
ImGui::TableNextColumn();
7070
ImGui::Text("this cell %d", cell);
7071
}
7072
ImGui::EndTable();
7073
}
7074
}
7075
ImGui::TreePop();
7076
}
7077
7078
// Demonstrate using Sorting facilities
7079
// This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting.
7080
// Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified)
7081
static const char* template_items_names[] =
7082
{
7083
"Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango",
7084
"Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot"
7085
};
7086
if (open_action != -1)
7087
ImGui::SetNextItemOpen(open_action != 0);
7088
IMGUI_DEMO_MARKER("Tables/Sorting");
7089
if (ImGui::TreeNode("Sorting"))
7090
{
7091
// Create item list
7092
static ImVector<MyItem> items;
7093
if (items.Size == 0)
7094
{
7095
items.resize(50, MyItem());
7096
for (int n = 0; n < items.Size; n++)
7097
{
7098
const int template_n = n % IM_ARRAYSIZE(template_items_names);
7099
MyItem& item = items[n];
7100
item.ID = n;
7101
item.Name = template_items_names[template_n];
7102
item.Quantity = (n * n - n) % 20; // Assign default quantities
7103
}
7104
}
7105
7106
// Options
7107
static ImGuiTableFlags flags =
7108
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7109
| ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody
7110
| ImGuiTableFlags_ScrollY;
7111
PushStyleCompact();
7112
ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
7113
ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7114
ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
7115
ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7116
PopStyleCompact();
7117
7118
if (ImGui::BeginTable("table_sorting", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f))
7119
{
7120
// Declare columns
7121
// We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7122
// This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7123
// Demonstrate using a mixture of flags among available sort-related flags:
7124
// - ImGuiTableColumnFlags_DefaultSort
7125
// - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending
7126
// - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending
7127
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_ID);
7128
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
7129
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);
7130
ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity);
7131
ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible
7132
ImGui::TableHeadersRow();
7133
7134
// Sort our data if sort specs have been changed!
7135
if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
7136
if (sort_specs->SpecsDirty)
7137
{
7138
MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);
7139
sort_specs->SpecsDirty = false;
7140
}
7141
7142
// Demonstrate using clipper for large vertical lists
7143
ImGuiListClipper clipper;
7144
clipper.Begin(items.Size);
7145
while (clipper.Step())
7146
for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7147
{
7148
// Display a data item
7149
MyItem* item = &items[row_n];
7150
ImGui::PushID(item->ID);
7151
ImGui::TableNextRow();
7152
ImGui::TableNextColumn();
7153
ImGui::Text("%04d", item->ID);
7154
ImGui::TableNextColumn();
7155
ImGui::TextUnformatted(item->Name);
7156
ImGui::TableNextColumn();
7157
ImGui::SmallButton("None");
7158
ImGui::TableNextColumn();
7159
ImGui::Text("%d", item->Quantity);
7160
ImGui::PopID();
7161
}
7162
ImGui::EndTable();
7163
}
7164
ImGui::TreePop();
7165
}
7166
7167
// In this example we'll expose most table flags and settings.
7168
// For specific flags and settings refer to the corresponding section for more detailed explanation.
7169
// This section is mostly useful to experiment with combining certain flags or settings with each others.
7170
//ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]
7171
if (open_action != -1)
7172
ImGui::SetNextItemOpen(open_action != 0);
7173
IMGUI_DEMO_MARKER("Tables/Advanced");
7174
if (ImGui::TreeNode("Advanced"))
7175
{
7176
static ImGuiTableFlags flags =
7177
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable
7178
| ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7179
| ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody
7180
| ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
7181
| ImGuiTableFlags_SizingFixedFit;
7182
static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;
7183
7184
enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };
7185
static int contents_type = CT_SelectableSpanRow;
7186
const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" };
7187
static int freeze_cols = 1;
7188
static int freeze_rows = 1;
7189
static int items_count = IM_ARRAYSIZE(template_items_names) * 2;
7190
static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);
7191
static float row_min_height = 0.0f; // Auto
7192
static float inner_width_with_scroll = 0.0f; // Auto-extend
7193
static bool outer_size_enabled = true;
7194
static bool show_headers = true;
7195
static bool show_wrapped_text = false;
7196
//static ImGuiTextFilter filter;
7197
//ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing
7198
if (ImGui::TreeNode("Options"))
7199
{
7200
// Make the UI compact because there are so many fields
7201
PushStyleCompact();
7202
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f);
7203
7204
if (ImGui::TreeNodeEx("Features:", ImGuiTreeNodeFlags_DefaultOpen))
7205
{
7206
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
7207
ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
7208
ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
7209
ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", &flags, ImGuiTableFlags_Sortable);
7210
ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", &flags, ImGuiTableFlags_NoSavedSettings);
7211
ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags, ImGuiTableFlags_ContextMenuInBody);
7212
ImGui::TreePop();
7213
}
7214
7215
if (ImGui::TreeNodeEx("Decorations:", ImGuiTreeNodeFlags_DefaultOpen))
7216
{
7217
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
7218
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
7219
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
7220
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
7221
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
7222
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
7223
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
7224
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers");
7225
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
7226
ImGui::TreePop();
7227
}
7228
7229
if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen))
7230
{
7231
EditTableSizingFlags(&flags);
7232
ImGui::SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical.");
7233
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
7234
ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.");
7235
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
7236
ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.");
7237
ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible);
7238
ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled.");
7239
ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
7240
ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.");
7241
ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
7242
ImGui::SameLine(); HelpMarker("Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options.");
7243
ImGui::TreePop();
7244
}
7245
7246
if (ImGui::TreeNodeEx("Padding:", ImGuiTreeNodeFlags_DefaultOpen))
7247
{
7248
ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX);
7249
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX);
7250
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX);
7251
ImGui::TreePop();
7252
}
7253
7254
if (ImGui::TreeNodeEx("Scrolling:", ImGuiTreeNodeFlags_DefaultOpen))
7255
{
7256
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
7257
ImGui::SameLine();
7258
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7259
ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
7260
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
7261
ImGui::SameLine();
7262
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7263
ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
7264
ImGui::TreePop();
7265
}
7266
7267
if (ImGui::TreeNodeEx("Sorting:", ImGuiTreeNodeFlags_DefaultOpen))
7268
{
7269
ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
7270
ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7271
ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
7272
ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7273
ImGui::TreePop();
7274
}
7275
7276
if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen))
7277
{
7278
ImGui::Checkbox("show_headers", &show_headers);
7279
ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
7280
ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader);
7281
ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo).");
7282
ImGui::TreePop();
7283
}
7284
7285
if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen))
7286
{
7287
ImGui::Checkbox("show_wrapped_text", &show_wrapped_text);
7288
7289
ImGui::DragFloat2("##OuterSize", &outer_size_value.x);
7290
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
7291
ImGui::Checkbox("outer_size", &outer_size_enabled);
7292
ImGui::SameLine();
7293
HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n"
7294
"- The table is output directly in the parent window.\n"
7295
"- OuterSize.x < 0.0f will right-align the table.\n"
7296
"- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n"
7297
"- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).");
7298
7299
// From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.
7300
// To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.
7301
ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);
7302
7303
ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX);
7304
ImGui::SameLine(); HelpMarker("Specify height of the Selectable item.");
7305
7306
ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999);
7307
ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_ARRAYSIZE(contents_type_names));
7308
//filter.Draw("filter");
7309
ImGui::TreePop();
7310
}
7311
7312
ImGui::PopItemWidth();
7313
PopStyleCompact();
7314
ImGui::Spacing();
7315
ImGui::TreePop();
7316
}
7317
7318
// Update item list if we changed the number of items
7319
static ImVector<MyItem> items;
7320
static ImVector<int> selection;
7321
static bool items_need_sort = false;
7322
if (items.Size != items_count)
7323
{
7324
items.resize(items_count, MyItem());
7325
for (int n = 0; n < items_count; n++)
7326
{
7327
const int template_n = n % IM_ARRAYSIZE(template_items_names);
7328
MyItem& item = items[n];
7329
item.ID = n;
7330
item.Name = template_items_names[template_n];
7331
item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities
7332
}
7333
}
7334
7335
const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();
7336
const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;
7337
ImVec2 table_scroll_cur, table_scroll_max; // For debug display
7338
const ImDrawList* table_draw_list = NULL; // "
7339
7340
// Submit table
7341
const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;
7342
if (ImGui::BeginTable("table_advanced", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use))
7343
{
7344
// Declare columns
7345
// We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7346
// This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7347
ImGui::TableSetupColumn("ID", columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID);
7348
ImGui::TableSetupColumn("Name", columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
7349
ImGui::TableSetupColumn("Action", columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);
7350
ImGui::TableSetupColumn("Quantity", columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity);
7351
ImGui::TableSetupColumn("Description", columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description);
7352
ImGui::TableSetupColumn("Hidden", columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);
7353
ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
7354
7355
// Sort our data if sort specs have been changed!
7356
ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();
7357
if (sort_specs && sort_specs->SpecsDirty)
7358
items_need_sort = true;
7359
if (sort_specs && items_need_sort && items.Size > 1)
7360
{
7361
MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);
7362
sort_specs->SpecsDirty = false;
7363
}
7364
items_need_sort = false;
7365
7366
// Take note of whether we are currently sorting based on the Quantity field,
7367
// we will use this to trigger sorting when we know the data of this column has been modified.
7368
const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0;
7369
7370
// Show headers
7371
if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)
7372
ImGui::TableAngledHeadersRow();
7373
if (show_headers)
7374
ImGui::TableHeadersRow();
7375
7376
// Show data
7377
// FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?
7378
#if 1
7379
// Demonstrate using clipper for large vertical lists
7380
ImGuiListClipper clipper;
7381
clipper.Begin(items.Size);
7382
while (clipper.Step())
7383
{
7384
for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7385
#else
7386
// Without clipper
7387
{
7388
for (int row_n = 0; row_n < items.Size; row_n++)
7389
#endif
7390
{
7391
MyItem* item = &items[row_n];
7392
//if (!filter.PassFilter(item->Name))
7393
// continue;
7394
7395
const bool item_is_selected = selection.contains(item->ID);
7396
ImGui::PushID(item->ID);
7397
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height);
7398
7399
// For the demo purpose we can select among different type of items submitted in the first column
7400
ImGui::TableSetColumnIndex(0);
7401
char label[32];
7402
sprintf(label, "%04d", item->ID);
7403
if (contents_type == CT_Text)
7404
ImGui::TextUnformatted(label);
7405
else if (contents_type == CT_Button)
7406
ImGui::Button(label);
7407
else if (contents_type == CT_SmallButton)
7408
ImGui::SmallButton(label);
7409
else if (contents_type == CT_FillButton)
7410
ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));
7411
else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)
7412
{
7413
ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None;
7414
if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height)))
7415
{
7416
if (ImGui::GetIO().KeyCtrl)
7417
{
7418
if (item_is_selected)
7419
selection.find_erase_unsorted(item->ID);
7420
else
7421
selection.push_back(item->ID);
7422
}
7423
else
7424
{
7425
selection.clear();
7426
selection.push_back(item->ID);
7427
}
7428
}
7429
}
7430
7431
if (ImGui::TableSetColumnIndex(1))
7432
ImGui::TextUnformatted(item->Name);
7433
7434
// Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
7435
// and we are currently sorting on the column showing the Quantity.
7436
// To avoid triggering a sort while holding the button, we only trigger it when the button has been released.
7437
// You will probably need some extra logic if you want to automatically sort when a specific entry changes.
7438
if (ImGui::TableSetColumnIndex(2))
7439
{
7440
if (ImGui::SmallButton("Chop")) { item->Quantity += 1; }
7441
if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7442
ImGui::SameLine();
7443
if (ImGui::SmallButton("Eat")) { item->Quantity -= 1; }
7444
if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7445
}
7446
7447
if (ImGui::TableSetColumnIndex(3))
7448
ImGui::Text("%d", item->Quantity);
7449
7450
ImGui::TableSetColumnIndex(4);
7451
if (show_wrapped_text)
7452
ImGui::TextWrapped("Lorem ipsum dolor sit amet");
7453
else
7454
ImGui::Text("Lorem ipsum dolor sit amet");
7455
7456
if (ImGui::TableSetColumnIndex(5))
7457
ImGui::Text("1234");
7458
7459
ImGui::PopID();
7460
}
7461
}
7462
7463
// Store some info to display debug details below
7464
table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());
7465
table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());
7466
table_draw_list = ImGui::GetWindowDrawList();
7467
ImGui::EndTable();
7468
}
7469
static bool show_debug_details = false;
7470
ImGui::Checkbox("Debug details", &show_debug_details);
7471
if (show_debug_details && table_draw_list)
7472
{
7473
ImGui::SameLine(0.0f, 0.0f);
7474
const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;
7475
if (table_draw_list == parent_draw_list)
7476
ImGui::Text(": DrawCmd: +%d (in same window)",
7477
table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);
7478
else
7479
ImGui::Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)",
7480
table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y);
7481
}
7482
ImGui::TreePop();
7483
}
7484
7485
ImGui::PopID();
7486
7487
DemoWindowColumns();
7488
7489
if (disable_indent)
7490
ImGui::PopStyleVar();
7491
}
7492
7493
// Demonstrate old/legacy Columns API!
7494
// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]
7495
static void DemoWindowColumns()
7496
{
7497
IMGUI_DEMO_MARKER("Columns (legacy API)");
7498
bool open = ImGui::TreeNode("Legacy Columns API");
7499
ImGui::SameLine();
7500
HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!");
7501
if (!open)
7502
return;
7503
7504
// Basic columns
7505
IMGUI_DEMO_MARKER("Columns (legacy API)/Basic");
7506
if (ImGui::TreeNode("Basic"))
7507
{
7508
ImGui::Text("Without border:");
7509
ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border
7510
ImGui::Separator();
7511
for (int n = 0; n < 14; n++)
7512
{
7513
char label[32];
7514
sprintf(label, "Item %d", n);
7515
if (ImGui::Selectable(label)) {}
7516
//if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}
7517
ImGui::NextColumn();
7518
}
7519
ImGui::Columns(1);
7520
ImGui::Separator();
7521
7522
ImGui::Text("With border:");
7523
ImGui::Columns(4, "mycolumns"); // 4-ways, with border
7524
ImGui::Separator();
7525
ImGui::Text("ID"); ImGui::NextColumn();
7526
ImGui::Text("Name"); ImGui::NextColumn();
7527
ImGui::Text("Path"); ImGui::NextColumn();
7528
ImGui::Text("Hovered"); ImGui::NextColumn();
7529
ImGui::Separator();
7530
const char* names[3] = { "One", "Two", "Three" };
7531
const char* paths[3] = { "/path/one", "/path/two", "/path/three" };
7532
static int selected = -1;
7533
for (int i = 0; i < 3; i++)
7534
{
7535
char label[32];
7536
sprintf(label, "%04d", i);
7537
if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))
7538
selected = i;
7539
bool hovered = ImGui::IsItemHovered();
7540
ImGui::NextColumn();
7541
ImGui::Text(names[i]); ImGui::NextColumn();
7542
ImGui::Text(paths[i]); ImGui::NextColumn();
7543
ImGui::Text("%d", hovered); ImGui::NextColumn();
7544
}
7545
ImGui::Columns(1);
7546
ImGui::Separator();
7547
ImGui::TreePop();
7548
}
7549
7550
IMGUI_DEMO_MARKER("Columns (legacy API)/Borders");
7551
if (ImGui::TreeNode("Borders"))
7552
{
7553
// NB: Future columns API should allow automatic horizontal borders.
7554
static bool h_borders = true;
7555
static bool v_borders = true;
7556
static int columns_count = 4;
7557
const int lines_count = 3;
7558
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7559
ImGui::DragInt("##columns_count", &columns_count, 0.1f, 2, 10, "%d columns");
7560
if (columns_count < 2)
7561
columns_count = 2;
7562
ImGui::SameLine();
7563
ImGui::Checkbox("horizontal", &h_borders);
7564
ImGui::SameLine();
7565
ImGui::Checkbox("vertical", &v_borders);
7566
ImGui::Columns(columns_count, NULL, v_borders);
7567
for (int i = 0; i < columns_count * lines_count; i++)
7568
{
7569
if (h_borders && ImGui::GetColumnIndex() == 0)
7570
ImGui::Separator();
7571
ImGui::PushID(i);
7572
ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i);
7573
ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
7574
ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
7575
ImGui::Text("Offset %.2f", ImGui::GetColumnOffset());
7576
ImGui::Text("Long text that is likely to clip");
7577
ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f));
7578
ImGui::PopID();
7579
ImGui::NextColumn();
7580
}
7581
ImGui::Columns(1);
7582
if (h_borders)
7583
ImGui::Separator();
7584
ImGui::TreePop();
7585
}
7586
7587
// Create multiple items in a same cell before switching to next column
7588
IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items");
7589
if (ImGui::TreeNode("Mixed items"))
7590
{
7591
ImGui::Columns(3, "mixed");
7592
ImGui::Separator();
7593
7594
ImGui::Text("Hello");
7595
ImGui::Button("Banana");
7596
ImGui::NextColumn();
7597
7598
ImGui::Text("ImGui");
7599
ImGui::Button("Apple");
7600
static float foo = 1.0f;
7601
ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f");
7602
ImGui::Text("An extra line here.");
7603
ImGui::NextColumn();
7604
7605
ImGui::Text("Sailor");
7606
ImGui::Button("Corniflower");
7607
static float bar = 1.0f;
7608
ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f");
7609
ImGui::NextColumn();
7610
7611
if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7612
if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7613
if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7614
ImGui::Columns(1);
7615
ImGui::Separator();
7616
ImGui::TreePop();
7617
}
7618
7619
// Word wrapping
7620
IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping");
7621
if (ImGui::TreeNode("Word-wrapping"))
7622
{
7623
ImGui::Columns(2, "word-wrapping");
7624
ImGui::Separator();
7625
ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
7626
ImGui::TextWrapped("Hello Left");
7627
ImGui::NextColumn();
7628
ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
7629
ImGui::TextWrapped("Hello Right");
7630
ImGui::Columns(1);
7631
ImGui::Separator();
7632
ImGui::TreePop();
7633
}
7634
7635
IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling");
7636
if (ImGui::TreeNode("Horizontal Scrolling"))
7637
{
7638
ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));
7639
ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);
7640
ImGui::BeginChild("##ScrollingRegion", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar);
7641
ImGui::Columns(10);
7642
7643
// Also demonstrate using clipper for large vertical lists
7644
int ITEMS_COUNT = 2000;
7645
ImGuiListClipper clipper;
7646
clipper.Begin(ITEMS_COUNT);
7647
while (clipper.Step())
7648
{
7649
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
7650
for (int j = 0; j < 10; j++)
7651
{
7652
ImGui::Text("Line %d Column %d...", i, j);
7653
ImGui::NextColumn();
7654
}
7655
}
7656
ImGui::Columns(1);
7657
ImGui::EndChild();
7658
ImGui::TreePop();
7659
}
7660
7661
IMGUI_DEMO_MARKER("Columns (legacy API)/Tree");
7662
if (ImGui::TreeNode("Tree"))
7663
{
7664
ImGui::Columns(2, "tree", true);
7665
for (int x = 0; x < 3; x++)
7666
{
7667
bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x);
7668
ImGui::NextColumn();
7669
ImGui::Text("Node contents");
7670
ImGui::NextColumn();
7671
if (open1)
7672
{
7673
for (int y = 0; y < 3; y++)
7674
{
7675
bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y);
7676
ImGui::NextColumn();
7677
ImGui::Text("Node contents");
7678
if (open2)
7679
{
7680
ImGui::Text("Even more contents");
7681
if (ImGui::TreeNode("Tree in column"))
7682
{
7683
ImGui::Text("The quick brown fox jumps over the lazy dog");
7684
ImGui::TreePop();
7685
}
7686
}
7687
ImGui::NextColumn();
7688
if (open2)
7689
ImGui::TreePop();
7690
}
7691
ImGui::TreePop();
7692
}
7693
}
7694
ImGui::Columns(1);
7695
ImGui::TreePop();
7696
}
7697
7698
ImGui::TreePop();
7699
}
7700
7701
//-----------------------------------------------------------------------------
7702
// [SECTION] DemoWindowInputs()
7703
//-----------------------------------------------------------------------------
7704
7705
static void DemoWindowInputs()
7706
{
7707
IMGUI_DEMO_MARKER("Inputs & Focus");
7708
if (ImGui::CollapsingHeader("Inputs & Focus"))
7709
{
7710
ImGuiIO& io = ImGui::GetIO();
7711
7712
// Display inputs submitted to ImGuiIO
7713
IMGUI_DEMO_MARKER("Inputs & Focus/Inputs");
7714
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
7715
bool inputs_opened = ImGui::TreeNode("Inputs");
7716
ImGui::SameLine();
7717
HelpMarker(
7718
"This is a simplified view. See more detailed input state:\n"
7719
"- in 'Tools->Metrics/Debugger->Inputs'.\n"
7720
"- in 'Tools->Debug Log->IO'.");
7721
if (inputs_opened)
7722
{
7723
if (ImGui::IsMousePosValid())
7724
ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
7725
else
7726
ImGui::Text("Mouse pos: <INVALID>");
7727
ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
7728
ImGui::Text("Mouse down:");
7729
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
7730
ImGui::Text("Mouse wheel: %.1f", io.MouseWheel);
7731
ImGui::Text("Mouse clicked count:");
7732
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); }
7733
7734
// We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows
7735
// displaying the data for old/new backends.
7736
// User code should never have to go through such hoops!
7737
// You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
7738
struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
7739
ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;
7740
ImGui::Text("Keys down:"); for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); }
7741
ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
7742
ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
7743
7744
ImGui::TreePop();
7745
}
7746
7747
// Display ImGuiIO output flags
7748
IMGUI_DEMO_MARKER("Inputs & Focus/Outputs");
7749
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
7750
bool outputs_opened = ImGui::TreeNode("Outputs");
7751
ImGui::SameLine();
7752
HelpMarker(
7753
"The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui "
7754
"to instruct your application of how to route inputs. Typically, when a value is true, it means "
7755
"Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n"
7756
"The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, "
7757
"and underlying application should ignore mouse inputs (in practice there are many and more subtle "
7758
"rules leading to how those flags are set).");
7759
if (outputs_opened)
7760
{
7761
ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse);
7762
ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
7763
ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
7764
ImGui::Text("io.WantTextInput: %d", io.WantTextInput);
7765
ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos);
7766
ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible);
7767
7768
IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override");
7769
if (ImGui::TreeNode("WantCapture override"))
7770
{
7771
HelpMarker(
7772
"Hovering the colored canvas will override io.WantCaptureXXX fields.\n"
7773
"Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering "
7774
"and true when clicking.");
7775
static int capture_override_mouse = -1;
7776
static int capture_override_keyboard = -1;
7777
const char* capture_override_desc[] = { "None", "Set to false", "Set to true" };
7778
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7779
ImGui::SliderInt("SetNextFrameWantCaptureMouse() on hover", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp);
7780
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7781
ImGui::SliderInt("SetNextFrameWantCaptureKeyboard() on hover", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp);
7782
7783
ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item
7784
if (ImGui::IsItemHovered() && capture_override_mouse != -1)
7785
ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1);
7786
if (ImGui::IsItemHovered() && capture_override_keyboard != -1)
7787
ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1);
7788
7789
ImGui::TreePop();
7790
}
7791
ImGui::TreePop();
7792
}
7793
7794
// Demonstrate using Shortcut() and Routing Policies.
7795
// The general flow is:
7796
// - Code interested in a chord (e.g. "Ctrl+A") declares their intent.
7797
// - Multiple locations may be interested in same chord! Routing helps find a winner.
7798
// - Every frame, we resolve all claims and assign one owner if the modifiers are matching.
7799
// - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route.
7800
// - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags.
7801
// - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused.
7802
// TL;DR: Most uses will simply be:
7803
// - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy.
7804
IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts");
7805
if (ImGui::TreeNode("Shortcuts"))
7806
{
7807
static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat;
7808
static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused;
7809
ImGui::CheckboxFlags("ImGuiInputFlags_Repeat", &route_options, ImGuiInputFlags_Repeat);
7810
ImGui::RadioButton("ImGuiInputFlags_RouteActive", &route_type, ImGuiInputFlags_RouteActive);
7811
ImGui::RadioButton("ImGuiInputFlags_RouteFocused (default)", &route_type, ImGuiInputFlags_RouteFocused);
7812
ImGui::RadioButton("ImGuiInputFlags_RouteGlobal", &route_type, ImGuiInputFlags_RouteGlobal);
7813
ImGui::Indent();
7814
ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal);
7815
ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverFocused", &route_options, ImGuiInputFlags_RouteOverFocused);
7816
ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive", &route_options, ImGuiInputFlags_RouteOverActive);
7817
ImGui::CheckboxFlags("ImGuiInputFlags_RouteUnlessBgFocused", &route_options, ImGuiInputFlags_RouteUnlessBgFocused);
7818
ImGui::EndDisabled();
7819
ImGui::Unindent();
7820
ImGui::RadioButton("ImGuiInputFlags_RouteAlways", &route_type, ImGuiInputFlags_RouteAlways);
7821
ImGuiInputFlags flags = route_type | route_options; // Merged flags
7822
if (route_type != ImGuiInputFlags_RouteGlobal)
7823
flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused);
7824
7825
ImGui::SeparatorText("Using SetNextItemShortcut()");
7826
ImGui::Text("Ctrl+S");
7827
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, flags | ImGuiInputFlags_Tooltip);
7828
ImGui::Button("Save");
7829
ImGui::Text("Alt+F");
7830
ImGui::SetNextItemShortcut(ImGuiMod_Alt | ImGuiKey_F, flags | ImGuiInputFlags_Tooltip);
7831
static float f = 0.5f;
7832
ImGui::SliderFloat("Factor", &f, 0.0f, 1.0f);
7833
7834
ImGui::SeparatorText("Using Shortcut()");
7835
const float line_height = ImGui::GetTextLineHeightWithSpacing();
7836
const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A;
7837
7838
ImGui::Text("Ctrl+A");
7839
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7840
7841
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f));
7842
7843
ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true);
7844
ImGui::Text("Press CTRL+A and see who receives it!");
7845
ImGui::Separator();
7846
7847
// 1: Window polling for CTRL+A
7848
ImGui::Text("(in WindowA)");
7849
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7850
7851
// 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active)
7852
// (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
7853
//char str[16] = "Press CTRL+A";
7854
//ImGui::Spacing();
7855
//ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
7856
//ImGuiID item_id = ImGui::GetItemID();
7857
//ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused");
7858
//ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "...");
7859
7860
// 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA
7861
ImGui::BeginChild("ChildD", ImVec2(-FLT_MIN, line_height * 4), true);
7862
ImGui::Text("(in ChildD: not using same Shortcut)");
7863
ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused());
7864
ImGui::EndChild();
7865
7866
// 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused.
7867
ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true);
7868
ImGui::Text("(in ChildE: using same Shortcut)");
7869
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7870
ImGui::EndChild();
7871
7872
// 5: In a popup
7873
if (ImGui::Button("Open Popup"))
7874
ImGui::OpenPopup("PopupF");
7875
if (ImGui::BeginPopup("PopupF"))
7876
{
7877
ImGui::Text("(in PopupF)");
7878
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7879
// (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
7880
//ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
7881
//ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "...");
7882
ImGui::EndPopup();
7883
}
7884
ImGui::EndChild();
7885
ImGui::PopStyleColor();
7886
7887
ImGui::TreePop();
7888
}
7889
7890
// Display mouse cursors
7891
IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors");
7892
if (ImGui::TreeNode("Mouse Cursors"))
7893
{
7894
const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" };
7895
IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
7896
7897
ImGuiMouseCursor current = ImGui::GetMouseCursor();
7898
const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A";
7899
ImGui::Text("Current mouse cursor = %d: %s", current, cursor_name);
7900
ImGui::BeginDisabled(true);
7901
ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
7902
ImGui::EndDisabled();
7903
7904
ImGui::Text("Hover to see mouse cursors:");
7905
ImGui::SameLine(); HelpMarker(
7906
"Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. "
7907
"If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, "
7908
"otherwise your backend needs to handle it.");
7909
for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)
7910
{
7911
char label[32];
7912
sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]);
7913
ImGui::Bullet(); ImGui::Selectable(label, false);
7914
if (ImGui::IsItemHovered())
7915
ImGui::SetMouseCursor(i);
7916
}
7917
ImGui::TreePop();
7918
}
7919
7920
IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing");
7921
if (ImGui::TreeNode("Tabbing"))
7922
{
7923
ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields.");
7924
static char buf[32] = "hello";
7925
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
7926
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
7927
ImGui::InputText("3", buf, IM_ARRAYSIZE(buf));
7928
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
7929
ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf));
7930
ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
7931
ImGui::PopItemFlag();
7932
ImGui::InputText("5", buf, IM_ARRAYSIZE(buf));
7933
ImGui::TreePop();
7934
}
7935
7936
IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code");
7937
if (ImGui::TreeNode("Focus from code"))
7938
{
7939
bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine();
7940
bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine();
7941
bool focus_3 = ImGui::Button("Focus on 3");
7942
int has_focus = 0;
7943
static char buf[128] = "click on a button to set focus";
7944
7945
if (focus_1) ImGui::SetKeyboardFocusHere();
7946
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
7947
if (ImGui::IsItemActive()) has_focus = 1;
7948
7949
if (focus_2) ImGui::SetKeyboardFocusHere();
7950
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
7951
if (ImGui::IsItemActive()) has_focus = 2;
7952
7953
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
7954
if (focus_3) ImGui::SetKeyboardFocusHere();
7955
ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf));
7956
if (ImGui::IsItemActive()) has_focus = 3;
7957
ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
7958
ImGui::PopItemFlag();
7959
7960
if (has_focus)
7961
ImGui::Text("Item with focus: %d", has_focus);
7962
else
7963
ImGui::Text("Item with focus: <none>");
7964
7965
// Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item
7966
static float f3[3] = { 0.0f, 0.0f, 0.0f };
7967
int focus_ahead = -1;
7968
if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine();
7969
if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine();
7970
if (ImGui::Button("Focus on Z")) { focus_ahead = 2; }
7971
if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead);
7972
ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f);
7973
7974
ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code.");
7975
ImGui::TreePop();
7976
}
7977
7978
IMGUI_DEMO_MARKER("Inputs & Focus/Dragging");
7979
if (ImGui::TreeNode("Dragging"))
7980
{
7981
ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");
7982
for (int button = 0; button < 3; button++)
7983
{
7984
ImGui::Text("IsMouseDragging(%d):", button);
7985
ImGui::Text(" w/ default threshold: %d,", ImGui::IsMouseDragging(button));
7986
ImGui::Text(" w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f));
7987
ImGui::Text(" w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f));
7988
}
7989
7990
ImGui::Button("Drag Me");
7991
if (ImGui::IsItemActive())
7992
ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor
7993
7994
// Drag operations gets "unlocked" when the mouse has moved past a certain threshold
7995
// (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
7996
// threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
7997
ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);
7998
ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0);
7999
ImVec2 mouse_delta = io.MouseDelta;
8000
ImGui::Text("GetMouseDragDelta(0):");
8001
ImGui::Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y);
8002
ImGui::Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y);
8003
ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y);
8004
ImGui::TreePop();
8005
}
8006
}
8007
}
8008
8009
//-----------------------------------------------------------------------------
8010
// [SECTION] About Window / ShowAboutWindow()
8011
// Access from Dear ImGui Demo -> Tools -> About
8012
//-----------------------------------------------------------------------------
8013
8014
void ImGui::ShowAboutWindow(bool* p_open)
8015
{
8016
if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize))
8017
{
8018
ImGui::End();
8019
return;
8020
}
8021
IMGUI_DEMO_MARKER("Tools/About Dear ImGui");
8022
ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8023
8024
ImGui::TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui");
8025
ImGui::SameLine();
8026
ImGui::TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md");
8027
ImGui::SameLine();
8028
ImGui::TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki");
8029
ImGui::SameLine();
8030
ImGui::TextLinkOpenURL("Extensions", "https://github.com/ocornut/imgui/wiki/Useful-Extensions");
8031
ImGui::SameLine();
8032
ImGui::TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases");
8033
ImGui::SameLine();
8034
ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding");
8035
8036
ImGui::Separator();
8037
ImGui::Text("(c) 2014-2025 Omar Cornut");
8038
ImGui::Text("Developed by Omar Cornut and all Dear ImGui contributors.");
8039
ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information.");
8040
ImGui::Text("If your company uses this, please consider funding the project.");
8041
8042
static bool show_config_info = false;
8043
ImGui::Checkbox("Config/Build Information", &show_config_info);
8044
if (show_config_info)
8045
{
8046
ImGuiIO& io = ImGui::GetIO();
8047
ImGuiStyle& style = ImGui::GetStyle();
8048
8049
bool copy_to_clipboard = ImGui::Button("Copy to clipboard");
8050
ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);
8051
ImGui::BeginChild(ImGui::GetID("cfg_infos"), child_size, ImGuiChildFlags_FrameStyle);
8052
if (copy_to_clipboard)
8053
{
8054
ImGui::LogToClipboard();
8055
ImGui::LogText("```cpp\n"); // Back quotes will make text appears without formatting when pasting on GitHub
8056
}
8057
8058
ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8059
ImGui::Separator();
8060
ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));
8061
ImGui::Text("define: __cplusplus=%d", (int)__cplusplus);
8062
#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8063
ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
8064
#endif
8065
#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
8066
ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
8067
#endif
8068
#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
8069
ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS");
8070
#endif
8071
#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS
8072
ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS");
8073
#endif
8074
#ifdef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
8075
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS");
8076
#endif
8077
#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
8078
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS");
8079
#endif
8080
#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
8081
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS");
8082
#endif
8083
#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
8084
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS");
8085
#endif
8086
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
8087
ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS");
8088
#endif
8089
#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS
8090
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS");
8091
#endif
8092
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
8093
ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR");
8094
#endif
8095
#ifdef _WIN32
8096
ImGui::Text("define: _WIN32");
8097
#endif
8098
#ifdef _WIN64
8099
ImGui::Text("define: _WIN64");
8100
#endif
8101
#ifdef __linux__
8102
ImGui::Text("define: __linux__");
8103
#endif
8104
#ifdef __APPLE__
8105
ImGui::Text("define: __APPLE__");
8106
#endif
8107
#ifdef _MSC_VER
8108
ImGui::Text("define: _MSC_VER=%d", _MSC_VER);
8109
#endif
8110
#ifdef _MSVC_LANG
8111
ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG);
8112
#endif
8113
#ifdef __MINGW32__
8114
ImGui::Text("define: __MINGW32__");
8115
#endif
8116
#ifdef __MINGW64__
8117
ImGui::Text("define: __MINGW64__");
8118
#endif
8119
#ifdef __GNUC__
8120
ImGui::Text("define: __GNUC__=%d", (int)__GNUC__);
8121
#endif
8122
#ifdef __clang_version__
8123
ImGui::Text("define: __clang_version__=%s", __clang_version__);
8124
#endif
8125
#ifdef __EMSCRIPTEN__
8126
ImGui::Text("define: __EMSCRIPTEN__");
8127
ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
8128
#endif
8129
ImGui::Separator();
8130
ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
8131
ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
8132
ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags);
8133
if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard");
8134
if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad");
8135
if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse");
8136
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange");
8137
if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) ImGui::Text(" NoKeyboard");
8138
if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor");
8139
if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors");
8140
if (io.ConfigNavMoveSetMousePos) ImGui::Text("io.ConfigNavMoveSetMousePos");
8141
if (io.ConfigNavCaptureKeyboard) ImGui::Text("io.ConfigNavCaptureKeyboard");
8142
if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink");
8143
if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges");
8144
if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly");
8145
if (io.ConfigMemoryCompactTimer >= 0.0f) ImGui::Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer);
8146
ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags);
8147
if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad");
8148
if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors");
8149
if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos");
8150
if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset");
8151
if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) ImGui::Text(" RendererHasTextures");
8152
ImGui::Separator();
8153
ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height);
8154
ImGui::Text("io.Fonts->FontLoaderName: %s", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL");
8155
ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
8156
ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
8157
ImGui::Separator();
8158
ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y);
8159
ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize);
8160
ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y);
8161
ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding);
8162
ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize);
8163
ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y);
8164
ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);
8165
8166
if (copy_to_clipboard)
8167
{
8168
ImGui::LogText("\n```\n");
8169
ImGui::LogFinish();
8170
}
8171
ImGui::EndChild();
8172
}
8173
ImGui::End();
8174
}
8175
8176
//-----------------------------------------------------------------------------
8177
// [SECTION] Style Editor / ShowStyleEditor()
8178
//-----------------------------------------------------------------------------
8179
// - ShowStyleSelector()
8180
// - ShowStyleEditor()
8181
//-----------------------------------------------------------------------------
8182
8183
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
8184
// Here we use the simplified Combo() api that packs items into a single literal string.
8185
// Useful for quick combo boxes where the choices are known locally.
8186
bool ImGui::ShowStyleSelector(const char* label)
8187
{
8188
static int style_idx = -1;
8189
if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0"))
8190
{
8191
switch (style_idx)
8192
{
8193
case 0: ImGui::StyleColorsDark(); break;
8194
case 1: ImGui::StyleColorsLight(); break;
8195
case 2: ImGui::StyleColorsClassic(); break;
8196
}
8197
return true;
8198
}
8199
return false;
8200
}
8201
8202
static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
8203
{
8204
if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return "DrawLinesNone";
8205
if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return "DrawLinesFull";
8206
if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return "DrawLinesToNodes";
8207
return "";
8208
}
8209
8210
// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8211
void ImGui::ShowStyleEditor(ImGuiStyle* ref)
8212
{
8213
IMGUI_DEMO_MARKER("Tools/Style Editor");
8214
// You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
8215
// (without a reference style pointer, we will use one compared locally as a reference)
8216
ImGuiStyle& style = GetStyle();
8217
static ImGuiStyle ref_saved_style;
8218
8219
// Default to using internal storage as reference
8220
static bool init = true;
8221
if (init && ref == NULL)
8222
ref_saved_style = style;
8223
init = false;
8224
if (ref == NULL)
8225
ref = &ref_saved_style;
8226
8227
PushItemWidth(GetWindowWidth() * 0.50f);
8228
8229
{
8230
// General
8231
SeparatorText("General");
8232
if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
8233
BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
8234
8235
if (ShowStyleSelector("Colors##Selector"))
8236
ref_saved_style = style;
8237
ShowFontSelector("Fonts##Selector");
8238
if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f"))
8239
style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
8240
SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize());
8241
DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);
8242
//BeginDisabled(GetIO().ConfigDpiScaleFonts);
8243
DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 5.0f);
8244
//SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
8245
//EndDisabled();
8246
8247
// Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
8248
if (SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
8249
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
8250
{ bool border = (style.WindowBorderSize > 0.0f); if (Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
8251
SameLine();
8252
{ bool border = (style.FrameBorderSize > 0.0f); if (Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
8253
SameLine();
8254
{ bool border = (style.PopupBorderSize > 0.0f); if (Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
8255
}
8256
8257
// Save/Revert button
8258
if (Button("Save Ref"))
8259
*ref = ref_saved_style = style;
8260
SameLine();
8261
if (Button("Revert Ref"))
8262
style = *ref;
8263
SameLine();
8264
HelpMarker(
8265
"Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
8266
"Use \"Export\" below to save them somewhere.");
8267
8268
SeparatorText("Details");
8269
if (BeginTabBar("##tabs", ImGuiTabBarFlags_None))
8270
{
8271
if (BeginTabItem("Sizes"))
8272
{
8273
SeparatorText("Main");
8274
SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
8275
SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
8276
SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
8277
SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
8278
SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
8279
SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
8280
SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
8281
SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
8282
8283
SeparatorText("Borders");
8284
SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
8285
SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
8286
SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
8287
SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
8288
8289
SeparatorText("Rounding");
8290
SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
8291
SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
8292
SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
8293
SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
8294
SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
8295
SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
8296
8297
SeparatorText("Tabs");
8298
SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
8299
SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f");
8300
SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f");
8301
SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
8302
DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
8303
DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
8304
SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
8305
8306
SeparatorText("Tables");
8307
SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
8308
SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f);
8309
SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
8310
8311
SeparatorText("Trees");
8312
bool combo_open = BeginCombo("TreeLinesFlags", GetTreeLinesFlagsName(style.TreeLinesFlags));
8313
SameLine();
8314
HelpMarker("[Experimental] Tree lines may not work in all situations (e.g. using a clipper) and may incurs slight traversal overhead.\n\nImGuiTreeNodeFlags_DrawLinesFull is faster than ImGuiTreeNodeFlags_DrawLinesToNode.");
8315
if (combo_open)
8316
{
8317
const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes };
8318
for (ImGuiTreeNodeFlags option : options)
8319
if (Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option))
8320
style.TreeLinesFlags = option;
8321
EndCombo();
8322
}
8323
SliderFloat("TreeLinesSize", &style.TreeLinesSize, 0.0f, 2.0f, "%.0f");
8324
SliderFloat("TreeLinesRounding", &style.TreeLinesRounding, 0.0f, 12.0f, "%.0f");
8325
8326
SeparatorText("Windows");
8327
SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
8328
SliderFloat("WindowBorderHoverPadding", &style.WindowBorderHoverPadding, 1.0f, 20.0f, "%.0f");
8329
int window_menu_button_position = style.WindowMenuButtonPosition + 1;
8330
if (Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0"))
8331
style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);
8332
8333
SeparatorText("Widgets");
8334
Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
8335
SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
8336
SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
8337
SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
8338
SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.");
8339
SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
8340
SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f");
8341
SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f");
8342
SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
8343
SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, 1.0f, "%.0f");
8344
8345
SeparatorText("Tooltips");
8346
for (int n = 0; n < 2; n++)
8347
if (TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav"))
8348
{
8349
ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;
8350
CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone);
8351
CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort);
8352
CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal);
8353
CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary);
8354
CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay);
8355
TreePop();
8356
}
8357
8358
SeparatorText("Misc");
8359
SliderFloat2("DisplayWindowPadding", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.");
8360
SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).");
8361
8362
EndTabItem();
8363
}
8364
8365
if (BeginTabItem("Colors"))
8366
{
8367
static int output_dest = 0;
8368
static bool output_only_modified = true;
8369
if (Button("Export"))
8370
{
8371
if (output_dest == 0)
8372
LogToClipboard();
8373
else
8374
LogToTTY();
8375
LogText("ImVec4* colors = GetStyle().Colors;" IM_NEWLINE);
8376
for (int i = 0; i < ImGuiCol_COUNT; i++)
8377
{
8378
const ImVec4& col = style.Colors[i];
8379
const char* name = GetStyleColorName(i);
8380
if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
8381
LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
8382
name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
8383
}
8384
LogFinish();
8385
}
8386
SameLine(); SetNextItemWidth(120); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
8387
SameLine(); Checkbox("Only Modified Colors", &output_only_modified);
8388
8389
static ImGuiTextFilter filter;
8390
filter.Draw("Filter colors", GetFontSize() * 16);
8391
8392
static ImGuiColorEditFlags alpha_flags = 0;
8393
if (RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine();
8394
if (RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } SameLine();
8395
if (RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } SameLine();
8396
HelpMarker(
8397
"In the color list:\n"
8398
"Left-click on color square to open color picker,\n"
8399
"Right-click to open edit options menu.");
8400
8401
SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));
8402
BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
8403
PushItemWidth(GetFontSize() * -12);
8404
for (int i = 0; i < ImGuiCol_COUNT; i++)
8405
{
8406
const char* name = GetStyleColorName(i);
8407
if (!filter.PassFilter(name))
8408
continue;
8409
PushID(i);
8410
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8411
if (Button("?"))
8412
DebugFlashStyleColor((ImGuiCol)i);
8413
SetItemTooltip("Flash given color to identify places where it is used.");
8414
SameLine();
8415
#endif
8416
ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);
8417
if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)
8418
{
8419
// Tips: in a real user application, you may want to merge and use an icon font into the main font,
8420
// so instead of "Save"/"Revert" you'd use icons!
8421
// Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
8422
SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Save")) { ref->Colors[i] = style.Colors[i]; }
8423
SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Revert")) { style.Colors[i] = ref->Colors[i]; }
8424
}
8425
SameLine(0.0f, style.ItemInnerSpacing.x);
8426
TextUnformatted(name);
8427
PopID();
8428
}
8429
PopItemWidth();
8430
EndChild();
8431
8432
EndTabItem();
8433
}
8434
8435
if (BeginTabItem("Fonts"))
8436
{
8437
ImGuiIO& io = GetIO();
8438
ImFontAtlas* atlas = io.Fonts;
8439
ShowFontAtlas(atlas);
8440
8441
// Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
8442
// (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
8443
/*
8444
SeparatorText("Legacy Scaling");
8445
const float MIN_SCALE = 0.3f;
8446
const float MAX_SCALE = 2.0f;
8447
HelpMarker(
8448
"Those are old settings provided for convenience.\n"
8449
"However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
8450
"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
8451
"Using those settings here will give you poor quality results.");
8452
PushItemWidth(GetFontSize() * 8);
8453
DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
8454
//static float window_scale = 1.0f;
8455
//if (DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
8456
// SetWindowFontScale(window_scale);
8457
PopItemWidth();
8458
*/
8459
8460
EndTabItem();
8461
}
8462
8463
if (BeginTabItem("Rendering"))
8464
{
8465
Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
8466
SameLine();
8467
HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
8468
8469
Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
8470
SameLine();
8471
HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
8472
8473
Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
8474
PushItemWidth(GetFontSize() * 8);
8475
DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
8476
if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
8477
8478
// When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
8479
DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp);
8480
const bool show_samples = IsItemActive();
8481
if (show_samples)
8482
SetNextWindowPos(GetCursorScreenPos());
8483
if (show_samples && BeginTooltip())
8484
{
8485
TextUnformatted("(R = radius, N = approx number of segments)");
8486
Spacing();
8487
ImDrawList* draw_list = GetWindowDrawList();
8488
const float min_widget_width = CalcTextSize("R: MMM\nN: MMM").x;
8489
for (int n = 0; n < 8; n++)
8490
{
8491
const float RAD_MIN = 5.0f;
8492
const float RAD_MAX = 70.0f;
8493
const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
8494
8495
BeginGroup();
8496
8497
// N is not always exact here due to how PathArcTo() function work internally
8498
Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad));
8499
8500
const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
8501
const float offset_x = floorf(canvas_width * 0.5f);
8502
const float offset_y = floorf(RAD_MAX);
8503
8504
const ImVec2 p1 = GetCursorScreenPos();
8505
draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
8506
Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8507
8508
/*
8509
const ImVec2 p2 = GetCursorScreenPos();
8510
draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
8511
Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8512
*/
8513
8514
EndGroup();
8515
SameLine();
8516
}
8517
EndTooltip();
8518
}
8519
SameLine();
8520
HelpMarker("When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically.");
8521
8522
DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.
8523
DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha).");
8524
PopItemWidth();
8525
8526
EndTabItem();
8527
}
8528
8529
EndTabBar();
8530
}
8531
PopItemWidth();
8532
}
8533
8534
//-----------------------------------------------------------------------------
8535
// [SECTION] User Guide / ShowUserGuide()
8536
//-----------------------------------------------------------------------------
8537
8538
// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8539
void ImGui::ShowUserGuide()
8540
{
8541
ImGuiIO& io = GetIO();
8542
BulletText("Double-click on title bar to collapse window.");
8543
BulletText(
8544
"Click and drag on lower corner to resize window\n"
8545
"(double-click to auto fit window to its contents).");
8546
BulletText("CTRL+Click on a slider or drag box to input value as text.");
8547
BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
8548
BulletText("CTRL+Tab to select a window.");
8549
if (io.FontAllowUserScaling)
8550
BulletText("CTRL+Mouse Wheel to zoom window contents.");
8551
BulletText("While inputting text:\n");
8552
Indent();
8553
BulletText("CTRL+Left/Right to word jump.");
8554
BulletText("CTRL+A or double-click to select all.");
8555
BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
8556
BulletText("CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.");
8557
BulletText("ESCAPE to revert.");
8558
Unindent();
8559
BulletText("With keyboard navigation enabled:");
8560
Indent();
8561
BulletText("Arrow keys to navigate.");
8562
BulletText("Space to activate a widget.");
8563
BulletText("Return to input text into a widget.");
8564
BulletText("Escape to deactivate a widget, close popup, exit child window.");
8565
BulletText("Alt to jump to the menu layer of a window.");
8566
Unindent();
8567
}
8568
8569
//-----------------------------------------------------------------------------
8570
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
8571
//-----------------------------------------------------------------------------
8572
// - ShowExampleAppMainMenuBar()
8573
// - ShowExampleMenuFile()
8574
//-----------------------------------------------------------------------------
8575
8576
// Demonstrate creating a "main" fullscreen menu bar and populating it.
8577
// Note the difference between BeginMainMenuBar() and BeginMenuBar():
8578
// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)
8579
// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.
8580
static void ShowExampleAppMainMenuBar()
8581
{
8582
if (ImGui::BeginMainMenuBar())
8583
{
8584
if (ImGui::BeginMenu("File"))
8585
{
8586
ShowExampleMenuFile();
8587
ImGui::EndMenu();
8588
}
8589
if (ImGui::BeginMenu("Edit"))
8590
{
8591
if (ImGui::MenuItem("Undo", "CTRL+Z")) {}
8592
if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item
8593
ImGui::Separator();
8594
if (ImGui::MenuItem("Cut", "CTRL+X")) {}
8595
if (ImGui::MenuItem("Copy", "CTRL+C")) {}
8596
if (ImGui::MenuItem("Paste", "CTRL+V")) {}
8597
ImGui::EndMenu();
8598
}
8599
ImGui::EndMainMenuBar();
8600
}
8601
}
8602
8603
// Note that shortcuts are currently provided for display only
8604
// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
8605
static void ShowExampleMenuFile()
8606
{
8607
IMGUI_DEMO_MARKER("Examples/Menu");
8608
ImGui::MenuItem("(demo menu)", NULL, false, false);
8609
if (ImGui::MenuItem("New")) {}
8610
if (ImGui::MenuItem("Open", "Ctrl+O")) {}
8611
if (ImGui::BeginMenu("Open Recent"))
8612
{
8613
ImGui::MenuItem("fish_hat.c");
8614
ImGui::MenuItem("fish_hat.inl");
8615
ImGui::MenuItem("fish_hat.h");
8616
if (ImGui::BeginMenu("More.."))
8617
{
8618
ImGui::MenuItem("Hello");
8619
ImGui::MenuItem("Sailor");
8620
if (ImGui::BeginMenu("Recurse.."))
8621
{
8622
ShowExampleMenuFile();
8623
ImGui::EndMenu();
8624
}
8625
ImGui::EndMenu();
8626
}
8627
ImGui::EndMenu();
8628
}
8629
if (ImGui::MenuItem("Save", "Ctrl+S")) {}
8630
if (ImGui::MenuItem("Save As..")) {}
8631
8632
ImGui::Separator();
8633
IMGUI_DEMO_MARKER("Examples/Menu/Options");
8634
if (ImGui::BeginMenu("Options"))
8635
{
8636
static bool enabled = true;
8637
ImGui::MenuItem("Enabled", "", &enabled);
8638
ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders);
8639
for (int i = 0; i < 10; i++)
8640
ImGui::Text("Scrolling Text %d", i);
8641
ImGui::EndChild();
8642
static float f = 0.5f;
8643
static int n = 0;
8644
ImGui::SliderFloat("Value", &f, 0.0f, 1.0f);
8645
ImGui::InputFloat("Input", &f, 0.1f);
8646
ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0");
8647
ImGui::EndMenu();
8648
}
8649
8650
IMGUI_DEMO_MARKER("Examples/Menu/Colors");
8651
if (ImGui::BeginMenu("Colors"))
8652
{
8653
float sz = ImGui::GetTextLineHeight();
8654
for (int i = 0; i < ImGuiCol_COUNT; i++)
8655
{
8656
const char* name = ImGui::GetStyleColorName((ImGuiCol)i);
8657
ImVec2 p = ImGui::GetCursorScreenPos();
8658
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i));
8659
ImGui::Dummy(ImVec2(sz, sz));
8660
ImGui::SameLine();
8661
ImGui::MenuItem(name);
8662
}
8663
ImGui::EndMenu();
8664
}
8665
8666
// Here we demonstrate appending again to the "Options" menu (which we already created above)
8667
// Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice.
8668
// In a real code-base using it would make senses to use this feature from very different code locations.
8669
if (ImGui::BeginMenu("Options")) // <-- Append!
8670
{
8671
IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu");
8672
static bool b = true;
8673
ImGui::Checkbox("SomeOption", &b);
8674
ImGui::EndMenu();
8675
}
8676
8677
if (ImGui::BeginMenu("Disabled", false)) // Disabled
8678
{
8679
IM_ASSERT(0);
8680
}
8681
if (ImGui::MenuItem("Checked", NULL, true)) {}
8682
ImGui::Separator();
8683
if (ImGui::MenuItem("Quit", "Alt+F4")) {}
8684
}
8685
8686
//-----------------------------------------------------------------------------
8687
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
8688
//-----------------------------------------------------------------------------
8689
8690
// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
8691
// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
8692
struct ExampleAppConsole
8693
{
8694
char InputBuf[256];
8695
ImVector<char*> Items;
8696
ImVector<const char*> Commands;
8697
ImVector<char*> History;
8698
int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
8699
ImGuiTextFilter Filter;
8700
bool AutoScroll;
8701
bool ScrollToBottom;
8702
8703
ExampleAppConsole()
8704
{
8705
IMGUI_DEMO_MARKER("Examples/Console");
8706
ClearLog();
8707
memset(InputBuf, 0, sizeof(InputBuf));
8708
HistoryPos = -1;
8709
8710
// "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
8711
Commands.push_back("HELP");
8712
Commands.push_back("HISTORY");
8713
Commands.push_back("CLEAR");
8714
Commands.push_back("CLASSIFY");
8715
AutoScroll = true;
8716
ScrollToBottom = false;
8717
AddLog("Welcome to Dear ImGui!");
8718
}
8719
~ExampleAppConsole()
8720
{
8721
ClearLog();
8722
for (int i = 0; i < History.Size; i++)
8723
ImGui::MemFree(History[i]);
8724
}
8725
8726
// Portable helpers
8727
static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }
8728
static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }
8729
static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); }
8730
static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
8731
8732
void ClearLog()
8733
{
8734
for (int i = 0; i < Items.Size; i++)
8735
ImGui::MemFree(Items[i]);
8736
Items.clear();
8737
}
8738
8739
void AddLog(const char* fmt, ...) IM_FMTARGS(2)
8740
{
8741
// FIXME-OPT
8742
char buf[1024];
8743
va_list args;
8744
va_start(args, fmt);
8745
vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
8746
buf[IM_ARRAYSIZE(buf)-1] = 0;
8747
va_end(args);
8748
Items.push_back(Strdup(buf));
8749
}
8750
8751
void Draw(const char* title, bool* p_open)
8752
{
8753
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
8754
if (!ImGui::Begin(title, p_open))
8755
{
8756
ImGui::End();
8757
return;
8758
}
8759
8760
// As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
8761
// So e.g. IsItemHovered() will return true when hovering the title bar.
8762
// Here we create a context menu only available from the title bar.
8763
if (ImGui::BeginPopupContextItem())
8764
{
8765
if (ImGui::MenuItem("Close Console"))
8766
*p_open = false;
8767
ImGui::EndPopup();
8768
}
8769
8770
ImGui::TextWrapped(
8771
"This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate "
8772
"implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
8773
ImGui::TextWrapped("Enter 'HELP' for help.");
8774
8775
// TODO: display items starting from the bottom
8776
8777
if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); }
8778
ImGui::SameLine();
8779
if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); }
8780
ImGui::SameLine();
8781
if (ImGui::SmallButton("Clear")) { ClearLog(); }
8782
ImGui::SameLine();
8783
bool copy_to_clipboard = ImGui::SmallButton("Copy");
8784
//static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
8785
8786
ImGui::Separator();
8787
8788
// Options menu
8789
if (ImGui::BeginPopup("Options"))
8790
{
8791
ImGui::Checkbox("Auto-scroll", &AutoScroll);
8792
ImGui::EndPopup();
8793
}
8794
8795
// Options, Filter
8796
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_O, ImGuiInputFlags_Tooltip);
8797
if (ImGui::Button("Options"))
8798
ImGui::OpenPopup("Options");
8799
ImGui::SameLine();
8800
Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
8801
ImGui::Separator();
8802
8803
// Reserve enough left-over height for 1 separator + 1 input text
8804
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
8805
if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar))
8806
{
8807
if (ImGui::BeginPopupContextWindow())
8808
{
8809
if (ImGui::Selectable("Clear")) ClearLog();
8810
ImGui::EndPopup();
8811
}
8812
8813
// Display every line as a separate entry so we can change their color or add custom widgets.
8814
// If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
8815
// NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
8816
// to only process visible items. The clipper will automatically measure the height of your first item and then
8817
// "seek" to display only items in the visible area.
8818
// To use the clipper we can replace your standard loop:
8819
// for (int i = 0; i < Items.Size; i++)
8820
// With:
8821
// ImGuiListClipper clipper;
8822
// clipper.Begin(Items.Size);
8823
// while (clipper.Step())
8824
// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8825
// - That your items are evenly spaced (same height)
8826
// - That you have cheap random access to your elements (you can access them given their index,
8827
// without processing all the ones before)
8828
// You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
8829
// We would need random-access on the post-filtered list.
8830
// A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
8831
// or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
8832
// and appending newly elements as they are inserted. This is left as a task to the user until we can manage
8833
// to improve this example code!
8834
// If your items are of variable height:
8835
// - Split them into same height items would be simpler and facilitate random-seeking into your list.
8836
// - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
8837
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing
8838
if (copy_to_clipboard)
8839
ImGui::LogToClipboard();
8840
for (const char* item : Items)
8841
{
8842
if (!Filter.PassFilter(item))
8843
continue;
8844
8845
// Normally you would store more information in your item than just a string.
8846
// (e.g. make Items[] an array of structure, store color/type etc.)
8847
ImVec4 color;
8848
bool has_color = false;
8849
if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
8850
else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
8851
if (has_color)
8852
ImGui::PushStyleColor(ImGuiCol_Text, color);
8853
ImGui::TextUnformatted(item);
8854
if (has_color)
8855
ImGui::PopStyleColor();
8856
}
8857
if (copy_to_clipboard)
8858
ImGui::LogFinish();
8859
8860
// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
8861
// Using a scrollbar or mouse-wheel will take away from the bottom edge.
8862
if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
8863
ImGui::SetScrollHereY(1.0f);
8864
ScrollToBottom = false;
8865
8866
ImGui::PopStyleVar();
8867
}
8868
ImGui::EndChild();
8869
ImGui::Separator();
8870
8871
// Command-line
8872
bool reclaim_focus = false;
8873
ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
8874
if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))
8875
{
8876
char* s = InputBuf;
8877
Strtrim(s);
8878
if (s[0])
8879
ExecCommand(s);
8880
strcpy(s, "");
8881
reclaim_focus = true;
8882
}
8883
8884
// Auto-focus on window apparition
8885
ImGui::SetItemDefaultFocus();
8886
if (reclaim_focus)
8887
ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
8888
8889
ImGui::End();
8890
}
8891
8892
void ExecCommand(const char* command_line)
8893
{
8894
AddLog("# %s\n", command_line);
8895
8896
// Insert into history. First find match and delete it so it can be pushed to the back.
8897
// This isn't trying to be smart or optimal.
8898
HistoryPos = -1;
8899
for (int i = History.Size - 1; i >= 0; i--)
8900
if (Stricmp(History[i], command_line) == 0)
8901
{
8902
ImGui::MemFree(History[i]);
8903
History.erase(History.begin() + i);
8904
break;
8905
}
8906
History.push_back(Strdup(command_line));
8907
8908
// Process command
8909
if (Stricmp(command_line, "CLEAR") == 0)
8910
{
8911
ClearLog();
8912
}
8913
else if (Stricmp(command_line, "HELP") == 0)
8914
{
8915
AddLog("Commands:");
8916
for (int i = 0; i < Commands.Size; i++)
8917
AddLog("- %s", Commands[i]);
8918
}
8919
else if (Stricmp(command_line, "HISTORY") == 0)
8920
{
8921
int first = History.Size - 10;
8922
for (int i = first > 0 ? first : 0; i < History.Size; i++)
8923
AddLog("%3d: %s\n", i, History[i]);
8924
}
8925
else
8926
{
8927
AddLog("Unknown command: '%s'\n", command_line);
8928
}
8929
8930
// On command input, we scroll to bottom even if AutoScroll==false
8931
ScrollToBottom = true;
8932
}
8933
8934
// In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
8935
static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)
8936
{
8937
ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;
8938
return console->TextEditCallback(data);
8939
}
8940
8941
int TextEditCallback(ImGuiInputTextCallbackData* data)
8942
{
8943
//AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
8944
switch (data->EventFlag)
8945
{
8946
case ImGuiInputTextFlags_CallbackCompletion:
8947
{
8948
// Example of TEXT COMPLETION
8949
8950
// Locate beginning of current word
8951
const char* word_end = data->Buf + data->CursorPos;
8952
const char* word_start = word_end;
8953
while (word_start > data->Buf)
8954
{
8955
const char c = word_start[-1];
8956
if (c == ' ' || c == '\t' || c == ',' || c == ';')
8957
break;
8958
word_start--;
8959
}
8960
8961
// Build a list of candidates
8962
ImVector<const char*> candidates;
8963
for (int i = 0; i < Commands.Size; i++)
8964
if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
8965
candidates.push_back(Commands[i]);
8966
8967
if (candidates.Size == 0)
8968
{
8969
// No match
8970
AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
8971
}
8972
else if (candidates.Size == 1)
8973
{
8974
// Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
8975
data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
8976
data->InsertChars(data->CursorPos, candidates[0]);
8977
data->InsertChars(data->CursorPos, " ");
8978
}
8979
else
8980
{
8981
// Multiple matches. Complete as much as we can..
8982
// So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
8983
int match_len = (int)(word_end - word_start);
8984
for (;;)
8985
{
8986
int c = 0;
8987
bool all_candidates_matches = true;
8988
for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
8989
if (i == 0)
8990
c = toupper(candidates[i][match_len]);
8991
else if (c == 0 || c != toupper(candidates[i][match_len]))
8992
all_candidates_matches = false;
8993
if (!all_candidates_matches)
8994
break;
8995
match_len++;
8996
}
8997
8998
if (match_len > 0)
8999
{
9000
data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
9001
data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
9002
}
9003
9004
// List matches
9005
AddLog("Possible matches:\n");
9006
for (int i = 0; i < candidates.Size; i++)
9007
AddLog("- %s\n", candidates[i]);
9008
}
9009
9010
break;
9011
}
9012
case ImGuiInputTextFlags_CallbackHistory:
9013
{
9014
// Example of HISTORY
9015
const int prev_history_pos = HistoryPos;
9016
if (data->EventKey == ImGuiKey_UpArrow)
9017
{
9018
if (HistoryPos == -1)
9019
HistoryPos = History.Size - 1;
9020
else if (HistoryPos > 0)
9021
HistoryPos--;
9022
}
9023
else if (data->EventKey == ImGuiKey_DownArrow)
9024
{
9025
if (HistoryPos != -1)
9026
if (++HistoryPos >= History.Size)
9027
HistoryPos = -1;
9028
}
9029
9030
// A better implementation would preserve the data on the current input line along with cursor position.
9031
if (prev_history_pos != HistoryPos)
9032
{
9033
const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
9034
data->DeleteChars(0, data->BufTextLen);
9035
data->InsertChars(0, history_str);
9036
}
9037
}
9038
}
9039
return 0;
9040
}
9041
};
9042
9043
static void ShowExampleAppConsole(bool* p_open)
9044
{
9045
static ExampleAppConsole console;
9046
console.Draw("Example: Console", p_open);
9047
}
9048
9049
//-----------------------------------------------------------------------------
9050
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
9051
//-----------------------------------------------------------------------------
9052
9053
// Usage:
9054
// static ExampleAppLog my_log;
9055
// my_log.AddLog("Hello %d world\n", 123);
9056
// my_log.Draw("title");
9057
struct ExampleAppLog
9058
{
9059
ImGuiTextBuffer Buf;
9060
ImGuiTextFilter Filter;
9061
ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
9062
bool AutoScroll; // Keep scrolling if already at the bottom.
9063
9064
ExampleAppLog()
9065
{
9066
AutoScroll = true;
9067
Clear();
9068
}
9069
9070
void Clear()
9071
{
9072
Buf.clear();
9073
LineOffsets.clear();
9074
LineOffsets.push_back(0);
9075
}
9076
9077
void AddLog(const char* fmt, ...) IM_FMTARGS(2)
9078
{
9079
int old_size = Buf.size();
9080
va_list args;
9081
va_start(args, fmt);
9082
Buf.appendfv(fmt, args);
9083
va_end(args);
9084
for (int new_size = Buf.size(); old_size < new_size; old_size++)
9085
if (Buf[old_size] == '\n')
9086
LineOffsets.push_back(old_size + 1);
9087
}
9088
9089
void Draw(const char* title, bool* p_open = NULL)
9090
{
9091
if (!ImGui::Begin(title, p_open))
9092
{
9093
ImGui::End();
9094
return;
9095
}
9096
9097
// Options menu
9098
if (ImGui::BeginPopup("Options"))
9099
{
9100
ImGui::Checkbox("Auto-scroll", &AutoScroll);
9101
ImGui::EndPopup();
9102
}
9103
9104
// Main window
9105
if (ImGui::Button("Options"))
9106
ImGui::OpenPopup("Options");
9107
ImGui::SameLine();
9108
bool clear = ImGui::Button("Clear");
9109
ImGui::SameLine();
9110
bool copy = ImGui::Button("Copy");
9111
ImGui::SameLine();
9112
Filter.Draw("Filter", -100.0f);
9113
9114
ImGui::Separator();
9115
9116
if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))
9117
{
9118
if (clear)
9119
Clear();
9120
if (copy)
9121
ImGui::LogToClipboard();
9122
9123
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9124
const char* buf = Buf.begin();
9125
const char* buf_end = Buf.end();
9126
if (Filter.IsActive())
9127
{
9128
// In this example we don't use the clipper when Filter is enabled.
9129
// This is because we don't have random access to the result of our filter.
9130
// A real application processing logs with ten of thousands of entries may want to store the result of
9131
// search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
9132
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
9133
{
9134
const char* line_start = buf + LineOffsets[line_no];
9135
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9136
if (Filter.PassFilter(line_start, line_end))
9137
ImGui::TextUnformatted(line_start, line_end);
9138
}
9139
}
9140
else
9141
{
9142
// The simplest and easy way to display the entire buffer:
9143
// ImGui::TextUnformatted(buf_begin, buf_end);
9144
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
9145
// to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
9146
// within the visible area.
9147
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
9148
// on your side is recommended. Using ImGuiListClipper requires
9149
// - A) random access into your data
9150
// - B) items all being the same height,
9151
// both of which we can handle since we have an array pointing to the beginning of each line of text.
9152
// When using the filter (in the block of code above) we don't have random access into the data to display
9153
// anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
9154
// it possible (and would be recommended if you want to search through tens of thousands of entries).
9155
ImGuiListClipper clipper;
9156
clipper.Begin(LineOffsets.Size);
9157
while (clipper.Step())
9158
{
9159
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
9160
{
9161
const char* line_start = buf + LineOffsets[line_no];
9162
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9163
ImGui::TextUnformatted(line_start, line_end);
9164
}
9165
}
9166
clipper.End();
9167
}
9168
ImGui::PopStyleVar();
9169
9170
// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
9171
// Using a scrollbar or mouse-wheel will take away from the bottom edge.
9172
if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
9173
ImGui::SetScrollHereY(1.0f);
9174
}
9175
ImGui::EndChild();
9176
ImGui::End();
9177
}
9178
};
9179
9180
// Demonstrate creating a simple log window with basic filtering.
9181
static void ShowExampleAppLog(bool* p_open)
9182
{
9183
static ExampleAppLog log;
9184
9185
// For the demo: add a debug button _BEFORE_ the normal log window contents
9186
// We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.
9187
// Most of the contents of the window will be added by the log.Draw() call.
9188
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
9189
ImGui::Begin("Example: Log", p_open);
9190
IMGUI_DEMO_MARKER("Examples/Log");
9191
if (ImGui::SmallButton("[Debug] Add 5 entries"))
9192
{
9193
static int counter = 0;
9194
const char* categories[3] = { "info", "warn", "error" };
9195
const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
9196
for (int n = 0; n < 5; n++)
9197
{
9198
const char* category = categories[counter % IM_ARRAYSIZE(categories)];
9199
const char* word = words[counter % IM_ARRAYSIZE(words)];
9200
log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
9201
ImGui::GetFrameCount(), category, ImGui::GetTime(), word);
9202
counter++;
9203
}
9204
}
9205
ImGui::End();
9206
9207
// Actually call in the regular Log helper (which will Begin() into the same window as we just did)
9208
log.Draw("Example: Log", p_open);
9209
}
9210
9211
//-----------------------------------------------------------------------------
9212
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
9213
//-----------------------------------------------------------------------------
9214
9215
// Demonstrate create a window with multiple child windows.
9216
static void ShowExampleAppLayout(bool* p_open)
9217
{
9218
ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver);
9219
if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar))
9220
{
9221
IMGUI_DEMO_MARKER("Examples/Simple layout");
9222
if (ImGui::BeginMenuBar())
9223
{
9224
if (ImGui::BeginMenu("File"))
9225
{
9226
if (ImGui::MenuItem("Close", "Ctrl+W")) { *p_open = false; }
9227
ImGui::EndMenu();
9228
}
9229
ImGui::EndMenuBar();
9230
}
9231
9232
// Left
9233
static int selected = 0;
9234
{
9235
ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
9236
for (int i = 0; i < 100; i++)
9237
{
9238
// FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav
9239
char label[128];
9240
sprintf(label, "MyObject %d", i);
9241
if (ImGui::Selectable(label, selected == i))
9242
selected = i;
9243
}
9244
ImGui::EndChild();
9245
}
9246
ImGui::SameLine();
9247
9248
// Right
9249
{
9250
ImGui::BeginGroup();
9251
ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
9252
ImGui::Text("MyObject: %d", selected);
9253
ImGui::Separator();
9254
if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
9255
{
9256
if (ImGui::BeginTabItem("Description"))
9257
{
9258
ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ");
9259
ImGui::EndTabItem();
9260
}
9261
if (ImGui::BeginTabItem("Details"))
9262
{
9263
ImGui::Text("ID: 0123456789");
9264
ImGui::EndTabItem();
9265
}
9266
ImGui::EndTabBar();
9267
}
9268
ImGui::EndChild();
9269
if (ImGui::Button("Revert")) {}
9270
ImGui::SameLine();
9271
if (ImGui::Button("Save")) {}
9272
ImGui::EndGroup();
9273
}
9274
}
9275
ImGui::End();
9276
}
9277
9278
//-----------------------------------------------------------------------------
9279
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
9280
//-----------------------------------------------------------------------------
9281
// Some of the interactions are a bit lack-luster:
9282
// - We would want pressing validating or leaving the filter to somehow restore focus.
9283
// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.
9284
// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.
9285
//-----------------------------------------------------------------------------
9286
9287
struct ExampleAppPropertyEditor
9288
{
9289
ImGuiTextFilter Filter;
9290
ExampleTreeNode* VisibleNode = NULL;
9291
9292
void Draw(ExampleTreeNode* root_node)
9293
{
9294
// Left side: draw tree
9295
// - Currently using a table to benefit from RowBg feature
9296
if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
9297
{
9298
ImGui::SetNextItemWidth(-FLT_MIN);
9299
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
9300
ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
9301
if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll))
9302
Filter.Build();
9303
ImGui::PopItemFlag();
9304
9305
if (ImGui::BeginTable("##bg", 1, ImGuiTableFlags_RowBg))
9306
{
9307
for (ExampleTreeNode* node : root_node->Childs)
9308
if (Filter.PassFilter(node->Name)) // Filter root node
9309
DrawTreeNode(node);
9310
ImGui::EndTable();
9311
}
9312
}
9313
ImGui::EndChild();
9314
9315
// Right side: draw properties
9316
ImGui::SameLine();
9317
9318
ImGui::BeginGroup(); // Lock X position
9319
if (ExampleTreeNode* node = VisibleNode)
9320
{
9321
ImGui::Text("%s", node->Name);
9322
ImGui::TextDisabled("UID: 0x%08X", node->UID);
9323
ImGui::Separator();
9324
if (ImGui::BeginTable("##properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))
9325
{
9326
// Push object ID after we entered the table, so table is shared for all objects
9327
ImGui::PushID((int)node->UID);
9328
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
9329
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger
9330
if (node->HasData)
9331
{
9332
// In a typical application, the structure description would be derived from a data-driven system.
9333
// - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.
9334
// - Limits and some details are hard-coded to simplify the demo.
9335
for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)
9336
{
9337
ImGui::TableNextRow();
9338
ImGui::PushID(field_desc.Name);
9339
ImGui::TableNextColumn();
9340
ImGui::AlignTextToFramePadding();
9341
ImGui::TextUnformatted(field_desc.Name);
9342
ImGui::TableNextColumn();
9343
void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);
9344
switch (field_desc.DataType)
9345
{
9346
case ImGuiDataType_Bool:
9347
{
9348
IM_ASSERT(field_desc.DataCount == 1);
9349
ImGui::Checkbox("##Editor", (bool*)field_ptr);
9350
break;
9351
}
9352
case ImGuiDataType_S32:
9353
{
9354
int v_min = INT_MIN, v_max = INT_MAX;
9355
ImGui::SetNextItemWidth(-FLT_MIN);
9356
ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max);
9357
break;
9358
}
9359
case ImGuiDataType_Float:
9360
{
9361
float v_min = 0.0f, v_max = 1.0f;
9362
ImGui::SetNextItemWidth(-FLT_MIN);
9363
ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max);
9364
break;
9365
}
9366
case ImGuiDataType_String:
9367
{
9368
ImGui::InputText("##Editor", reinterpret_cast<char*>(field_ptr), 28);
9369
break;
9370
}
9371
}
9372
ImGui::PopID();
9373
}
9374
}
9375
ImGui::PopID();
9376
ImGui::EndTable();
9377
}
9378
}
9379
ImGui::EndGroup();
9380
}
9381
9382
void DrawTreeNode(ExampleTreeNode* node)
9383
{
9384
ImGui::TableNextRow();
9385
ImGui::TableNextColumn();
9386
ImGui::PushID(node->UID);
9387
ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
9388
tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;// Standard opening mode as we are likely to want to add selection afterwards
9389
tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support
9390
tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
9391
tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
9392
if (node == VisibleNode)
9393
tree_flags |= ImGuiTreeNodeFlags_Selected;
9394
if (node->Childs.Size == 0)
9395
tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
9396
if (node->DataMyBool == false)
9397
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
9398
bool node_open = ImGui::TreeNodeEx("", tree_flags, "%s", node->Name);
9399
if (node->DataMyBool == false)
9400
ImGui::PopStyleColor();
9401
if (ImGui::IsItemFocused())
9402
VisibleNode = node;
9403
if (node_open)
9404
{
9405
for (ExampleTreeNode* child : node->Childs)
9406
DrawTreeNode(child);
9407
ImGui::TreePop();
9408
}
9409
ImGui::PopID();
9410
}
9411
};
9412
9413
// Demonstrate creating a simple property editor.
9414
static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data)
9415
{
9416
ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);
9417
if (!ImGui::Begin("Example: Property editor", p_open))
9418
{
9419
ImGui::End();
9420
return;
9421
}
9422
9423
IMGUI_DEMO_MARKER("Examples/Property Editor");
9424
static ExampleAppPropertyEditor property_editor;
9425
if (demo_data->DemoTree == NULL)
9426
demo_data->DemoTree = ExampleTree_CreateDemoTree();
9427
property_editor.Draw(demo_data->DemoTree);
9428
9429
ImGui::End();
9430
}
9431
9432
//-----------------------------------------------------------------------------
9433
// [SECTION] Example App: Long Text / ShowExampleAppLongText()
9434
//-----------------------------------------------------------------------------
9435
9436
// Demonstrate/test rendering huge amount of text, and the incidence of clipping.
9437
static void ShowExampleAppLongText(bool* p_open)
9438
{
9439
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
9440
if (!ImGui::Begin("Example: Long text display", p_open))
9441
{
9442
ImGui::End();
9443
return;
9444
}
9445
IMGUI_DEMO_MARKER("Examples/Long text display");
9446
9447
static int test_type = 0;
9448
static ImGuiTextBuffer log;
9449
static int lines = 0;
9450
ImGui::Text("Printing unusually long amount of text.");
9451
ImGui::Combo("Test type", &test_type,
9452
"Single call to TextUnformatted()\0"
9453
"Multiple calls to Text(), clipped\0"
9454
"Multiple calls to Text(), not clipped (slow)\0");
9455
ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size());
9456
if (ImGui::Button("Clear")) { log.clear(); lines = 0; }
9457
ImGui::SameLine();
9458
if (ImGui::Button("Add 1000 lines"))
9459
{
9460
for (int i = 0; i < 1000; i++)
9461
log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i);
9462
lines += 1000;
9463
}
9464
ImGui::BeginChild("Log");
9465
switch (test_type)
9466
{
9467
case 0:
9468
// Single call to TextUnformatted() with a big buffer
9469
ImGui::TextUnformatted(log.begin(), log.end());
9470
break;
9471
case 1:
9472
{
9473
// Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
9474
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9475
ImGuiListClipper clipper;
9476
clipper.Begin(lines);
9477
while (clipper.Step())
9478
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9479
ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
9480
ImGui::PopStyleVar();
9481
break;
9482
}
9483
case 2:
9484
// Multiple calls to Text(), not clipped (slow)
9485
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9486
for (int i = 0; i < lines; i++)
9487
ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
9488
ImGui::PopStyleVar();
9489
break;
9490
}
9491
ImGui::EndChild();
9492
ImGui::End();
9493
}
9494
9495
//-----------------------------------------------------------------------------
9496
// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
9497
//-----------------------------------------------------------------------------
9498
9499
// Demonstrate creating a window which gets auto-resized according to its content.
9500
static void ShowExampleAppAutoResize(bool* p_open)
9501
{
9502
if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize))
9503
{
9504
ImGui::End();
9505
return;
9506
}
9507
IMGUI_DEMO_MARKER("Examples/Auto-resizing window");
9508
9509
static int lines = 10;
9510
ImGui::TextUnformatted(
9511
"Window will resize every-frame to the size of its content.\n"
9512
"Note that you probably don't want to query the window size to\n"
9513
"output your content because that would create a feedback loop.");
9514
ImGui::SliderInt("Number of lines", &lines, 1, 20);
9515
for (int i = 0; i < lines; i++)
9516
ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally
9517
ImGui::End();
9518
}
9519
9520
//-----------------------------------------------------------------------------
9521
// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
9522
//-----------------------------------------------------------------------------
9523
9524
// Demonstrate creating a window with custom resize constraints.
9525
// Note that size constraints currently don't work on a docked window (when in 'docking' branch)
9526
static void ShowExampleAppConstrainedResize(bool* p_open)
9527
{
9528
struct CustomConstraints
9529
{
9530
// Helper functions to demonstrate programmatic constraints
9531
// FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.
9532
// FIXME: None of the three demos works consistently when resizing from borders.
9533
static void AspectRatio(ImGuiSizeCallbackData* data)
9534
{
9535
float aspect_ratio = *(float*)data->UserData;
9536
data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio);
9537
}
9538
static void Square(ImGuiSizeCallbackData* data)
9539
{
9540
data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);
9541
}
9542
static void Step(ImGuiSizeCallbackData* data)
9543
{
9544
float step = *(float*)data->UserData;
9545
data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step);
9546
}
9547
};
9548
9549
const char* test_desc[] =
9550
{
9551
"Between 100x100 and 500x500",
9552
"At least 100x100",
9553
"Resize vertical + lock current width",
9554
"Resize horizontal + lock current height",
9555
"Width Between 400 and 500",
9556
"Height at least 400",
9557
"Custom: Aspect Ratio 16:9",
9558
"Custom: Always Square",
9559
"Custom: Fixed Steps (100)",
9560
};
9561
9562
// Options
9563
static bool auto_resize = false;
9564
static bool window_padding = true;
9565
static int type = 6; // Aspect Ratio
9566
static int display_lines = 10;
9567
9568
// Submit constraint
9569
float aspect_ratio = 16.0f / 9.0f;
9570
float fixed_step = 100.0f;
9571
if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500)); // Between 100x100 and 500x500
9572
if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100
9573
if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width
9574
if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height
9575
if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width Between and 400 and 500
9576
if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, FLT_MAX)); // Height at least 400
9577
if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio); // Aspect ratio
9578
if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square
9579
if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step); // Fixed Step
9580
9581
// Submit window
9582
if (!window_padding)
9583
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
9584
const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;
9585
const bool window_open = ImGui::Begin("Example: Constrained Resize", p_open, window_flags);
9586
if (!window_padding)
9587
ImGui::PopStyleVar();
9588
if (window_open)
9589
{
9590
IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
9591
if (ImGui::GetIO().KeyShift)
9592
{
9593
// Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture.
9594
ImVec2 avail_size = ImGui::GetContentRegionAvail();
9595
ImVec2 pos = ImGui::GetCursorScreenPos();
9596
ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);
9597
ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10));
9598
ImGui::Text("%.2f x %.2f", avail_size.x, avail_size.y);
9599
}
9600
else
9601
{
9602
ImGui::Text("(Hold SHIFT to display a dummy viewport)");
9603
if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine();
9604
if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine();
9605
if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); }
9606
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9607
ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc));
9608
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9609
ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100);
9610
ImGui::Checkbox("Auto-resize", &auto_resize);
9611
ImGui::Checkbox("Window padding", &window_padding);
9612
for (int i = 0; i < display_lines; i++)
9613
ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, "");
9614
}
9615
}
9616
ImGui::End();
9617
}
9618
9619
//-----------------------------------------------------------------------------
9620
// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
9621
//-----------------------------------------------------------------------------
9622
9623
// Demonstrate creating a simple static window with no decoration
9624
// + a context-menu to choose which corner of the screen to use.
9625
static void ShowExampleAppSimpleOverlay(bool* p_open)
9626
{
9627
static int location = 0;
9628
ImGuiIO& io = ImGui::GetIO();
9629
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
9630
if (location >= 0)
9631
{
9632
const float PAD = 10.0f;
9633
const ImGuiViewport* viewport = ImGui::GetMainViewport();
9634
ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
9635
ImVec2 work_size = viewport->WorkSize;
9636
ImVec2 window_pos, window_pos_pivot;
9637
window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);
9638
window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);
9639
window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f;
9640
window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f;
9641
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
9642
window_flags |= ImGuiWindowFlags_NoMove;
9643
}
9644
else if (location == -2)
9645
{
9646
// Center window
9647
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9648
window_flags |= ImGuiWindowFlags_NoMove;
9649
}
9650
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
9651
if (ImGui::Begin("Example: Simple overlay", p_open, window_flags))
9652
{
9653
IMGUI_DEMO_MARKER("Examples/Simple Overlay");
9654
ImGui::Text("Simple overlay\n" "(right-click to change position)");
9655
ImGui::Separator();
9656
if (ImGui::IsMousePosValid())
9657
ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
9658
else
9659
ImGui::Text("Mouse Position: <invalid>");
9660
if (ImGui::BeginPopupContextWindow())
9661
{
9662
if (ImGui::MenuItem("Custom", NULL, location == -1)) location = -1;
9663
if (ImGui::MenuItem("Center", NULL, location == -2)) location = -2;
9664
if (ImGui::MenuItem("Top-left", NULL, location == 0)) location = 0;
9665
if (ImGui::MenuItem("Top-right", NULL, location == 1)) location = 1;
9666
if (ImGui::MenuItem("Bottom-left", NULL, location == 2)) location = 2;
9667
if (ImGui::MenuItem("Bottom-right", NULL, location == 3)) location = 3;
9668
if (p_open && ImGui::MenuItem("Close")) *p_open = false;
9669
ImGui::EndPopup();
9670
}
9671
}
9672
ImGui::End();
9673
}
9674
9675
//-----------------------------------------------------------------------------
9676
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
9677
//-----------------------------------------------------------------------------
9678
9679
// Demonstrate creating a window covering the entire screen/viewport
9680
static void ShowExampleAppFullscreen(bool* p_open)
9681
{
9682
static bool use_work_area = true;
9683
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
9684
9685
// We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
9686
// Based on your use case you may want one or the other.
9687
const ImGuiViewport* viewport = ImGui::GetMainViewport();
9688
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
9689
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
9690
9691
if (ImGui::Begin("Example: Fullscreen window", p_open, flags))
9692
{
9693
ImGui::Checkbox("Use work area instead of main area", &use_work_area);
9694
ImGui::SameLine();
9695
HelpMarker("Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference.");
9696
9697
ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground);
9698
ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration);
9699
ImGui::Indent();
9700
ImGui::CheckboxFlags("ImGuiWindowFlags_NoTitleBar", &flags, ImGuiWindowFlags_NoTitleBar);
9701
ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse);
9702
ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar);
9703
ImGui::Unindent();
9704
9705
if (p_open && ImGui::Button("Close this window"))
9706
*p_open = false;
9707
}
9708
ImGui::End();
9709
}
9710
9711
//-----------------------------------------------------------------------------
9712
// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()
9713
//-----------------------------------------------------------------------------
9714
9715
// Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation.
9716
// This applies to all regular items as well.
9717
// Read FAQ section "How can I have multiple widgets with the same label?" for details.
9718
static void ShowExampleAppWindowTitles(bool*)
9719
{
9720
const ImGuiViewport* viewport = ImGui::GetMainViewport();
9721
const ImVec2 base_pos = viewport->Pos;
9722
9723
// By default, Windows are uniquely identified by their title.
9724
// You can use the "##" and "###" markers to manipulate the display/ID.
9725
9726
// Using "##" to display same title but have unique identifier.
9727
ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver);
9728
ImGui::Begin("Same title as another window##1");
9729
IMGUI_DEMO_MARKER("Examples/Manipulating window titles");
9730
ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique.");
9731
ImGui::End();
9732
9733
ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver);
9734
ImGui::Begin("Same title as another window##2");
9735
ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique.");
9736
ImGui::End();
9737
9738
// Using "###" to display a changing title but keep a static identifier "AnimatedTitle"
9739
char buf[128];
9740
sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount());
9741
ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver);
9742
ImGui::Begin(buf);
9743
ImGui::Text("This window has a changing title.");
9744
ImGui::End();
9745
}
9746
9747
//-----------------------------------------------------------------------------
9748
// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
9749
//-----------------------------------------------------------------------------
9750
9751
// Add a |_| looking shape
9752
static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz)
9753
{
9754
const ImVec2 pos_norms[] = { { 0.0f, 0.0f }, { 0.3f, 0.0f }, { 0.3f, 0.7f }, { 0.7f, 0.7f }, { 0.7f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } };
9755
for (const ImVec2& p : pos_norms)
9756
draw_list->PathLineTo(ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y)));
9757
}
9758
9759
// Demonstrate using the low-level ImDrawList to draw custom shapes.
9760
static void ShowExampleAppCustomRendering(bool* p_open)
9761
{
9762
if (!ImGui::Begin("Example: Custom rendering", p_open))
9763
{
9764
ImGui::End();
9765
return;
9766
}
9767
IMGUI_DEMO_MARKER("Examples/Custom Rendering");
9768
9769
// Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
9770
// overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
9771
// types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
9772
// exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
9773
9774
if (ImGui::BeginTabBar("##TabBar"))
9775
{
9776
if (ImGui::BeginTabItem("Primitives"))
9777
{
9778
ImGui::PushItemWidth(-ImGui::GetFontSize() * 15);
9779
ImDrawList* draw_list = ImGui::GetWindowDrawList();
9780
9781
// Draw gradients
9782
// (note that those are currently exacerbating our sRGB/Linear issues)
9783
// Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..
9784
ImGui::Text("Gradients");
9785
ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
9786
{
9787
ImVec2 p0 = ImGui::GetCursorScreenPos();
9788
ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9789
ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));
9790
ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));
9791
draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
9792
ImGui::InvisibleButton("##gradient1", gradient_size);
9793
}
9794
{
9795
ImVec2 p0 = ImGui::GetCursorScreenPos();
9796
ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9797
ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));
9798
ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));
9799
draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
9800
ImGui::InvisibleButton("##gradient2", gradient_size);
9801
}
9802
9803
// Draw a bunch of primitives
9804
ImGui::Text("All primitives");
9805
static float sz = 36.0f;
9806
static float thickness = 3.0f;
9807
static int ngon_sides = 6;
9808
static bool circle_segments_override = false;
9809
static int circle_segments_override_v = 12;
9810
static bool curve_segments_override = false;
9811
static int curve_segments_override_v = 8;
9812
static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
9813
ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 100.0f, "%.0f");
9814
ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f");
9815
ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12);
9816
ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override);
9817
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
9818
circle_segments_override |= ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40);
9819
ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override);
9820
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
9821
curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40);
9822
ImGui::ColorEdit4("Color", &colf.x);
9823
9824
const ImVec2 p = ImGui::GetCursorScreenPos();
9825
const ImU32 col = ImColor(colf);
9826
const float spacing = 10.0f;
9827
const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;
9828
const float rounding = sz / 5.0f;
9829
const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;
9830
const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;
9831
const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves
9832
const ImVec2 cp4[4] = { ImVec2(0.0f, 0.0f), ImVec2(sz * 1.3f, sz * 0.3f), ImVec2(sz - sz * 1.3f, sz - sz * 0.3f), ImVec2(sz, sz) };
9833
9834
float x = p.x + 4.0f;
9835
float y = p.y + 4.0f;
9836
for (int n = 0; n < 2; n++)
9837
{
9838
// First line uses a thickness of 1.0f, second line uses the configurable thickness
9839
float th = (n == 0) ? 1.0f : thickness;
9840
draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon
9841
draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle
9842
draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse
9843
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square
9844
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners
9845
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners
9846
draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle
9847
//draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle
9848
PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape
9849
//draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th);
9850
draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
9851
draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
9852
draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line
9853
9854
// Path
9855
draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f);
9856
draw_list->PathStroke(col, ImDrawFlags_None, th);
9857
x += sz + spacing;
9858
9859
// Quadratic Bezier Curve (3 control points)
9860
draw_list->AddBezierQuadratic(ImVec2(x + cp3[0].x, y + cp3[0].y), ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), col, th, curve_segments);
9861
x += sz + spacing;
9862
9863
// Cubic Bezier Curve (4 control points)
9864
draw_list->AddBezierCubic(ImVec2(x + cp4[0].x, y + cp4[0].y), ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), col, th, curve_segments);
9865
9866
x = p.x + 4;
9867
y += sz + spacing;
9868
}
9869
9870
// Filled shapes
9871
draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides); x += sz + spacing; // N-gon
9872
draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments); x += sz + spacing; // Circle
9873
draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), ImVec2(sz * 0.5f, sz * 0.3f), col, -0.3f, circle_segments); x += sz + spacing;// Ellipse
9874
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square
9875
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners
9876
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners
9877
draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle
9878
//draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle
9879
PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col); x += sz + spacing; // Concave shape
9880
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness)
9881
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness)
9882
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine)
9883
9884
// Path
9885
draw_list->PathArcTo(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, 3.141592f * -0.5f, 3.141592f);
9886
draw_list->PathFillConvex(col);
9887
x += sz + spacing;
9888
9889
// Quadratic Bezier Curve (3 control points)
9890
draw_list->PathLineTo(ImVec2(x + cp3[0].x, y + cp3[0].y));
9891
draw_list->PathBezierQuadraticCurveTo(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), curve_segments);
9892
draw_list->PathFillConvex(col);
9893
x += sz + spacing;
9894
9895
draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255));
9896
x += sz + spacing;
9897
9898
ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f));
9899
ImGui::PopItemWidth();
9900
ImGui::EndTabItem();
9901
}
9902
9903
if (ImGui::BeginTabItem("Canvas"))
9904
{
9905
static ImVector<ImVec2> points;
9906
static ImVec2 scrolling(0.0f, 0.0f);
9907
static bool opt_enable_grid = true;
9908
static bool opt_enable_context_menu = true;
9909
static bool adding_line = false;
9910
9911
ImGui::Checkbox("Enable grid", &opt_enable_grid);
9912
ImGui::Checkbox("Enable context menu", &opt_enable_context_menu);
9913
ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
9914
9915
// Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
9916
// Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
9917
// To use a child window instead we could use, e.g:
9918
// ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
9919
// ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
9920
// ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);
9921
// ImGui::PopStyleColor();
9922
// ImGui::PopStyleVar();
9923
// [...]
9924
// ImGui::EndChild();
9925
9926
// Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
9927
ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
9928
ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
9929
if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
9930
if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
9931
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
9932
9933
// Draw border and background color
9934
ImGuiIO& io = ImGui::GetIO();
9935
ImDrawList* draw_list = ImGui::GetWindowDrawList();
9936
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));
9937
draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));
9938
9939
// This will catch our interactions
9940
ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
9941
const bool is_hovered = ImGui::IsItemHovered(); // Hovered
9942
const bool is_active = ImGui::IsItemActive(); // Held
9943
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
9944
const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
9945
9946
// Add first and second point
9947
if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
9948
{
9949
points.push_back(mouse_pos_in_canvas);
9950
points.push_back(mouse_pos_in_canvas);
9951
adding_line = true;
9952
}
9953
if (adding_line)
9954
{
9955
points.back() = mouse_pos_in_canvas;
9956
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
9957
adding_line = false;
9958
}
9959
9960
// Pan (we use a zero mouse threshold when there's no context menu)
9961
// You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
9962
const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;
9963
if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan))
9964
{
9965
scrolling.x += io.MouseDelta.x;
9966
scrolling.y += io.MouseDelta.y;
9967
}
9968
9969
// Context menu (under default mouse threshold)
9970
ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
9971
if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
9972
ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
9973
if (ImGui::BeginPopup("context"))
9974
{
9975
if (adding_line)
9976
points.resize(points.size() - 2);
9977
adding_line = false;
9978
if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); }
9979
if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0)) { points.clear(); }
9980
ImGui::EndPopup();
9981
}
9982
9983
// Draw grid + all lines in the canvas
9984
draw_list->PushClipRect(canvas_p0, canvas_p1, true);
9985
if (opt_enable_grid)
9986
{
9987
const float GRID_STEP = 64.0f;
9988
for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
9989
draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));
9990
for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
9991
draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));
9992
}
9993
for (int n = 0; n < points.Size; n += 2)
9994
draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
9995
draw_list->PopClipRect();
9996
9997
ImGui::EndTabItem();
9998
}
9999
10000
if (ImGui::BeginTabItem("BG/FG draw lists"))
10001
{
10002
static bool draw_bg = true;
10003
static bool draw_fg = true;
10004
ImGui::Checkbox("Draw in Background draw list", &draw_bg);
10005
ImGui::SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows.");
10006
ImGui::Checkbox("Draw in Foreground draw list", &draw_fg);
10007
ImGui::SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows.");
10008
ImVec2 window_pos = ImGui::GetWindowPos();
10009
ImVec2 window_size = ImGui::GetWindowSize();
10010
ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);
10011
if (draw_bg)
10012
ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4);
10013
if (draw_fg)
10014
ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10);
10015
ImGui::EndTabItem();
10016
}
10017
10018
// Demonstrate out-of-order rendering via channels splitting
10019
// We use functions in ImDrawList as each draw list contains a convenience splitter,
10020
// but you can also instantiate your own ImDrawListSplitter if you need to nest them.
10021
if (ImGui::BeginTabItem("Draw Channels"))
10022
{
10023
ImDrawList* draw_list = ImGui::GetWindowDrawList();
10024
{
10025
ImGui::Text("Blue shape is drawn first: appears in back");
10026
ImGui::Text("Red shape is drawn after: appears in front");
10027
ImVec2 p0 = ImGui::GetCursorScreenPos();
10028
draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10029
draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10030
ImGui::Dummy(ImVec2(75, 75));
10031
}
10032
ImGui::Separator();
10033
{
10034
ImGui::Text("Blue shape is drawn first, into channel 1: appears in front");
10035
ImGui::Text("Red shape is drawn after, into channel 0: appears in back");
10036
ImVec2 p1 = ImGui::GetCursorScreenPos();
10037
10038
// Create 2 channels and draw a Blue shape THEN a Red shape.
10039
// You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.
10040
draw_list->ChannelsSplit(2);
10041
draw_list->ChannelsSetCurrent(1);
10042
draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10043
draw_list->ChannelsSetCurrent(0);
10044
draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10045
10046
// Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.
10047
// This works by copying draw indices only (vertices are not copied).
10048
draw_list->ChannelsMerge();
10049
ImGui::Dummy(ImVec2(75, 75));
10050
ImGui::Text("After reordering, contents of channel 0 appears below channel 1.");
10051
}
10052
ImGui::EndTabItem();
10053
}
10054
10055
ImGui::EndTabBar();
10056
}
10057
10058
ImGui::End();
10059
}
10060
10061
//-----------------------------------------------------------------------------
10062
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
10063
//-----------------------------------------------------------------------------
10064
10065
// Simplified structure to mimic a Document model
10066
struct MyDocument
10067
{
10068
char Name[32]; // Document title
10069
int UID; // Unique ID (necessary as we can change title)
10070
bool Open; // Set when open (we keep an array of all available documents to simplify demo code!)
10071
bool OpenPrev; // Copy of Open from last update.
10072
bool Dirty; // Set when the document has been modified
10073
ImVec4 Color; // An arbitrary variable associated to the document
10074
10075
MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
10076
{
10077
UID = uid;
10078
snprintf(Name, sizeof(Name), "%s", name);
10079
Open = OpenPrev = open;
10080
Dirty = false;
10081
Color = color;
10082
}
10083
void DoOpen() { Open = true; }
10084
void DoForceClose() { Open = false; Dirty = false; }
10085
void DoSave() { Dirty = false; }
10086
};
10087
10088
struct ExampleAppDocuments
10089
{
10090
ImVector<MyDocument> Documents;
10091
ImVector<MyDocument*> CloseQueue;
10092
MyDocument* RenamingDoc = NULL;
10093
bool RenamingStarted = false;
10094
10095
ExampleAppDocuments()
10096
{
10097
Documents.push_back(MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
10098
Documents.push_back(MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
10099
Documents.push_back(MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
10100
Documents.push_back(MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));
10101
Documents.push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f)));
10102
Documents.push_back(MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f)));
10103
}
10104
10105
// As we allow to change document name, we append a never-changing document ID so tabs are stable
10106
void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size)
10107
{
10108
snprintf(out_buf, out_buf_size, "%s###doc%d", doc->Name, doc->UID);
10109
}
10110
10111
// Display placeholder contents for the Document
10112
void DisplayDocContents(MyDocument* doc)
10113
{
10114
ImGui::PushID(doc);
10115
ImGui::Text("Document \"%s\"", doc->Name);
10116
ImGui::PushStyleColor(ImGuiCol_Text, doc->Color);
10117
ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
10118
ImGui::PopStyleColor();
10119
10120
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip);
10121
if (ImGui::Button("Rename.."))
10122
{
10123
RenamingDoc = doc;
10124
RenamingStarted = true;
10125
}
10126
ImGui::SameLine();
10127
10128
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip);
10129
if (ImGui::Button("Modify"))
10130
doc->Dirty = true;
10131
10132
ImGui::SameLine();
10133
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip);
10134
if (ImGui::Button("Save"))
10135
doc->DoSave();
10136
10137
ImGui::SameLine();
10138
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip);
10139
if (ImGui::Button("Close"))
10140
CloseQueue.push_back(doc);
10141
ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior.
10142
ImGui::PopID();
10143
}
10144
10145
// Display context menu for the Document
10146
void DisplayDocContextMenu(MyDocument* doc)
10147
{
10148
if (!ImGui::BeginPopupContextItem())
10149
return;
10150
10151
char buf[256];
10152
sprintf(buf, "Save %s", doc->Name);
10153
if (ImGui::MenuItem(buf, "Ctrl+S", false, doc->Open))
10154
doc->DoSave();
10155
if (ImGui::MenuItem("Rename...", "Ctrl+R", false, doc->Open))
10156
RenamingDoc = doc;
10157
if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open))
10158
CloseQueue.push_back(doc);
10159
ImGui::EndPopup();
10160
}
10161
10162
// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
10163
// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
10164
// as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
10165
// the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has
10166
// disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
10167
// give the impression of a flicker for one frame.
10168
// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
10169
// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
10170
void NotifyOfDocumentsClosedElsewhere()
10171
{
10172
for (MyDocument& doc : Documents)
10173
{
10174
if (!doc.Open && doc.OpenPrev)
10175
ImGui::SetTabItemClosed(doc.Name);
10176
doc.OpenPrev = doc.Open;
10177
}
10178
}
10179
};
10180
10181
void ShowExampleAppDocuments(bool* p_open)
10182
{
10183
static ExampleAppDocuments app;
10184
10185
// Options
10186
static bool opt_reorderable = true;
10187
static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;
10188
10189
bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar);
10190
if (!window_contents_visible)
10191
{
10192
ImGui::End();
10193
return;
10194
}
10195
10196
// Menu
10197
if (ImGui::BeginMenuBar())
10198
{
10199
if (ImGui::BeginMenu("File"))
10200
{
10201
int open_count = 0;
10202
for (MyDocument& doc : app.Documents)
10203
open_count += doc.Open ? 1 : 0;
10204
10205
if (ImGui::BeginMenu("Open", open_count < app.Documents.Size))
10206
{
10207
for (MyDocument& doc : app.Documents)
10208
if (!doc.Open && ImGui::MenuItem(doc.Name))
10209
doc.DoOpen();
10210
ImGui::EndMenu();
10211
}
10212
if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0))
10213
for (MyDocument& doc : app.Documents)
10214
app.CloseQueue.push_back(&doc);
10215
if (ImGui::MenuItem("Exit") && p_open)
10216
*p_open = false;
10217
ImGui::EndMenu();
10218
}
10219
ImGui::EndMenuBar();
10220
}
10221
10222
// [Debug] List documents with one checkbox for each
10223
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
10224
{
10225
MyDocument& doc = app.Documents[doc_n];
10226
if (doc_n > 0)
10227
ImGui::SameLine();
10228
ImGui::PushID(&doc);
10229
if (ImGui::Checkbox(doc.Name, &doc.Open))
10230
if (!doc.Open)
10231
doc.DoForceClose();
10232
ImGui::PopID();
10233
}
10234
10235
ImGui::Separator();
10236
10237
// About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.
10238
// They have multiple effects:
10239
// - Display a dot next to the title.
10240
// - Tab is selected when clicking the X close button.
10241
// - Closure is not assumed (will wait for user to stop submitting the tab).
10242
// Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
10243
// We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty
10244
// hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.
10245
// The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
10246
10247
// Submit Tab Bar and Tabs
10248
{
10249
ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);
10250
tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;
10251
if (ImGui::BeginTabBar("##tabs", tab_bar_flags))
10252
{
10253
if (opt_reorderable)
10254
app.NotifyOfDocumentsClosedElsewhere();
10255
10256
// [DEBUG] Stress tests
10257
//if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.
10258
//if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..
10259
10260
// Submit Tabs
10261
for (MyDocument& doc : app.Documents)
10262
{
10263
if (!doc.Open)
10264
continue;
10265
10266
// As we allow to change document name, we append a never-changing document id so tabs are stable
10267
char doc_name_buf[64];
10268
app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf));
10269
ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);
10270
bool visible = ImGui::BeginTabItem(doc_name_buf, &doc.Open, tab_flags);
10271
10272
// Cancel attempt to close when unsaved add to save queue so we can display a popup.
10273
if (!doc.Open && doc.Dirty)
10274
{
10275
doc.Open = true;
10276
app.CloseQueue.push_back(&doc);
10277
}
10278
10279
app.DisplayDocContextMenu(&doc);
10280
if (visible)
10281
{
10282
app.DisplayDocContents(&doc);
10283
ImGui::EndTabItem();
10284
}
10285
}
10286
10287
ImGui::EndTabBar();
10288
}
10289
}
10290
10291
// Display renaming UI
10292
if (app.RenamingDoc != NULL)
10293
{
10294
if (app.RenamingStarted)
10295
ImGui::OpenPopup("Rename");
10296
if (ImGui::BeginPopup("Rename"))
10297
{
10298
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30);
10299
if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue))
10300
{
10301
ImGui::CloseCurrentPopup();
10302
app.RenamingDoc = NULL;
10303
}
10304
if (app.RenamingStarted)
10305
ImGui::SetKeyboardFocusHere(-1);
10306
ImGui::EndPopup();
10307
}
10308
else
10309
{
10310
app.RenamingDoc = NULL;
10311
}
10312
app.RenamingStarted = false;
10313
}
10314
10315
// Display closing confirmation UI
10316
if (!app.CloseQueue.empty())
10317
{
10318
int close_queue_unsaved_documents = 0;
10319
for (int n = 0; n < app.CloseQueue.Size; n++)
10320
if (app.CloseQueue[n]->Dirty)
10321
close_queue_unsaved_documents++;
10322
10323
if (close_queue_unsaved_documents == 0)
10324
{
10325
// Close documents when all are unsaved
10326
for (int n = 0; n < app.CloseQueue.Size; n++)
10327
app.CloseQueue[n]->DoForceClose();
10328
app.CloseQueue.clear();
10329
}
10330
else
10331
{
10332
if (!ImGui::IsPopupOpen("Save?"))
10333
ImGui::OpenPopup("Save?");
10334
if (ImGui::BeginPopupModal("Save?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
10335
{
10336
ImGui::Text("Save change to the following items?");
10337
float item_height = ImGui::GetTextLineHeightWithSpacing();
10338
if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle))
10339
for (MyDocument* doc : app.CloseQueue)
10340
if (doc->Dirty)
10341
ImGui::Text("%s", doc->Name);
10342
ImGui::EndChild();
10343
10344
ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
10345
if (ImGui::Button("Yes", button_size))
10346
{
10347
for (MyDocument* doc : app.CloseQueue)
10348
{
10349
if (doc->Dirty)
10350
doc->DoSave();
10351
doc->DoForceClose();
10352
}
10353
app.CloseQueue.clear();
10354
ImGui::CloseCurrentPopup();
10355
}
10356
ImGui::SameLine();
10357
if (ImGui::Button("No", button_size))
10358
{
10359
for (MyDocument* doc : app.CloseQueue)
10360
doc->DoForceClose();
10361
app.CloseQueue.clear();
10362
ImGui::CloseCurrentPopup();
10363
}
10364
ImGui::SameLine();
10365
if (ImGui::Button("Cancel", button_size))
10366
{
10367
app.CloseQueue.clear();
10368
ImGui::CloseCurrentPopup();
10369
}
10370
ImGui::EndPopup();
10371
}
10372
}
10373
}
10374
10375
ImGui::End();
10376
}
10377
10378
//-----------------------------------------------------------------------------
10379
// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
10380
//-----------------------------------------------------------------------------
10381
10382
//#include "imgui_internal.h" // NavMoveRequestTryWrapping()
10383
10384
struct ExampleAsset
10385
{
10386
ImGuiID ID;
10387
int Type;
10388
10389
ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; }
10390
10391
static const ImGuiTableSortSpecs* s_current_sort_specs;
10392
10393
static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count)
10394
{
10395
s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
10396
if (items_count > 1)
10397
qsort(items, (size_t)items_count, sizeof(items[0]), ExampleAsset::CompareWithSortSpecs);
10398
s_current_sort_specs = NULL;
10399
}
10400
10401
// Compare function to be used by qsort()
10402
static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
10403
{
10404
const ExampleAsset* a = (const ExampleAsset*)lhs;
10405
const ExampleAsset* b = (const ExampleAsset*)rhs;
10406
for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
10407
{
10408
const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
10409
int delta = 0;
10410
if (sort_spec->ColumnIndex == 0)
10411
delta = ((int)a->ID - (int)b->ID);
10412
else if (sort_spec->ColumnIndex == 1)
10413
delta = (a->Type - b->Type);
10414
if (delta > 0)
10415
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
10416
if (delta < 0)
10417
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
10418
}
10419
return ((int)a->ID - (int)b->ID);
10420
}
10421
};
10422
const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL;
10423
10424
struct ExampleAssetsBrowser
10425
{
10426
// Options
10427
bool ShowTypeOverlay = true;
10428
bool AllowSorting = true;
10429
bool AllowDragUnselected = false;
10430
bool AllowBoxSelect = true;
10431
float IconSize = 32.0f;
10432
int IconSpacing = 10;
10433
int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer.
10434
bool StretchSpacing = true;
10435
10436
// State
10437
ImVector<ExampleAsset> Items; // Our items
10438
ExampleSelectionWithDeletion Selection; // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion)
10439
ImGuiID NextItemId = 0; // Unique identifier when creating new items
10440
bool RequestDelete = false; // Deferred deletion request
10441
bool RequestSort = false; // Deferred sort request
10442
float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better
10443
10444
// Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way.
10445
ImVec2 LayoutItemSize;
10446
ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing
10447
float LayoutItemSpacing = 0.0f;
10448
float LayoutSelectableSpacing = 0.0f;
10449
float LayoutOuterPadding = 0.0f;
10450
int LayoutColumnCount = 0;
10451
int LayoutLineCount = 0;
10452
10453
// Functions
10454
ExampleAssetsBrowser()
10455
{
10456
AddItems(10000);
10457
}
10458
void AddItems(int count)
10459
{
10460
if (Items.Size == 0)
10461
NextItemId = 0;
10462
Items.reserve(Items.Size + count);
10463
for (int n = 0; n < count; n++, NextItemId++)
10464
Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2));
10465
RequestSort = true;
10466
}
10467
void ClearItems()
10468
{
10469
Items.clear();
10470
Selection.Clear();
10471
}
10472
10473
// Logic would be written in the main code BeginChild() and outputting to local variables.
10474
// We extracted it into a function so we can call it easily from multiple places.
10475
void UpdateLayoutSizes(float avail_width)
10476
{
10477
// Layout: when not stretching: allow extending into right-most spacing.
10478
LayoutItemSpacing = (float)IconSpacing;
10479
if (StretchSpacing == false)
10480
avail_width += floorf(LayoutItemSpacing * 0.5f);
10481
10482
// Layout: calculate number of icon per line and number of lines
10483
LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize));
10484
LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);
10485
LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;
10486
10487
// Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.
10488
if (StretchSpacing && LayoutColumnCount > 1)
10489
LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount;
10490
10491
LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing);
10492
LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);
10493
LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f);
10494
}
10495
10496
void Draw(const char* title, bool* p_open)
10497
{
10498
ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver);
10499
if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar))
10500
{
10501
ImGui::End();
10502
return;
10503
}
10504
10505
// Menu bar
10506
if (ImGui::BeginMenuBar())
10507
{
10508
if (ImGui::BeginMenu("File"))
10509
{
10510
if (ImGui::MenuItem("Add 10000 items"))
10511
AddItems(10000);
10512
if (ImGui::MenuItem("Clear items"))
10513
ClearItems();
10514
ImGui::Separator();
10515
if (ImGui::MenuItem("Close", NULL, false, p_open != NULL))
10516
*p_open = false;
10517
ImGui::EndMenu();
10518
}
10519
if (ImGui::BeginMenu("Edit"))
10520
{
10521
if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0))
10522
RequestDelete = true;
10523
ImGui::EndMenu();
10524
}
10525
if (ImGui::BeginMenu("Options"))
10526
{
10527
ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
10528
10529
ImGui::SeparatorText("Contents");
10530
ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay);
10531
ImGui::Checkbox("Allow Sorting", &AllowSorting);
10532
10533
ImGui::SeparatorText("Selection Behavior");
10534
ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected);
10535
ImGui::Checkbox("Allow box-selection", &AllowBoxSelect);
10536
10537
ImGui::SeparatorText("Layout");
10538
ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f");
10539
ImGui::SameLine(); HelpMarker("Use CTRL+Wheel to zoom");
10540
ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32);
10541
ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32);
10542
ImGui::Checkbox("Stretch Spacing", &StretchSpacing);
10543
ImGui::PopItemWidth();
10544
ImGui::EndMenu();
10545
}
10546
ImGui::EndMenuBar();
10547
}
10548
10549
// Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI
10550
if (AllowSorting)
10551
{
10552
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
10553
ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders;
10554
if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight())))
10555
{
10556
ImGui::TableSetupColumn("Index");
10557
ImGui::TableSetupColumn("Type");
10558
ImGui::TableHeadersRow();
10559
if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
10560
if (sort_specs->SpecsDirty || RequestSort)
10561
{
10562
ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size);
10563
sort_specs->SpecsDirty = RequestSort = false;
10564
}
10565
ImGui::EndTable();
10566
}
10567
ImGui::PopStyleVar();
10568
}
10569
10570
ImGuiIO& io = ImGui::GetIO();
10571
ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));
10572
if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove))
10573
{
10574
ImDrawList* draw_list = ImGui::GetWindowDrawList();
10575
10576
const float avail_width = ImGui::GetContentRegionAvail().x;
10577
UpdateLayoutSizes(avail_width);
10578
10579
// Calculate and store start position.
10580
ImVec2 start_pos = ImGui::GetCursorScreenPos();
10581
start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding);
10582
ImGui::SetCursorScreenPos(start_pos);
10583
10584
// Multi-select
10585
ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid;
10586
10587
// - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items)
10588
if (AllowBoxSelect)
10589
ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d;
10590
10591
// - This feature allows dragging an unselected item without selecting it (rarely used)
10592
if (AllowDragUnselected)
10593
ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease;
10594
10595
// - Enable keyboard wrapping on X axis
10596
// (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing:
10597
// ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
10598
// When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system)
10599
ms_flags |= ImGuiMultiSelectFlags_NavWrapX;
10600
10601
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size);
10602
10603
// Use custom selection adapter: store ID in selection (recommended)
10604
Selection.UserData = this;
10605
Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; };
10606
Selection.ApplyRequests(ms_io);
10607
10608
const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete;
10609
const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1;
10610
RequestDelete = false;
10611
10612
// Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items)
10613
// Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...
10614
// But it is necessary for two reasons:
10615
// - Selectables uses it by default to visually fill the space between two items.
10616
// - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do).
10617
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing));
10618
10619
// Rendering parameters
10620
const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) };
10621
const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220));
10622
const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f);
10623
const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x);
10624
10625
const int column_count = LayoutColumnCount;
10626
ImGuiListClipper clipper;
10627
clipper.Begin(LayoutLineCount, LayoutItemStep.y);
10628
if (item_curr_idx_to_focus != -1)
10629
clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped.
10630
if (ms_io->RangeSrcItem != -1)
10631
clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped.
10632
while (clipper.Step())
10633
{
10634
for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++)
10635
{
10636
const int item_min_idx_for_current_line = line_idx * column_count;
10637
const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size);
10638
for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx)
10639
{
10640
ExampleAsset* item_data = &Items[item_idx];
10641
ImGui::PushID((int)item_data->ID);
10642
10643
// Position item
10644
ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y);
10645
ImGui::SetCursorScreenPos(pos);
10646
10647
ImGui::SetNextItemSelectionUserData(item_idx);
10648
bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID);
10649
bool item_is_visible = ImGui::IsRectVisible(LayoutItemSize);
10650
ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize);
10651
10652
// Update our selection state immediately (without waiting for EndMultiSelect() requests)
10653
// because we use this to alter the color of our text/icon.
10654
if (ImGui::IsItemToggledSelection())
10655
item_is_selected = !item_is_selected;
10656
10657
// Focus (for after deletion)
10658
if (item_curr_idx_to_focus == item_idx)
10659
ImGui::SetKeyboardFocusHere(-1);
10660
10661
// Drag and drop
10662
if (ImGui::BeginDragDropSource())
10663
{
10664
// Create payload with full selection OR single unselected item.
10665
// (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
10666
if (ImGui::GetDragDropPayload() == NULL)
10667
{
10668
ImVector<ImGuiID> payload_items;
10669
void* it = NULL;
10670
ImGuiID id = 0;
10671
if (!item_is_selected)
10672
payload_items.push_back(item_data->ID);
10673
else
10674
while (Selection.GetNextSelectedItem(&it, &id))
10675
payload_items.push_back(id);
10676
ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
10677
}
10678
10679
// Display payload content in tooltip, by extracting it from the payload data
10680
// (we could read from selection, but it is more correct and reusable to read from payload)
10681
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
10682
const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID);
10683
ImGui::Text("%d assets", payload_count);
10684
10685
ImGui::EndDragDropSource();
10686
}
10687
10688
// Render icon (a real app would likely display an image/thumbnail here)
10689
// Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well.
10690
if (item_is_visible)
10691
{
10692
ImVec2 box_min(pos.x - 1, pos.y - 1);
10693
ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious
10694
draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color
10695
if (ShowTypeOverlay && item_data->Type != 0)
10696
{
10697
ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)];
10698
draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col);
10699
}
10700
if (display_label)
10701
{
10702
ImU32 label_col = ImGui::GetColorU32(item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled);
10703
char label[32];
10704
sprintf(label, "%d", item_data->ID);
10705
draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label);
10706
}
10707
}
10708
10709
ImGui::PopID();
10710
}
10711
}
10712
}
10713
clipper.End();
10714
ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
10715
10716
// Context menu
10717
if (ImGui::BeginPopupContextWindow())
10718
{
10719
ImGui::Text("Selection: %d items", Selection.Size);
10720
ImGui::Separator();
10721
if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0))
10722
RequestDelete = true;
10723
ImGui::EndPopup();
10724
}
10725
10726
ms_io = ImGui::EndMultiSelect();
10727
Selection.ApplyRequests(ms_io);
10728
if (want_delete)
10729
Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus);
10730
10731
// Zooming with CTRL+Wheel
10732
if (ImGui::IsWindowAppearing())
10733
ZoomWheelAccum = 0.0f;
10734
if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false)
10735
{
10736
ZoomWheelAccum += io.MouseWheel;
10737
if (fabsf(ZoomWheelAccum) >= 1.0f)
10738
{
10739
// Calculate hovered item index from mouse location
10740
// FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it.
10741
const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x;
10742
const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y;
10743
const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx;
10744
//ImGui::SetTooltip("%f,%f -> item %d", hovered_item_nx, hovered_item_ny, hovered_item_idx); // Move those 4 lines in block above for easy debugging
10745
10746
// Zoom
10747
IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum);
10748
IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f);
10749
ZoomWheelAccum -= (int)ZoomWheelAccum;
10750
UpdateLayoutSizes(avail_width);
10751
10752
// Manipulate scroll to that we will land at the same Y location of currently hovered item.
10753
// - Calculate next frame position of item under mouse
10754
// - Set new scroll position to be used in next ImGui::BeginChild() call.
10755
float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(hovered_item_ny, 1.0f)) * LayoutItemStep.y;
10756
hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y;
10757
float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y;
10758
ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y);
10759
}
10760
}
10761
}
10762
ImGui::EndChild();
10763
10764
ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size);
10765
ImGui::End();
10766
}
10767
};
10768
10769
void ShowExampleAppAssetsBrowser(bool* p_open)
10770
{
10771
IMGUI_DEMO_MARKER("Examples/Assets Browser");
10772
static ExampleAssetsBrowser assets_browser;
10773
assets_browser.Draw("Example: Assets Browser", p_open);
10774
}
10775
10776
// End of Demo code
10777
#else
10778
10779
void ImGui::ShowAboutWindow(bool*) {}
10780
void ImGui::ShowDemoWindow(bool*) {}
10781
void ImGui::ShowUserGuide() {}
10782
void ImGui::ShowStyleEditor(ImGuiStyle*) {}
10783
bool ImGui::ShowStyleSelector(const char*) { return false; }
10784
10785
#endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS
10786
10787
#endif // #ifndef IMGUI_DISABLE
10788
10789