master
ブランチ master
ShaderPlayground / deps / imgui / imgui / imgui_demo.cpp
596.3 KB、11559行 生 Blame 履歴
1// dear imgui, v1.92.9 WIP
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 pthom's online imgui_explorer: web version the demo w/ source code browser: https://pthom.github.io/imgui_explorer
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
69Index 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] Helpers: ExampleImageViewer
77// [SECTION] DemoWindowWidgetsBasic()
78// [SECTION] DemoWindowWidgetsBullets()
79// [SECTION] DemoWindowWidgetsCollapsingHeaders()
80// [SECTION] DemoWindowWidgetsComboBoxes()
81// [SECTION] DemoWindowWidgetsColorAndPickers()
82// [SECTION] DemoWindowWidgetsDataTypes()
83// [SECTION] DemoWindowWidgetsDisableBlocks()
84// [SECTION] DemoWindowWidgetsDragAndDrop()
85// [SECTION] DemoWindowWidgetsDragsAndSliders()
86// [SECTION] DemoWindowWidgetsFonts()
87// [SECTION] DemoWindowWidgetsImages()
88// [SECTION] DemoWindowWidgetsListBoxes()
89// [SECTION] DemoWindowWidgetsMultiComponents()
90// [SECTION] DemoWindowWidgetsPlotting()
91// [SECTION] DemoWindowWidgetsProgressBars()
92// [SECTION] DemoWindowWidgetsQueryingStatuses()
93// [SECTION] DemoWindowWidgetsSelectables()
94// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
95// [SECTION] DemoWindowWidgetsTabs()
96// [SECTION] DemoWindowWidgetsText()
97// [SECTION] DemoWindowWidgetsTextFilter()
98// [SECTION] DemoWindowWidgetsTextInput()
99// [SECTION] DemoWindowWidgetsTooltips()
100// [SECTION] DemoWindowWidgetsTreeNodes()
101// [SECTION] DemoWindowWidgetsVerticalSliders()
102// [SECTION] DemoWindowWidgets()
103// [SECTION] DemoWindowLayout()
104// [SECTION] DemoWindowPopups()
105// [SECTION] DemoWindowTables()
106// [SECTION] DemoWindowInputs()
107// [SECTION] About Window / ShowAboutWindow()
108// [SECTION] Style Editor / ShowStyleEditor()
109// [SECTION] User Guide / ShowUserGuide()
110// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
111// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
112// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer()
113// [SECTION] Example App: Debug Log / ShowExampleAppLog()
114// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
115// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
116// [SECTION] Example App: Long Text / ShowExampleAppLongText()
117// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
118// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
119// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
120// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
121// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()
122// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
123// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
124// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
125// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
126
127*/
128
129#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
130#define _CRT_SECURE_NO_WARNINGS
131#endif
132
133#include "imgui.h"
134#ifndef IMGUI_DISABLE
135
136// System includes
137#include <ctype.h> // toupper
138#include <limits.h> // INT_MIN, INT_MAX
139#include <math.h> // sqrtf, powf, cosf, sinf, floorf, ceilf
140#include <stdio.h> // vsnprintf, sscanf, printf
141#include <stdlib.h> // NULL, malloc, free, atoi
142#include <stdint.h> // intptr_t
143#if !defined(_MSC_VER) || _MSC_VER >= 1800
144#include <inttypes.h> // PRId64/PRIu64, not avail in some MinGW headers.
145#endif
146#ifdef __EMSCRIPTEN__
147#include <emscripten/version.h> // __EMSCRIPTEN_MAJOR__ etc.
148#endif
149
150// Visual Studio warnings
151#ifdef _MSC_VER
152#pragma warning (disable: 4127) // condition expression is constant
153#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
154#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).
155#endif
156
157// Clang/GCC warnings with -Weverything
158#if defined(__clang__)
159#if __has_warning("-Wunknown-warning-option")
160#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!
161#endif
162#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
163#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
164#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)
165#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type
166#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
167#pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal
168#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.
169#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.
170#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
171#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.
172#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
173#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
174#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
175#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
176#elif defined(__GNUC__)
177#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
178#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
179#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
180#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
181#pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure)
182#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
183#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
184#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.
185#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
186#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
187#endif
188
189// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
190#ifdef _WIN32
191#define IM_NEWLINE "\r\n"
192#else
193#define IM_NEWLINE "\n"
194#endif
195
196// Helpers
197#if defined(_MSC_VER) && !defined(snprintf)
198#define snprintf _snprintf
199#endif
200#if defined(_MSC_VER) && !defined(vsnprintf)
201#define vsnprintf _vsnprintf
202#endif
203
204// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013)
205#if !defined(PRId64) && defined(_MSC_VER)
206#define PRId64 "I64d"
207#define PRIu64 "I64u"
208#elif !defined(PRId64)
209#define PRId64 "lld"
210#define PRIu64 "llu"
211#endif
212
213// Helpers macros
214// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
215// but making an exception here as those are largely simplifying code...
216// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
217#define IM_MIN(A, B) (((A) < (B)) ? (A) : (B))
218#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
219#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
220
221// Enforce cdecl calling convention for functions called by the standard library,
222// in case compilation settings changed the default to e.g. __vectorcall
223#ifndef IMGUI_CDECL
224#ifdef _MSC_VER
225#define IMGUI_CDECL __cdecl
226#else
227#define IMGUI_CDECL
228#endif
229#endif
230
231//-----------------------------------------------------------------------------
232// [SECTION] Forward Declarations
233//-----------------------------------------------------------------------------
234
235#if !defined(IMGUI_DISABLE_DEMO_WINDOWS)
236
237// Forward Declarations
238struct ImGuiDemoWindowData;
239static void ShowExampleAppMainMenuBar();
240static void ShowExampleAppAssetsBrowser(bool* p_open);
241static void ShowExampleAppConsole(bool* p_open);
242static void ShowExampleAppCustomRendering(bool* p_open);
243static void ShowExampleAppDockSpace(bool* p_open);
244static void ShowExampleAppDocuments(bool* p_open);
245static void ShowExampleAppImageViewer(bool* p_open);
246static void ShowExampleAppLog(bool* p_open);
247static void ShowExampleAppLayout(bool* p_open);
248static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);
249static void ShowExampleAppSimpleOverlay(bool* p_open);
250static void ShowExampleAppAutoResize(bool* p_open);
251static void ShowExampleAppConstrainedResize(bool* p_open);
252static void ShowExampleAppFullscreen(bool* p_open);
253static void ShowExampleAppLongText(bool* p_open);
254static void ShowExampleAppWindowTitles(bool* p_open);
255static void ShowExampleMenuFile();
256
257// We split the contents of the big ShowDemoWindow() function into smaller functions
258// (because the link time of very large functions tends to grow non-linearly)
259static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data);
260static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data);
261static void DemoWindowLayout();
262static void DemoWindowPopups();
263static void DemoWindowTables();
264static void DemoWindowColumns();
265static void DemoWindowInputs();
266
267// Helper tree functions used by Property Editor & Multi-Select demos
268struct ExampleTreeNode;
269static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent);
270static void ExampleTree_DestroyNode(ExampleTreeNode* node);
271
272//-----------------------------------------------------------------------------
273// [SECTION] Helpers
274//-----------------------------------------------------------------------------
275
276// Helper to display a little (?) mark which shows a tooltip when hovered.
277// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
278static void HelpMarker(const char* desc)
279{
280 ImGui::TextDisabled("(?)");
281 if (ImGui::BeginItemTooltip())
282 {
283 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
284 ImGui::TextUnformatted(desc);
285 ImGui::PopTextWrapPos();
286 ImGui::EndTooltip();
287 }
288}
289
290static void ShowDockingDisabledMessage()
291{
292 ImGuiIO& io = ImGui::GetIO();
293 ImGui::Text("ERROR: Docking is not enabled! See Demo > Configuration.");
294 ImGui::Text("Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or ");
295 ImGui::SameLine(0.0f, 0.0f);
296 if (ImGui::SmallButton("click here"))
297 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
298}
299
300// Helper to wire demo markers located in code to an interactive browser (e.g. https://pthom.github.io/imgui_explorer)
301#if IMGUI_VERSION_NUM >= 19263
302namespace ImGui { extern IMGUI_API void DemoMarker(const char* file, int line, const char* section); }
303#define IMGUI_DEMO_MARKER(section) do { ImGui::DemoMarker("imgui_demo.cpp", __LINE__, section); } while (0)
304#endif
305
306// Sneakily forward declare functions which aren't worth putting in public API yet
307namespace ImGui
308{
309 IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
310 IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool is_open);
311}
312
313//-----------------------------------------------------------------------------
314// [SECTION] Demo Window / ShowDemoWindow()
315//-----------------------------------------------------------------------------
316
317// Data to be shared across different functions of the demo.
318struct ImGuiDemoWindowData
319{
320 // Examples Apps (accessible from the "Examples" menu)
321 bool ShowMainMenuBar = false;
322 bool ShowAppAssetsBrowser = false;
323 bool ShowAppConsole = false;
324 bool ShowAppCustomRendering = false;
325 bool ShowAppDocuments = false;
326 bool ShowAppDockSpace = false;
327 bool ShowAppImageViewer = false;
328 bool ShowAppLog = false;
329 bool ShowAppLayout = false;
330 bool ShowAppPropertyEditor = false;
331 bool ShowAppSimpleOverlay = false;
332 bool ShowAppAutoResize = false;
333 bool ShowAppConstrainedResize = false;
334 bool ShowAppFullscreen = false;
335 bool ShowAppLongText = false;
336 bool ShowAppWindowTitles = false;
337
338 // Dear ImGui Tools (accessible from the "Tools" menu)
339 bool ShowMetrics = false;
340 bool ShowDebugLog = false;
341 bool ShowIDStackTool = false;
342 bool ShowStyleEditor = false;
343 bool ShowAbout = false;
344
345 // Other data
346 bool DisableSections = false;
347 ExampleTreeNode* DemoTree = NULL;
348
349 ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); }
350};
351
352// Demonstrate most Dear ImGui features (this is big function!)
353// You may execute this function to experiment with the UI and understand what it does.
354// You may then search for keywords in the code when you are interested by a specific feature.
355void ImGui::ShowDemoWindow(bool* p_open)
356{
357 // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
358 // Most functions would normally just assert/crash if the context is missing.
359 IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!");
360
361 // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
362 IMGUI_CHECKVERSION();
363
364 // Stored data
365 static ImGuiDemoWindowData demo_data;
366
367 // Examples Apps (accessible from the "Examples" menu)
368 if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); }
369 if (demo_data.ShowAppDockSpace) { ShowExampleAppDockSpace(&demo_data.ShowAppDockSpace); } // Important: Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function)
370 if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); } // ...process the Document app next, as it may also use a DockSpace()
371 if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); }
372 if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); }
373 if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); }
374 if (demo_data.ShowAppImageViewer) { ShowExampleAppImageViewer(&demo_data.ShowAppImageViewer); }
375 if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); }
376 if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); }
377 if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); }
378 if (demo_data.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(&demo_data.ShowAppSimpleOverlay); }
379 if (demo_data.ShowAppAutoResize) { ShowExampleAppAutoResize(&demo_data.ShowAppAutoResize); }
380 if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo_data.ShowAppConstrainedResize); }
381 if (demo_data.ShowAppFullscreen) { ShowExampleAppFullscreen(&demo_data.ShowAppFullscreen); }
382 if (demo_data.ShowAppLongText) { ShowExampleAppLongText(&demo_data.ShowAppLongText); }
383 if (demo_data.ShowAppWindowTitles) { ShowExampleAppWindowTitles(&demo_data.ShowAppWindowTitles); }
384
385 // Dear ImGui Tools (accessible from the "Tools" menu)
386 if (demo_data.ShowMetrics) { ImGui::ShowMetricsWindow(&demo_data.ShowMetrics); }
387 if (demo_data.ShowDebugLog) { ImGui::ShowDebugLogWindow(&demo_data.ShowDebugLog); }
388 if (demo_data.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(&demo_data.ShowIDStackTool); }
389 if (demo_data.ShowAbout) { ImGui::ShowAboutWindow(&demo_data.ShowAbout); }
390 if (demo_data.ShowStyleEditor)
391 {
392 ImGui::Begin("Dear ImGui Style Editor", &demo_data.ShowStyleEditor);
393 ImGui::ShowStyleEditor();
394 ImGui::End();
395 }
396
397 // Demonstrate the various window flags. Typically you would just use the default!
398 static bool no_titlebar = false;
399 static bool no_scrollbar = false;
400 static bool no_menu = false;
401 static bool no_move = false;
402 static bool no_resize = false;
403 static bool no_collapse = false;
404 static bool no_close = false;
405 static bool no_nav = false;
406 static bool no_background = false;
407 static bool no_bring_to_front = false;
408 static bool no_docking = false;
409 static bool unsaved_document = false;
410
411 ImGuiWindowFlags window_flags = 0;
412 if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar;
413 if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar;
414 if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar;
415 if (no_move) window_flags |= ImGuiWindowFlags_NoMove;
416 if (no_resize) window_flags |= ImGuiWindowFlags_NoResize;
417 if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse;
418 if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
419 if (no_background) window_flags |= ImGuiWindowFlags_NoBackground;
420 if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
421 if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking;
422 if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument;
423 if (no_close) p_open = NULL; // Don't pass our bool* to Begin
424
425 // We specify a default position/size in case there's no data in the .ini file.
426 // We only do it to make the demo applications a little more welcoming, but typically this isn't required.
427 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
428 ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver);
429 ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver);
430
431 // Main body of the Demo window starts here.
432 if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags))
433 {
434 // Early out if the window is collapsed, as an optimization.
435 ImGui::End();
436 return;
437 }
438
439 // Most framed widgets share a common width settings. Remaining width is used for the label.
440 // The width of the frame may be changed with PushItemWidth() or SetNextItemWidth().
441 // - Positive value for absolute size, negative value for right-alignment.
442 // - The default value is about GetWindowWidth() * 0.65f.
443 // - See 'Demo->Layout->Widgets Width' for details.
444 // Here we change the frame width based on how much width we want to give to the label.
445 const float label_width_base = ImGui::GetFontSize() * 12; // Some amount of width for label, based on font size.
446 const float label_width_max = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets.
447 const float label_width = IM_MIN(label_width_base, label_width_max);
448 ImGui::PushItemWidth(-label_width); // Right-align: framed items will leave 'label_width' available for the label.
449 //ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for framed widgets, leaving 60% width for labels.
450 //ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for labels, leaving 60% width for framed widgets.
451 //ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Use XXX width for labels, leaving the rest for framed widgets.
452
453 // Menu Bar
454 DemoWindowMenuBar(&demo_data);
455
456 ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
457 ImGui::Spacing();
458
459 if (ImGui::CollapsingHeader("Help"))
460 {
461 IMGUI_DEMO_MARKER("Help");
462 ImGui::SeparatorText("ABOUT THIS DEMO:");
463 ImGui::BulletText("Sections below are demonstrating many aspects of the library.");
464 ImGui::BulletText("The \"Examples\" menu above leads to more demo contents.");
465 ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n"
466 "and Metrics/Debugger (general purpose Dear ImGui debugging tool).");
467 ImGui::BulletText("Web demo (w/ source code browser): ");
468 ImGui::SameLine(0, 0);
469 ImGui::TextLinkOpenURL("https://pthom.github.io/imgui_explorer");
470
471 ImGui::SeparatorText("PROGRAMMER GUIDE:");
472 ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!");
473 ImGui::BulletText("See comments in imgui.cpp.");
474 ImGui::BulletText("See example applications in the examples/ folder.");
475 ImGui::BulletText("Read the FAQ at ");
476 ImGui::SameLine(0, 0);
477 ImGui::TextLinkOpenURL("https://www.dearimgui.com/faq/");
478 ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.");
479 ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.");
480
481 ImGui::SeparatorText("USER GUIDE:");
482 ImGui::ShowUserGuide();
483 }
484
485 if (ImGui::CollapsingHeader("Configuration"))
486 {
487 ImGuiIO& io = ImGui::GetIO();
488
489 if (ImGui::TreeNode("Configuration##2"))
490 {
491 IMGUI_DEMO_MARKER("Configuration");
492 ImGui::SeparatorText("General");
493 ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
494 ImGui::SameLine(); HelpMarker("Enable keyboard controls.");
495 ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
496 ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
497 ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", &io.ConfigFlags, ImGuiConfigFlags_NoMouse);
498 ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable mouse inputs and interactions.");
499
500 // The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:
501 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
502 {
503 if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f)
504 {
505 ImGui::SameLine();
506 ImGui::Text("<<PRESS SPACE TO DISABLE>>");
507 }
508 // Prevent both being checked
509 if (ImGui::IsKeyPressed(ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard))
510 io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
511 }
512
513 ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
514 ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.");
515 ImGui::CheckboxFlags("io.ConfigFlags: NoKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NoKeyboard);
516 ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable keyboard inputs and interactions.");
517
518 ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue);
519 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.");
520 ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
521 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).");
522
523 ImGui::SeparatorText("Keyboard/Gamepad Navigation");
524 ImGui::Checkbox("io.ConfigNavSwapGamepadButtons", &io.ConfigNavSwapGamepadButtons);
525 ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos);
526 ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
527 ImGui::Checkbox("io.ConfigNavCaptureKeyboard", &io.ConfigNavCaptureKeyboard);
528 ImGui::Checkbox("io.ConfigNavEscapeClearFocusItem", &io.ConfigNavEscapeClearFocusItem);
529 ImGui::SameLine(); HelpMarker("Pressing Escape clears focused item.");
530 ImGui::Checkbox("io.ConfigNavEscapeClearFocusWindow", &io.ConfigNavEscapeClearFocusWindow);
531 ImGui::SameLine(); HelpMarker("Pressing Escape clears focused window.");
532 ImGui::Checkbox("io.ConfigNavCursorVisibleAuto", &io.ConfigNavCursorVisibleAuto);
533 ImGui::SameLine(); HelpMarker("Using directional navigation key makes the cursor visible. Mouse click hides the cursor.");
534 ImGui::Checkbox("io.ConfigNavCursorVisibleAlways", &io.ConfigNavCursorVisibleAlways);
535 ImGui::SameLine(); HelpMarker("Navigation cursor is always visible.");
536
537 ImGui::SeparatorText("Docking");
538 ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable);
539 ImGui::SameLine();
540 if (io.ConfigDockingWithShift)
541 HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to enable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows).");
542 else
543 HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows).");
544 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
545 {
546 ImGui::Indent();
547 ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit);
548 ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.");
549 ImGui::Checkbox("io.ConfigDockingNoDockingOver", &io.ConfigDockingNoDockingOver);
550 ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window merging into a same tab-bar, so docking is limited to splitting windows.");
551 ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift);
552 ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)");
553 ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar);
554 ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows.");
555 ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload);
556 ImGui::SameLine(); HelpMarker("Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge.");
557 ImGui::Unindent();
558 }
559
560 ImGui::SeparatorText("Multi-viewports");
561 ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", &io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable);
562 ImGui::SameLine(); HelpMarker("[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details.");
563 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
564 {
565 ImGui::Indent();
566 ImGui::Checkbox("io.ConfigViewportsNoAutoMerge", &io.ConfigViewportsNoAutoMerge);
567 ImGui::SameLine(); HelpMarker("Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it.");
568 ImGui::Checkbox("io.ConfigViewportsNoTaskBarIcon", &io.ConfigViewportsNoTaskBarIcon);
569 ImGui::SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)");
570 ImGui::Checkbox("io.ConfigViewportsNoDecoration", &io.ConfigViewportsNoDecoration);
571 ImGui::SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)");
572 ImGui::Checkbox("io.ConfigViewportsNoDefaultParent", &io.ConfigViewportsNoDefaultParent);
573 ImGui::SameLine(); HelpMarker("(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)");
574 ImGui::Checkbox("io.ConfigViewportsPlatformFocusSetsImGuiFocus", &io.ConfigViewportsPlatformFocusSetsImGuiFocus);
575 ImGui::SameLine(); HelpMarker("When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change).");
576 ImGui::Unindent();
577 }
578
579 //ImGui::SeparatorText("DPI/Scaling");
580 //ImGui::Checkbox("io.ConfigDpiScaleFonts", &io.ConfigDpiScaleFonts);
581 //ImGui::SameLine(); HelpMarker("Experimental: Automatically update style.FontScaleDpi when Monitor DPI changes. This will scale fonts but NOT style sizes/padding for now.");
582 //ImGui::Checkbox("io.ConfigDpiScaleViewports", &io.ConfigDpiScaleViewports);
583 //ImGui::SameLine(); HelpMarker("Experimental: Scale Dear ImGui and Platform Windows when Monitor DPI changes.");
584
585 ImGui::SeparatorText("Windows");
586 ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
587 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.");
588 ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
589 ImGui::Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]
590 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.");
591 ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
592 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.");
593
594 ImGui::SeparatorText("Widgets");
595 ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
596 ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).");
597 ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive);
598 ImGui::SameLine(); HelpMarker("Pressing Enter will reactivate item and select all text (single-line only).");
599 ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
600 ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
601 ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors);
602 ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
603 ImGui::Text("Also see Style->Rendering for rendering options.");
604
605 // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
606 ImGui::SeparatorText("Error Handling");
607
608 ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
609 ImGui::SameLine(); HelpMarker(
610 "Options to configure how we handle recoverable errors.\n"
611 "- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
612 "- You not are not supposed to rely on it in the course of a normal application run.\n"
613 "- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
614 "- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! "
615 "Otherwise it would severely hinder your ability to catch and correct mistakes!");
616 ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert);
617 ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog);
618 ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip);
619 if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
620 io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
621
622 // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
623 ImGui::SeparatorText("Debug");
624 ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
625 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.");
626 ImGui::Checkbox("io.ConfigDebugHighlightIdConflicts", &io.ConfigDebugHighlightIdConflicts);
627 ImGui::SameLine(); HelpMarker("Highlight and show an error message when multiple items have conflicting identifiers.");
628 ImGui::BeginDisabled();
629 ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce);
630 ImGui::EndDisabled();
631 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.");
632 ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop);
633 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.");
634 ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss);
635 ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.");
636 ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
637 ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).");
638
639 ImGui::TreePop();
640 ImGui::Spacing();
641 }
642
643 if (ImGui::TreeNode("Backend Flags"))
644 {
645 IMGUI_DEMO_MARKER("Configuration/Backend Flags");
646 HelpMarker(
647 "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n"
648 "Here we expose them as read-only fields to avoid breaking interactions with your backend.");
649
650 // Make a local copy to avoid modifying actual backend flags.
651 // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?
652 ImGui::BeginDisabled();
653 ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &io.BackendFlags, ImGuiBackendFlags_HasGamepad);
654 ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
655 ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);
656 ImGui::CheckboxFlags("io.BackendFlags: PlatformHasViewports", &io.BackendFlags, ImGuiBackendFlags_PlatformHasViewports);
657 ImGui::CheckboxFlags("io.BackendFlags: HasMouseHoveredViewport",&io.BackendFlags, ImGuiBackendFlags_HasMouseHoveredViewport);
658 ImGui::CheckboxFlags("io.BackendFlags: HasParentViewport", &io.BackendFlags, ImGuiBackendFlags_HasParentViewport);
659 ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);
660 ImGui::CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);
661 ImGui::CheckboxFlags("io.BackendFlags: RendererHasViewports", &io.BackendFlags, ImGuiBackendFlags_RendererHasViewports);
662 ImGui::EndDisabled();
663
664 ImGui::TreePop();
665 ImGui::Spacing();
666 }
667
668 if (ImGui::TreeNode("Style, Fonts"))
669 {
670 IMGUI_DEMO_MARKER("Configuration/Style, Fonts");
671 ImGui::Checkbox("Style Editor", &demo_data.ShowStyleEditor);
672 ImGui::SameLine();
673 HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.");
674 ImGui::TreePop();
675 ImGui::Spacing();
676 }
677
678 if (ImGui::TreeNode("Capture/Logging"))
679 {
680 IMGUI_DEMO_MARKER("Configuration/Capture, Logging");
681 HelpMarker(
682 "The logging API redirects all text output so you can easily capture the content of "
683 "a window or a block. Tree nodes can be automatically expanded.\n"
684 "Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
685 ImGui::LogButtons();
686
687 HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output.");
688 if (ImGui::Button("Copy \"Hello, world!\" to clipboard"))
689 {
690 ImGui::LogToClipboard();
691 ImGui::LogText("Hello, world!");
692 ImGui::LogFinish();
693 }
694 ImGui::TreePop();
695 }
696 }
697
698 if (ImGui::CollapsingHeader("Window options"))
699 {
700 IMGUI_DEMO_MARKER("Window options");
701 if (ImGui::BeginTable("split", 3))
702 {
703 ImGui::TableNextColumn(); ImGui::Checkbox("No titlebar", &no_titlebar);
704 ImGui::TableNextColumn(); ImGui::Checkbox("No scrollbar", &no_scrollbar);
705 ImGui::TableNextColumn(); ImGui::Checkbox("No menu", &no_menu);
706 ImGui::TableNextColumn(); ImGui::Checkbox("No move", &no_move);
707 ImGui::TableNextColumn(); ImGui::Checkbox("No resize", &no_resize);
708 ImGui::TableNextColumn(); ImGui::Checkbox("No collapse", &no_collapse);
709 ImGui::TableNextColumn(); ImGui::Checkbox("No close", &no_close);
710 ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav);
711 ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background);
712 ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front);
713 ImGui::TableNextColumn(); ImGui::Checkbox("No docking", &no_docking);
714 ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document);
715 ImGui::EndTable();
716 }
717 }
718
719 // All demo contents
720 DemoWindowWidgets(&demo_data);
721 DemoWindowLayout();
722 DemoWindowPopups();
723 DemoWindowTables();
724 DemoWindowInputs();
725
726 // End of ShowDemoWindow()
727 ImGui::PopItemWidth();
728 ImGui::End();
729}
730
731//-----------------------------------------------------------------------------
732// [SECTION] DemoWindowMenuBar()
733//-----------------------------------------------------------------------------
734
735static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
736{
737 if (ImGui::BeginMenuBar())
738 {
739 if (ImGui::BeginMenu("Menu"))
740 {
741 IMGUI_DEMO_MARKER("Menu/File");
742 ShowExampleMenuFile();
743 ImGui::EndMenu();
744 }
745 if (ImGui::BeginMenu("Examples"))
746 {
747 IMGUI_DEMO_MARKER("Menu/Examples");
748 ImGui::MenuItem("Main menu bar", NULL, &demo_data->ShowMainMenuBar);
749
750 ImGui::SeparatorText("Mini apps");
751 ImGui::MenuItem("Assets Browser", NULL, &demo_data->ShowAppAssetsBrowser);
752 ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole);
753 ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering);
754 ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments);
755 ImGui::MenuItem("Dockspace", NULL, &demo_data->ShowAppDockSpace);
756 ImGui::MenuItem("Image Viewer", NULL, &demo_data->ShowAppImageViewer);
757 ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog);
758 ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor);
759 ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout);
760 ImGui::MenuItem("Simple overlay", NULL, &demo_data->ShowAppSimpleOverlay);
761
762 ImGui::SeparatorText("Concepts");
763 ImGui::MenuItem("Auto-resizing window", NULL, &demo_data->ShowAppAutoResize);
764 ImGui::MenuItem("Constrained-resizing window", NULL, &demo_data->ShowAppConstrainedResize);
765 ImGui::MenuItem("Fullscreen window", NULL, &demo_data->ShowAppFullscreen);
766 ImGui::MenuItem("Long text display", NULL, &demo_data->ShowAppLongText);
767 ImGui::MenuItem("Manipulating window titles", NULL, &demo_data->ShowAppWindowTitles);
768
769 ImGui::EndMenu();
770 }
771 //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar!
772 if (ImGui::BeginMenu("Tools"))
773 {
774 IMGUI_DEMO_MARKER("Menu/Tools");
775 ImGuiIO& io = ImGui::GetIO();
776#ifndef IMGUI_DISABLE_DEBUG_TOOLS
777 const bool has_debug_tools = true;
778#else
779 const bool has_debug_tools = false;
780#endif
781 ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools);
782 if (ImGui::BeginMenu("Debug Options"))
783 {
784 ImGui::BeginDisabled(!has_debug_tools);
785 ImGui::Checkbox("Highlight ID Conflicts", &io.ConfigDebugHighlightIdConflicts);
786 ImGui::EndDisabled();
787 ImGui::Checkbox("Assert on error recovery", &io.ConfigErrorRecoveryEnableAssert);
788 ImGui::TextDisabled("(see Demo->Configuration for more)");
789 ImGui::EndMenu();
790 }
791 ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools);
792 ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools);
793 bool is_debugger_present = io.ConfigDebugIsDebuggerPresent;
794 if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools))// && is_debugger_present))
795 ImGui::DebugStartItemPicker();
796 if (!is_debugger_present)
797 ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable some extra features to avoid casual users crashing the application.");
798 ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor);
799 ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout);
800
801 ImGui::EndMenu();
802 }
803 ImGui::EndMenuBar();
804 }
805}
806
807//-----------------------------------------------------------------------------
808// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
809//-----------------------------------------------------------------------------
810
811// Simple representation for a tree
812// (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.)
813struct ExampleTreeNode
814{
815 // Tree structure
816 char Name[28] = "";
817 int UID = 0;
818 ExampleTreeNode* Parent = NULL;
819 ImVector<ExampleTreeNode*> Childs;
820 int IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily
821
822 // Leaf Data
823 bool HasData = false; // All leaves have data
824 bool DataMyBool = true;
825 int DataMyInt = 128;
826 ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f);
827};
828
829// Simple representation of struct metadata/serialization data.
830// (this is a minimal version of what a typical advanced application may provide)
831struct ExampleMemberInfo
832{
833 const char* Name; // Member name
834 ImGuiDataType DataType; // Member type
835 int DataCount; // Member count (1 when scalar)
836 int Offset; // Offset inside parent structure
837};
838
839// Metadata description of ExampleTreeNode struct.
840static const ExampleMemberInfo ExampleTreeNodeMemberInfos[]
841{
842 { "MyName", ImGuiDataType_String, 1, offsetof(ExampleTreeNode, Name) },
843 { "MyBool", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataMyBool) },
844 { "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataMyInt) },
845 { "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataMyVec2) },
846};
847
848static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent)
849{
850 ExampleTreeNode* node = IM_NEW(ExampleTreeNode);
851 snprintf(node->Name, IM_COUNTOF(node->Name), "%s", name);
852 node->UID = uid;
853 node->Parent = parent;
854 node->IndexInParent = parent ? parent->Childs.Size : 0;
855 if (parent)
856 parent->Childs.push_back(node);
857 return node;
858}
859
860static void ExampleTree_DestroyNode(ExampleTreeNode* node)
861{
862 for (ExampleTreeNode* child_node : node->Childs)
863 ExampleTree_DestroyNode(child_node);
864 IM_DELETE(node);
865}
866
867// Create example tree data
868// (warning: this can allocates MANY MANY more times than other code in all of Dear ImGui + demo combined)
869// (a real application managing one million nodes would likely store its tree data differently)
870static ExampleTreeNode* ExampleTree_CreateDemoTree()
871{
872 // 20 root nodes -> 211 total nodes, ~261 allocs.
873 // 1000 root nodes -> ~11K total nodes, ~14K allocs.
874 // 10000 root nodes -> ~123K total nodes, ~154K allocs.
875 // 100000 root nodes -> ~1338K total nodes, ~1666K allocs.
876 const int ROOT_ITEMS_COUNT = 20;
877
878 static const char* category_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" };
879 const int category_count = IM_COUNTOF(category_names);
880 const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name);
881 char name_buf[NAME_MAX_LEN];
882 int uid = 0;
883 ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL);
884 for (int idx_L0 = 0; idx_L0 < ROOT_ITEMS_COUNT; idx_L0++)
885 {
886 snprintf(name_buf, IM_COUNTOF(name_buf), "%s %d", category_names[idx_L0 / (ROOT_ITEMS_COUNT / category_count)], idx_L0 % (ROOT_ITEMS_COUNT / category_count));
887 ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);
888 const int number_of_childs = (int)strlen(node_L1->Name);
889 for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
890 {
891 snprintf(name_buf, IM_COUNTOF(name_buf), "Child %d", idx_L1);
892 ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1);
893 node_L2->HasData = true;
894 if (idx_L1 == 0)
895 {
896 snprintf(name_buf, IM_COUNTOF(name_buf), "Sub-child %d", 0);
897 ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2);
898 node_L3->HasData = true;
899 }
900 }
901 }
902 return node_L0;
903}
904
905//-----------------------------------------------------------------------------
906// [SECTION] Helpers: ExampleImageViewer
907//-----------------------------------------------------------------------------
908
909struct ExampleImageViewerData
910{
911 ImU32 ImageBgColor = IM_COL32(100, 100, 100, 255);
912 ImU32 GridColor = IM_COL32(255, 255, 255, 100);
913 bool GridEnabled = true;
914 bool ViewReset = true;
915 ImVec2 ViewOffset; // in image space
916 float Zoom = 10.0f;
917 float ZoomMin = 1.0f;
918 float ZoomMax = 10000.0f;
919};
920
921static void ExampleImageViewer_DrawOptions(ExampleImageViewerData* data)
922{
923 ImGui::SetNextItemShortcut(ImGuiKey_G, ImGuiInputFlags_Tooltip); // | ImGuiInputFlags_RouteGlobal
924 ImGui::Checkbox("Grid", &data->GridEnabled);
925 ImGui::SameLine();
926 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
927 float zoom_100 = data->Zoom * 100.0f;
928 if (ImGui::DragFloat("##Zoom", &zoom_100, 5.0f, data->ZoomMin * 100.0f, data->ZoomMax * 100.0f, "%.0f%%", ImGuiSliderFlags_AlwaysClamp))
929 data->Zoom = zoom_100 / 100.0f;
930}
931
932static void ExampleImageViewer_DrawCanvas(ExampleImageViewerData* data, ImVec2 canvas_size, ImTextureRef image_tex_ref, int image_w, int image_h)
933{
934 ImGuiIO& io = ImGui::GetIO();
935 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
936 ImDrawList* draw_list = ImGui::GetWindowDrawList();
937 IM_ASSERT(canvas_size.x >= 0.0f && canvas_size.y >= 0.0f);
938
939 // Layout canvas
940 ImGui::InvisibleButton("##Canvas", canvas_size);
941 ImVec2 canvas_min = ImGui::GetItemRectMin();
942 ImVec2 canvas_max = ImGui::GetItemRectMax();
943
944 if (data->ViewReset)
945 data->ViewOffset = ImVec2((canvas_size.x * 0.5f / data->Zoom) - 0.5f, (canvas_size.y * 0.5f / data->Zoom) - 0.5f); // Add half a pixel padding
946 data->ViewReset = false;
947
948 // Handle inputs
949 if (ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY))
950 if (io.MouseWheel != 0.0f)
951 data->Zoom = IM_CLAMP(data->Zoom * (1.0f + io.MouseWheel * 0.10f), data->ZoomMin, data->ZoomMax);
952 float zoom = data->Zoom; // (float)(int)ViewZoom;
953 if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0))
954 {
955 data->ViewOffset.x -= io.MouseDelta.x / zoom;
956 data->ViewOffset.y -= io.MouseDelta.y / zoom;
957 }
958
959 // Display image
960 ImVec2 image_min, image_max;
961 image_min.x = (float)(int)((canvas_min.x - (data->ViewOffset.x * zoom)) + (canvas_size.x * 0.5f));
962 image_min.y = (float)(int)((canvas_min.y - (data->ViewOffset.y * zoom)) + (canvas_size.y * 0.5f));
963 image_max.x = (float)(int)(image_min.x + image_w * zoom);
964 image_max.y = (float)(int)(image_min.y + image_h * zoom);
965 draw_list->AddRect(ImVec2(canvas_min.x - 1.0f, canvas_min.y - 1.0f), ImVec2(canvas_max.x + 1.0f, canvas_max.y + 1.0f), IM_COL32(255, 255, 255, 255));
966 draw_list->PushClipRect(canvas_min, canvas_max, true);
967 draw_list->AddRectFilled(image_min, image_max, data->ImageBgColor);
968 if (platform_io.DrawCallback_SetSamplerNearest != NULL)
969 draw_list->AddCallback(platform_io.DrawCallback_SetSamplerNearest);
970 draw_list->AddImage(image_tex_ref, image_min, image_max);
971 if (platform_io.DrawCallback_SetSamplerLinear != NULL)
972 draw_list->AddCallback(ImGui::GetPlatformIO().DrawCallback_SetSamplerLinear);
973
974 // Display grid lines for visible pixels
975 if (data->GridEnabled && zoom > 6.0f)
976 {
977 const float step = (float)zoom;
978 for (int px = (int)((canvas_min.x - image_min.x) / step); px <= (int)((canvas_max.x - image_min.x) / step); px++)
979 draw_list->AddLineV(image_min.x + px * step, canvas_min.y, canvas_max.y, data->GridColor, 1.0f);
980 for (int py = (int)((canvas_min.y - image_min.y) / step); py <= (int)((canvas_max.y - image_min.y) / step); py++)
981 draw_list->AddLineH(canvas_min.x, canvas_max.x, image_min.y + py * step, data->GridColor, 1.0f);
982 }
983 draw_list->PopClipRect();
984}
985
986//-----------------------------------------------------------------------------
987// [SECTION] DemoWindowWidgetsBasic()
988//-----------------------------------------------------------------------------
989
990static void DemoWindowWidgetsBasic()
991{
992 if (ImGui::TreeNode("Basic"))
993 {
994 IMGUI_DEMO_MARKER("Widgets/Basic");
995 ImGui::SeparatorText("General");
996
997 IMGUI_DEMO_MARKER("Widgets/Basic/Button");
998 static int clicked = 0;
999 if (ImGui::Button("Button"))
1000 clicked++;
1001 if (clicked & 1)
1002 {
1003 ImGui::SameLine();
1004 ImGui::Text("Thanks for clicking me!");
1005 }
1006
1007 IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox");
1008 static bool check = true;
1009 ImGui::Checkbox("checkbox", &check);
1010
1011 IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton");
1012 static int e = 0;
1013 ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine();
1014 ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine();
1015 ImGui::RadioButton("radio c", &e, 2);
1016
1017 ImGui::AlignTextToFramePadding();
1018 ImGui::TextLinkOpenURL("Hyperlink", "https://github.com/ocornut/imgui/wiki/Error-Handling");
1019
1020 // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
1021 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)");
1022 for (int i = 0; i < 7; i++)
1023 {
1024 if (i > 0)
1025 ImGui::SameLine();
1026 ImGui::PushID(i);
1027 ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));
1028 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));
1029 ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));
1030 ImGui::Button("Click");
1031 ImGui::PopStyleColor(3);
1032 ImGui::PopID();
1033 }
1034
1035 // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
1036 // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
1037 // See 'Demo->Layout->Text Baseline Alignment' for details.
1038 ImGui::AlignTextToFramePadding();
1039 ImGui::Text("Hold to repeat:");
1040 ImGui::SameLine();
1041
1042 // Arrow buttons with Repeater
1043 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)");
1044 static int counter = 0;
1045 float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
1046 ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
1047 if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; }
1048 ImGui::SameLine(0.0f, spacing);
1049 if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; }
1050 ImGui::PopItemFlag();
1051 ImGui::SameLine();
1052 ImGui::Text("%d", counter);
1053
1054 ImGui::Button("Tooltip");
1055 ImGui::SetItemTooltip("I am a tooltip");
1056
1057 ImGui::LabelText("label", "Value");
1058
1059 ImGui::SeparatorText("Inputs");
1060
1061 {
1062 // If you want to use InputText() with std::string or any custom dynamic string type:
1063 // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp
1064 // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.
1065 IMGUI_DEMO_MARKER("Widgets/Basic/InputText");
1066 static char str0[128] = "Hello, world!";
1067 ImGui::InputText("input text", str0, IM_COUNTOF(str0));
1068 ImGui::SameLine(); HelpMarker(
1069 "USER:\n"
1070 "Hold Shift or use mouse to select text.\n"
1071 "Ctrl+Left/Right to word jump.\n"
1072 "Ctrl+A or Double-Click to select all.\n"
1073 "Ctrl+X,Ctrl+C,Ctrl+V for clipboard.\n"
1074 "Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.\n"
1075 "Escape to revert.\n\n"
1076 "PROGRAMMER:\n"
1077 "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
1078 "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
1079 "in imgui_demo.cpp).");
1080
1081 static char str1[128] = "";
1082 ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_COUNTOF(str1));
1083
1084 IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat");
1085 static int i0 = 123;
1086 ImGui::InputInt("input int", &i0);
1087
1088 static float f0 = 0.001f;
1089 ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f");
1090
1091 static double d0 = 999999.00000001;
1092 ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f");
1093
1094 static float f1 = 1.e10f;
1095 ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e");
1096 ImGui::SameLine(); HelpMarker(
1097 "You can input value using the scientific notation,\n"
1098 " e.g. \"1e+8\" becomes \"100000000\".");
1099
1100 static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
1101 ImGui::InputFloat3("input float3", vec4a);
1102 }
1103
1104 ImGui::SeparatorText("Drags");
1105
1106 {
1107 IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat");
1108 static int i1 = 50, i2 = 42, i3 = 128;
1109 ImGui::DragInt("drag int", &i1, 1);
1110 ImGui::SameLine(); HelpMarker(
1111 "Click and drag to edit value.\n"
1112 "Hold Shift/Alt for faster/slower edit.\n"
1113 "Double-Click or Ctrl+Click to input value.");
1114 ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp);
1115 ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround);
1116
1117 static float f1 = 1.00f, f2 = 0.0067f;
1118 ImGui::DragFloat("drag float", &f1, 0.005f);
1119 ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns");
1120 //ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround);
1121 }
1122
1123 ImGui::SeparatorText("Sliders");
1124
1125 {
1126 IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat");
1127 static int i1 = 0;
1128 ImGui::SliderInt("slider int", &i1, -1, 3);
1129 ImGui::SameLine(); HelpMarker("Ctrl+Click to input value.");
1130
1131 static float f1 = 0.123f, f2 = 0.0f;
1132 ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
1133 ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic);
1134
1135 IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle");
1136 static float angle = 0.0f;
1137 ImGui::SliderAngle("slider angle", &angle);
1138
1139 // Using the format string to display a name instead of an integer.
1140 // Here we completely omit '%d' from the format string, so it'll only display a name.
1141 // This technique can also be used with DragInt().
1142 IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)");
1143 enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };
1144 static int elem = Element_Fire;
1145 const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
1146 const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
1147 ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable Ctrl+Click here.
1148 ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer.");
1149 }
1150
1151 ImGui::SeparatorText("Selectors/Pickers");
1152
1153 {
1154 IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4");
1155 static float col1[3] = { 1.0f, 0.0f, 0.2f };
1156 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1157 ImGui::ColorEdit3("color 1", col1);
1158 ImGui::SameLine(); HelpMarker(
1159 "Click on the color square to open a color picker.\n"
1160 "Click and hold to use drag and drop.\n"
1161 "Right-Click on the color square to show options.\n"
1162 "Ctrl+Click on individual component to input value.\n");
1163
1164 ImGui::ColorEdit4("color 2", col2);
1165 }
1166
1167 {
1168 // Using the _simplified_ one-liner Combo() api here
1169 // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.
1170 IMGUI_DEMO_MARKER("Widgets/Basic/Combo");
1171 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" };
1172 static int item_current = 0;
1173 ImGui::Combo("combo", &item_current, items, IM_COUNTOF(items));
1174 ImGui::SameLine(); HelpMarker(
1175 "Using the simplified one-liner Combo API here.\n"
1176 "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.");
1177 }
1178
1179 {
1180 // Using the _simplified_ one-liner ListBox() api here
1181 // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.
1182 IMGUI_DEMO_MARKER("Widgets/Basic/ListBox");
1183 const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
1184 static int item_current = 1;
1185 ImGui::ListBox("listbox", &item_current, items, IM_COUNTOF(items), 4);
1186 ImGui::SameLine(); HelpMarker(
1187 "Using the simplified one-liner ListBox API here.\n"
1188 "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.");
1189 }
1190
1191 // Testing ImGuiOnceUponAFrame helper.
1192 //static ImGuiOnceUponAFrame once;
1193 //for (int i = 0; i < 5; i++)
1194 // if (once)
1195 // ImGui::Text("This will be displayed only once.");
1196
1197 ImGui::TreePop();
1198 }
1199}
1200
1201//-----------------------------------------------------------------------------
1202// [SECTION] DemoWindowWidgetsBullets()
1203//-----------------------------------------------------------------------------
1204
1205static void DemoWindowWidgetsBullets()
1206{
1207 if (ImGui::TreeNode("Bullets"))
1208 {
1209 IMGUI_DEMO_MARKER("Widgets/Bullets");
1210 ImGui::BulletText("Bullet point 1");
1211 ImGui::BulletText("Bullet point 2\nOn multiple lines");
1212 if (ImGui::TreeNode("Tree node"))
1213 {
1214 ImGui::BulletText("Another bullet point");
1215 ImGui::TreePop();
1216 }
1217 ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)");
1218 ImGui::Bullet(); ImGui::SmallButton("Button");
1219 ImGui::TreePop();
1220 }
1221}
1222
1223//-----------------------------------------------------------------------------
1224// [SECTION] DemoWindowWidgetsCollapsingHeaders()
1225//-----------------------------------------------------------------------------
1226
1227static void DemoWindowWidgetsCollapsingHeaders()
1228{
1229 if (ImGui::TreeNode("Collapsing Headers"))
1230 {
1231 IMGUI_DEMO_MARKER("Widgets/Collapsing Headers");
1232 static bool closable_group = true;
1233 ImGui::Checkbox("Show 2nd header", &closable_group);
1234 if (ImGui::CollapsingHeader("Header", ImGuiTreeNodeFlags_None))
1235 {
1236 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1237 for (int i = 0; i < 5; i++)
1238 ImGui::Text("Some content %d", i);
1239 }
1240 if (ImGui::CollapsingHeader("Header with a close button", &closable_group))
1241 {
1242 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1243 for (int i = 0; i < 5; i++)
1244 ImGui::Text("More content %d", i);
1245 }
1246 /*
1247 if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet))
1248 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1249 */
1250 ImGui::TreePop();
1251 }
1252}
1253
1254//-----------------------------------------------------------------------------
1255// [SECTION] DemoWindowWidgetsColorAndPickers()
1256//-----------------------------------------------------------------------------
1257
1258static void DemoWindowWidgetsColorAndPickers()
1259{
1260 if (ImGui::TreeNode("Color/Picker Widgets"))
1261 {
1262 IMGUI_DEMO_MARKER("Widgets/Color");
1263 static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);
1264 static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None;
1265
1266 ImGui::SeparatorText("Options");
1267 ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &base_flags, ImGuiColorEditFlags_NoAlpha);
1268 ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque);
1269 ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg);
1270 ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf);
1271 ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options.");
1272 ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop);
1273 ImGui::CheckboxFlags("ImGuiColorEditFlags_NoColorMarkers", &base_flags, ImGuiColorEditFlags_NoColorMarkers);
1274 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.");
1275
1276 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit");
1277 ImGui::SeparatorText("Inline color editor");
1278 ImGui::Text("Color widget:");
1279 ImGui::SameLine(); HelpMarker(
1280 "Click on the color square to open a color picker.\n"
1281 "Ctrl+Click on individual component to input value.\n");
1282 ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags);
1283
1284 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)");
1285 ImGui::Text("Color widget HSV with Alpha:");
1286 ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | base_flags);
1287
1288 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)");
1289 ImGui::Text("Color widget with Float Display:");
1290 ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | base_flags);
1291
1292 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)");
1293 ImGui::Text("Color button with Picker:");
1294 ImGui::SameLine(); HelpMarker(
1295 "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n"
1296 "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only "
1297 "be used for the tooltip and picker popup.");
1298 ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags);
1299
1300 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)");
1301 ImGui::Text("Color button with Custom Picker Popup:");
1302
1303 // Generate a default palette. The palette will persist and can be edited.
1304 static bool saved_palette_init = true;
1305 static ImVec4 saved_palette[32] = {};
1306 if (saved_palette_init)
1307 {
1308 for (int n = 0; n < IM_COUNTOF(saved_palette); n++)
1309 {
1310 ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f,
1311 saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);
1312 saved_palette[n].w = 1.0f; // Alpha
1313 }
1314 saved_palette_init = false;
1315 }
1316
1317 static ImVec4 backup_color;
1318 bool open_popup = ImGui::ColorButton("MyColor##3b", color, base_flags);
1319 ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
1320 open_popup |= ImGui::Button("Palette");
1321 if (open_popup)
1322 {
1323 ImGui::OpenPopup("mypicker");
1324 backup_color = color;
1325 }
1326 if (ImGui::BeginPopup("mypicker"))
1327 {
1328 ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!");
1329 ImGui::Separator();
1330 ImGui::ColorPicker4("##picker", (float*)&color, base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);
1331 ImGui::SameLine();
1332
1333 ImGui::BeginGroup(); // Lock X position
1334 ImGui::Text("Current");
1335 ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40));
1336 ImGui::Text("Previous");
1337 if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)))
1338 color = backup_color;
1339 ImGui::Separator();
1340 ImGui::Text("Palette");
1341 for (int n = 0; n < IM_COUNTOF(saved_palette); n++)
1342 {
1343 ImGui::PushID(n);
1344 if ((n % 8) != 0)
1345 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
1346
1347 ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
1348 if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, ImVec2(20, 20)))
1349 color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
1350
1351 // Allow user to drop colors into each palette entry. Note that ColorButton() is already a
1352 // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
1353 if (ImGui::BeginDragDropTarget())
1354 {
1355 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
1356 memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);
1357 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
1358 memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);
1359 ImGui::EndDragDropTarget();
1360 }
1361
1362 ImGui::PopID();
1363 }
1364 ImGui::EndGroup();
1365 ImGui::EndPopup();
1366 }
1367
1368 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)");
1369 ImGui::Text("Color button only:");
1370 static bool no_border = false;
1371 ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border);
1372 ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80));
1373
1374 IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker");
1375 ImGui::SeparatorText("Color picker");
1376
1377 static bool ref_color = false;
1378 static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);
1379 static int picker_mode = 0;
1380 static int display_mode = 0;
1381 static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar;
1382
1383 ImGui::PushID("Color picker");
1384 ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &color_picker_flags, ImGuiColorEditFlags_NoAlpha);
1385 ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaBar", &color_picker_flags, ImGuiColorEditFlags_AlphaBar);
1386 ImGui::CheckboxFlags("ImGuiColorEditFlags_NoSidePreview", &color_picker_flags, ImGuiColorEditFlags_NoSidePreview);
1387 if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview)
1388 {
1389 ImGui::SameLine();
1390 ImGui::Checkbox("With Ref Color", &ref_color);
1391 if (ref_color)
1392 {
1393 ImGui::SameLine();
1394 ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags);
1395 }
1396 }
1397
1398 ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0");
1399 ImGui::SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode.");
1400
1401 ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0");
1402 ImGui::SameLine(); HelpMarker(
1403 "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
1404 "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
1405 "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
1406
1407 ImGuiColorEditFlags flags = base_flags | color_picker_flags;
1408 if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar;
1409 if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel;
1410 if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays
1411 if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode
1412 if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV;
1413 if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;
1414 ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);
1415
1416 ImGui::Text("Set defaults in code:");
1417 ImGui::SameLine(); HelpMarker(
1418 "SetColorEditOptions() is designed to allow you to set boot-time default.\n"
1419 "We don't have Push/Pop functions because you can force options on a per-widget basis if needed, "
1420 "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid "
1421 "encouraging you to persistently save values that aren't forward-compatible.");
1422 if (ImGui::Button("Default: Uint8 + HSV + Hue Bar"))
1423 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);
1424 if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
1425 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
1426
1427 // Always display a small version of both types of pickers
1428 // (that's in order to make it more visible in the demo to people who are skimming quickly through it)
1429 ImGui::Text("Both types:");
1430 float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;
1431 ImGui::SetNextItemWidth(w);
1432 ImGui::ColorPicker3("##MyColor##5", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1433 ImGui::SameLine();
1434 ImGui::SetNextItemWidth(w);
1435 ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1436 ImGui::PopID();
1437
1438 // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
1439 static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
1440 ImGui::Spacing();
1441 ImGui::Text("HSV encoded colors");
1442 ImGui::SameLine(); HelpMarker(
1443 "By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV "
1444 "allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the "
1445 "added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
1446 ImGui::Text("Color widget with InputHSV:");
1447 ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1448 ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1449 ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f);
1450
1451 ImGui::TreePop();
1452 }
1453}
1454
1455//-----------------------------------------------------------------------------
1456// [SECTION] DemoWindowWidgetsComboBoxes()
1457//-----------------------------------------------------------------------------
1458
1459static void DemoWindowWidgetsComboBoxes()
1460{
1461 if (ImGui::TreeNode("Combo"))
1462 {
1463 IMGUI_DEMO_MARKER("Widgets/Combo");
1464 // Combo Boxes are also called "Dropdown" in other systems
1465 // Expose flags as checkbox for the demo
1466 static ImGuiComboFlags flags = 0;
1467 ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft);
1468 ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo");
1469 if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton))
1470 flags &= ~ImGuiComboFlags_NoPreview; // Clear incompatible flags
1471 if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview))
1472 flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags
1473 if (ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview))
1474 flags &= ~ImGuiComboFlags_NoPreview;
1475
1476 // Override default popup height
1477 if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightSmall", &flags, ImGuiComboFlags_HeightSmall))
1478 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall);
1479 if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightRegular", &flags, ImGuiComboFlags_HeightRegular))
1480 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular);
1481 if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightLargest", &flags, ImGuiComboFlags_HeightLargest))
1482 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest);
1483
1484 // Using the generic BeginCombo() API, you have full control over how to display the combo contents.
1485 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1486 // stored in the object itself, etc.)
1487 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1488 static int item_selected_idx = 0; // Here we store our selection data as an index.
1489
1490 // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[])
1491 const char* combo_preview_value = items[item_selected_idx];
1492 if (ImGui::BeginCombo("combo 1", combo_preview_value, flags))
1493 {
1494 for (int n = 0; n < IM_COUNTOF(items); n++)
1495 {
1496 const bool is_selected = (item_selected_idx == n);
1497 if (ImGui::Selectable(items[n], is_selected))
1498 item_selected_idx = n;
1499
1500 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1501 if (is_selected)
1502 ImGui::SetItemDefaultFocus();
1503 }
1504 ImGui::EndCombo();
1505 }
1506
1507 // Show case embedding a filter using a simple trick: displaying the filter inside combo contents.
1508 // See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives.
1509 if (ImGui::BeginCombo("combo 2 (w/ filter)", combo_preview_value, flags))
1510 {
1511 static ImGuiTextFilter filter;
1512 if (ImGui::IsWindowAppearing())
1513 {
1514 ImGui::SetKeyboardFocusHere();
1515 filter.Clear();
1516 }
1517 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F);
1518 filter.Draw("##Filter", -FLT_MIN);
1519
1520 for (int n = 0; n < IM_COUNTOF(items); n++)
1521 {
1522 const bool is_selected = (item_selected_idx == n);
1523 if (filter.PassFilter(items[n]))
1524 if (ImGui::Selectable(items[n], is_selected))
1525 item_selected_idx = n;
1526 }
1527 ImGui::EndCombo();
1528 }
1529
1530 ImGui::Spacing();
1531 ImGui::SeparatorText("One-liner variants");
1532 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.");
1533
1534 // Simplified one-liner Combo() API, using values packed in a single constant string
1535 // This is a convenience for when the selection set is small and known at compile-time.
1536 static int item_current_2 = 0;
1537 ImGui::Combo("combo 3 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
1538
1539 // Simplified one-liner Combo() using an array of const char*
1540 // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
1541 static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview
1542 ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_COUNTOF(items));
1543
1544 // Simplified one-liner Combo() using an accessor function
1545 static int item_current_4 = 0;
1546 ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_COUNTOF(items));
1547
1548 ImGui::TreePop();
1549 }
1550}
1551
1552//-----------------------------------------------------------------------------
1553// [SECTION] DemoWindowWidgetsDataTypes()
1554//-----------------------------------------------------------------------------
1555
1556static void DemoWindowWidgetsDataTypes()
1557{
1558 if (ImGui::TreeNode("Data Types"))
1559 {
1560 IMGUI_DEMO_MARKER("Widgets/Data Types");
1561 // DragScalar/InputScalar/SliderScalar functions allow various data types
1562 // - signed/unsigned
1563 // - 8/16/32/64-bits
1564 // - integer/float/double
1565 // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
1566 // to pass the type, and passing all arguments by pointer.
1567 // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type.
1568 // In practice, if you frequently use a given type that is not covered by the normal API entry points,
1569 // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
1570 // and then pass their address to the generic function. For example:
1571 // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
1572 // {
1573 // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
1574 // }
1575
1576 // Setup limits (as helper variables so we can take their address, as explained above)
1577 // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
1578 #ifndef LLONG_MIN
1579 ImS64 LLONG_MIN = -9223372036854775807LL - 1;
1580 ImS64 LLONG_MAX = 9223372036854775807LL;
1581 ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);
1582 #endif
1583 const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127;
1584 const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255;
1585 const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767;
1586 const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535;
1587 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;
1588 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;
1589 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;
1590 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;
1591 const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;
1592 const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;
1593
1594 // State
1595 static char s8_v = 127;
1596 static ImU8 u8_v = 255;
1597 static short s16_v = 32767;
1598 static ImU16 u16_v = 65535;
1599 static ImS32 s32_v = -1;
1600 static ImU32 u32_v = (ImU32)-1;
1601 static ImS64 s64_v = -1;
1602 static ImU64 u64_v = (ImU64)-1;
1603 static float f32_v = 0.123f;
1604 static double f64_v = 90000.01234567890123456789;
1605
1606 const float drag_speed = 0.2f;
1607 static bool drag_clamp = false;
1608 IMGUI_DEMO_MARKER("Widgets/Data Types/Drags");
1609 ImGui::SeparatorText("Drags");
1610 ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp);
1611 ImGui::SameLine(); HelpMarker(
1612 "As with every widget in dear imgui, we never modify values unless there is a user interaction.\n"
1613 "You can override the clamping limits by using Ctrl+Click to input a value.");
1614 ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL);
1615 ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms");
1616 ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL);
1617 ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms");
1618 ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL);
1619 ImGui::DragScalar("drag s32 hex", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X");
1620 ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms");
1621 ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);
1622 ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);
1623 ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f");
1624 ImGui::DragScalar("drag float log", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", ImGuiSliderFlags_Logarithmic);
1625 ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams");
1626 ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic);
1627
1628 IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders");
1629 ImGui::SeparatorText("Sliders");
1630 ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d");
1631 ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u");
1632 ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d");
1633 ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u");
1634 ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d");
1635 ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d");
1636 ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d");
1637 ImGui::SliderScalar("slider s32 hex", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "0x%04X");
1638 ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u");
1639 ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u");
1640 ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u");
1641 ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%" PRId64);
1642 ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" PRId64);
1643 ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" PRId64);
1644 ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" PRIu64 " ms");
1645 ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" PRIu64 " ms");
1646 ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" PRIu64 " ms");
1647 ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one);
1648 ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic);
1649 ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e");
1650 ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams");
1651 ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", ImGuiSliderFlags_Logarithmic);
1652 ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams");
1653
1654 ImGui::SeparatorText("Sliders (reverse)");
1655 ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d");
1656 ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u");
1657 ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d");
1658 ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u");
1659 ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" PRId64);
1660 ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" PRIu64 " ms");
1661
1662 IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs");
1663 static bool inputs_step = true;
1664 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
1665 ImGui::SeparatorText("Inputs");
1666 ImGui::Checkbox("Show step buttons", &inputs_step);
1667 ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
1668 ImGui::CheckboxFlags("ImGuiInputTextFlags_ParseEmptyRefVal", &flags, ImGuiInputTextFlags_ParseEmptyRefVal);
1669 ImGui::CheckboxFlags("ImGuiInputTextFlags_DisplayEmptyRefVal", &flags, ImGuiInputTextFlags_DisplayEmptyRefVal);
1670 ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d", flags);
1671 ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u", flags);
1672 ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d", flags);
1673 ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u", flags);
1674 ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d", flags);
1675 ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X", flags);
1676 ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u", flags);
1677 ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", flags);
1678 ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL, NULL, NULL, flags);
1679 ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL, NULL, NULL, flags);
1680 ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL, NULL, NULL, flags);
1681 ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL, NULL, NULL, flags);
1682
1683 ImGui::TreePop();
1684 }
1685}
1686
1687//-----------------------------------------------------------------------------
1688// [SECTION] DemoWindowWidgetsDisableBlocks()
1689//-----------------------------------------------------------------------------
1690
1691static void DemoWindowWidgetsDisableBlocks(ImGuiDemoWindowData* demo_data)
1692{
1693 if (ImGui::TreeNode("Disable Blocks"))
1694 {
1695 IMGUI_DEMO_MARKER("Widgets/Disable Blocks");
1696 ImGui::Checkbox("Disable entire section above", &demo_data->DisableSections);
1697 ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across other sections.");
1698 ImGui::TreePop();
1699 }
1700}
1701
1702//-----------------------------------------------------------------------------
1703// [SECTION] DemoWindowWidgetsDragAndDrop()
1704//-----------------------------------------------------------------------------
1705
1706static void DemoWindowWidgetsDragAndDrop()
1707{
1708 if (ImGui::TreeNode("Drag and Drop"))
1709 {
1710 IMGUI_DEMO_MARKER("Widgets/Drag and drop");
1711 if (ImGui::TreeNode("Drag and drop in standard widgets"))
1712 {
1713 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets");
1714 // ColorEdit widgets automatically act as drag source and drag target.
1715 // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
1716 // to allow your own widgets to use colors in their drag and drop interaction.
1717 // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
1718 HelpMarker("You can drag from the color squares.");
1719 static float col1[3] = { 1.0f, 0.0f, 0.2f };
1720 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1721 ImGui::ColorEdit3("color 1", col1);
1722 ImGui::ColorEdit4("color 2", col2);
1723 ImGui::TreePop();
1724 }
1725
1726 if (ImGui::TreeNode("Drag and drop to copy/swap items"))
1727 {
1728 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items");
1729 enum Mode
1730 {
1731 Mode_Copy,
1732 Mode_Move,
1733 Mode_Swap
1734 };
1735 static int mode = 0;
1736 if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();
1737 if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();
1738 if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; }
1739 static const char* names[9] =
1740 {
1741 "Bobby", "Beatrice", "Betty",
1742 "Brianna", "Barry", "Bernard",
1743 "Bibi", "Blaine", "Bryn"
1744 };
1745 for (int n = 0; n < IM_COUNTOF(names); n++)
1746 {
1747 ImGui::PushID(n);
1748 if ((n % 3) != 0)
1749 ImGui::SameLine();
1750 ImGui::Button(names[n], ImVec2(60, 60));
1751
1752 // Our buttons are both drag sources and drag targets here!
1753 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
1754 {
1755 // Set payload to carry the index of our item (could be anything)
1756 ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int));
1757
1758 // Display preview (could be anything, e.g. when dragging an image we could decide to display
1759 // the filename and a small preview of the image, etc.)
1760 if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); }
1761 if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); }
1762 if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); }
1763 ImGui::EndDragDropSource();
1764 }
1765 if (ImGui::BeginDragDropTarget())
1766 {
1767 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL"))
1768 {
1769 IM_ASSERT(payload->DataSize == sizeof(int));
1770 int payload_n = *(const int*)payload->Data;
1771 if (mode == Mode_Copy)
1772 {
1773 names[n] = names[payload_n];
1774 }
1775 if (mode == Mode_Move)
1776 {
1777 names[n] = names[payload_n];
1778 names[payload_n] = "";
1779 }
1780 if (mode == Mode_Swap)
1781 {
1782 const char* tmp = names[n];
1783 names[n] = names[payload_n];
1784 names[payload_n] = tmp;
1785 }
1786 }
1787 ImGui::EndDragDropTarget();
1788 }
1789 ImGui::PopID();
1790 }
1791 ImGui::TreePop();
1792 }
1793
1794 if (ImGui::TreeNode("Drag to reorder items (simple)"))
1795 {
1796 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)");
1797 // FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
1798 // This code was always slightly faulty but in a way which was not easily noticeable.
1799 // Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
1800 ImGui::PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true);
1801
1802 // Simple reordering
1803 HelpMarker(
1804 "We don't use the drag and drop api at all here! "
1805 "Instead we query when the item is held but not hovered, and order items accordingly.");
1806 static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
1807 for (int n = 0; n < IM_COUNTOF(item_names); n++)
1808 {
1809 const char* item = item_names[n];
1810 ImGui::Selectable(item);
1811
1812 if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
1813 {
1814 int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1);
1815 if (n_next >= 0 && n_next < IM_COUNTOF(item_names))
1816 {
1817 item_names[n] = item_names[n_next];
1818 item_names[n_next] = item;
1819 ImGui::ResetMouseDragDelta();
1820 }
1821 }
1822 }
1823
1824 ImGui::PopItemFlag();
1825 ImGui::TreePop();
1826 }
1827
1828 if (ImGui::TreeNode("Tooltip at target location"))
1829 {
1830 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location");
1831 for (int n = 0; n < 2; n++)
1832 {
1833 // Drop targets
1834 ImGui::Button(n ? "drop here##1" : "drop here##0");
1835 if (ImGui::BeginDragDropTarget())
1836 {
1837 ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip;
1838 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags))
1839 {
1840 IM_UNUSED(payload);
1841 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
1842 ImGui::SetTooltip("Cannot drop here!");
1843 }
1844 ImGui::EndDragDropTarget();
1845 }
1846
1847 // Drop source
1848 static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f };
1849 if (n == 0)
1850 ImGui::ColorButton("drag me", col4);
1851
1852 }
1853 ImGui::TreePop();
1854 }
1855
1856 ImGui::TreePop();
1857 }
1858}
1859
1860//-----------------------------------------------------------------------------
1861// [SECTION] DemoWindowWidgetsDragsAndSliders()
1862//-----------------------------------------------------------------------------
1863
1864static void DemoWindowWidgetsDragsAndSliders()
1865{
1866 if (ImGui::TreeNode("Drag/Slider Flags"))
1867 {
1868 IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags");
1869 // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
1870 static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
1871 ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp);
1872 ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput);
1873 ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds.");
1874 ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange);
1875 ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
1876 ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic);
1877 ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values).");
1878 ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat);
1879 ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
1880 ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput);
1881 ImGui::SameLine(); HelpMarker("Disable Ctrl+Click or Enter key allowing to input text directly into the widget.");
1882 ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks);
1883 ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
1884 ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround);
1885 ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)");
1886 ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkers", &flags, ImGuiSliderFlags_ColorMarkers);
1887
1888 // Drags
1889 static float drag_f = 0.5f;
1890 static float drag_f4[4];
1891 static int drag_i = 50;
1892 ImGui::Text("Underlying float value: %f", drag_f);
1893 ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags);
1894 ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags);
1895 ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags);
1896 ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags);
1897 //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange
1898 //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags);
1899 ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags);
1900 ImGui::DragFloat4("DragFloat4 (0 -> 1)", drag_f4, 0.005f, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers.
1901
1902 // Sliders
1903 static float slider_f = 0.5f;
1904 static float slider_f4[4];
1905 static int slider_i = 50;
1906 const ImGuiSliderFlags flags_for_sliders = (flags & ~ImGuiSliderFlags_WrapAround);
1907 ImGui::Text("Underlying float value: %f", slider_f);
1908 ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders);
1909 ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders);
1910 ImGui::SliderFloat4("SliderFloat4 (0 -> 1)", slider_f4, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers.
1911
1912 ImGui::TreePop();
1913 }
1914}
1915
1916//-----------------------------------------------------------------------------
1917// [SECTION] DemoWindowWidgetsFonts()
1918//-----------------------------------------------------------------------------
1919
1920static void DemoWindowWidgetsFonts()
1921{
1922 if (ImGui::TreeNode("Fonts"))
1923 {
1924 IMGUI_DEMO_MARKER("Widgets/Fonts");
1925 ImFontAtlas* atlas = ImGui::GetIO().Fonts;
1926 ImGui::ShowFontAtlas(atlas);
1927 // FIXME-NEWATLAS: Provide a demo to add/create a procedural font?
1928 ImGui::TreePop();
1929 }
1930}
1931
1932//-----------------------------------------------------------------------------
1933// [SECTION] DemoWindowWidgetsImages()
1934//-----------------------------------------------------------------------------
1935
1936static void DemoWindowWidgetsImages()
1937{
1938 if (ImGui::TreeNode("Images"))
1939 {
1940 IMGUI_DEMO_MARKER("Widgets/Images");
1941 ImGuiIO& io = ImGui::GetIO();
1942 ImGui::TextWrapped(
1943 "Below we are displaying the font texture (which is the only texture we have access to in this demo). "
1944 "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. "
1945 "Hover the texture for a zoomed view!");
1946
1947 // Below we are displaying the font texture because it is the only texture we have access to inside the demo!
1948 // Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers.
1949 // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
1950 // of their respective source file to specify what they are using as texture identifier, for example:
1951 // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer.
1952 // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
1953 // So with the DirectX11 backend, you call ImGui::Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID.
1954 // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
1955 // to ImGui::Image(), and gather width/height through your own functions, etc.
1956 // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
1957 // it will help you debug issues if you are confused about it.
1958 // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
1959 // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
1960 // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1961
1962 // Grab the current texture identifier used by the font atlas.
1963 ImFontAtlas* atlas = io.Fonts;
1964 ImTextureRef my_tex_id = atlas->TexRef;
1965 float my_tex_w = (float)atlas->TexData->Width; // 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.
1966 float my_tex_h = (float)atlas->TexData->Height;
1967 ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
1968
1969 // Basic drawing
1970 ImGui::SeparatorText("Image()/ImageWithBg() function");
1971 ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
1972 ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
1973 ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
1974 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));
1975 ImGui::PopStyleVar();
1976
1977 // Fancy widget
1978 ImGui::SeparatorText("Interactive Image Viewer");
1979 static ExampleImageViewerData image_viewer;
1980 ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, my_tex_h * 2.0f);
1981 ExampleImageViewer_DrawOptions(&image_viewer);
1982 ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, my_tex_id, (int)my_tex_w, (int)my_tex_h);
1983
1984 IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons");
1985 ImGui::SeparatorText("Textured Buttons");
1986 ImGui::TextWrapped("And now some textured buttons..");
1987 static int pressed_count = 0;
1988 for (int i = 0; i < 8; i++)
1989 {
1990 // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.
1991 // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.
1992 // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1993 ImGui::PushID(i);
1994 if (i > 0)
1995 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(i - 1.0f, i - 1.0f));
1996 ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible
1997 ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left
1998 ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h); // UV coordinates for (32,32) in our texture
1999 ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background
2000 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
2001 if (ImGui::ImageButton("", my_tex_id, size, uv0, uv1, bg_col, tint_col))
2002 pressed_count += 1;
2003 if (i > 0)
2004 ImGui::PopStyleVar();
2005 ImGui::PopID();
2006 ImGui::SameLine();
2007 }
2008 ImGui::NewLine();
2009 ImGui::Text("Pressed %d times.", pressed_count);
2010 ImGui::TreePop();
2011 }
2012}
2013
2014//-----------------------------------------------------------------------------
2015// [SECTION] DemoWindowWidgetsListBoxes()
2016//-----------------------------------------------------------------------------
2017
2018static void DemoWindowWidgetsListBoxes()
2019{
2020 if (ImGui::TreeNode("List Boxes"))
2021 {
2022 IMGUI_DEMO_MARKER("Widgets/List Boxes");
2023 // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild()
2024 // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
2025 // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild()
2026 // to always be called (inconsistent with BeginListBox()/EndListBox()).
2027
2028 // Using the generic BeginListBox() API, you have full control over how to display the combo contents.
2029 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
2030 // stored in the object itself, etc.)
2031 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
2032 static int item_selected_idx = 0; // Here we store our selected data as an index.
2033
2034 static bool item_highlight = false;
2035 int item_highlighted_idx = -1; // Here we store our highlighted data as an index.
2036 ImGui::Checkbox("Highlight hovered item in second listbox", &item_highlight);
2037
2038 if (ImGui::BeginListBox("listbox 1"))
2039 {
2040 for (int n = 0; n < IM_COUNTOF(items); n++)
2041 {
2042 const bool is_selected = (item_selected_idx == n);
2043 if (ImGui::Selectable(items[n], is_selected))
2044 item_selected_idx = n;
2045
2046 if (item_highlight && ImGui::IsItemHovered())
2047 item_highlighted_idx = n;
2048
2049 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
2050 if (is_selected)
2051 ImGui::SetItemDefaultFocus();
2052 }
2053 ImGui::EndListBox();
2054 }
2055 ImGui::SameLine(); HelpMarker("Here we are sharing selection state between both boxes.");
2056
2057 // Custom size: use all width, 5 items tall
2058 ImGui::Text("Full-width:");
2059 if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
2060 {
2061 for (int n = 0; n < IM_COUNTOF(items); n++)
2062 {
2063 bool is_selected = (item_selected_idx == n);
2064 ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0;
2065 if (ImGui::Selectable(items[n], is_selected, flags))
2066 item_selected_idx = n;
2067
2068 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
2069 if (is_selected)
2070 ImGui::SetItemDefaultFocus();
2071 }
2072 ImGui::EndListBox();
2073 }
2074
2075 ImGui::TreePop();
2076 }
2077}
2078
2079//-----------------------------------------------------------------------------
2080// [SECTION] DemoWindowWidgetsMultiComponents()
2081//-----------------------------------------------------------------------------
2082
2083static void DemoWindowWidgetsMultiComponents()
2084{
2085 if (ImGui::TreeNode("Multi-component Widgets"))
2086 {
2087 IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets");
2088 static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
2089 static int vec4i[4] = { 1, 5, 100, 255 };
2090
2091 static ImGuiSliderFlags flags = 0;
2092 ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkers", &flags, ImGuiSliderFlags_ColorMarkers); // Only passing this to Drag/Sliders
2093
2094 ImGui::SeparatorText("2-wide");
2095 ImGui::InputFloat2("input float2", vec4f);
2096 ImGui::InputInt2("input int2", vec4i);
2097 ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags);
2098 ImGui::DragInt2("drag int2", vec4i, 1, 0, 255, NULL, flags);
2099 ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f, NULL, flags);
2100 ImGui::SliderInt2("slider int2", vec4i, 0, 255, NULL, flags);
2101
2102 ImGui::SeparatorText("3-wide");
2103 ImGui::InputFloat3("input float3", vec4f);
2104 ImGui::InputInt3("input int3", vec4i);
2105 ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags);
2106 ImGui::DragInt3("drag int3", vec4i, 1, 0, 255, NULL, flags);
2107 ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f, NULL, flags);
2108 ImGui::SliderInt3("slider int3", vec4i, 0, 255, NULL, flags);
2109
2110 ImGui::SeparatorText("4-wide");
2111 ImGui::InputFloat4("input float4", vec4f);
2112 ImGui::InputInt4("input int4", vec4i);
2113 ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags);
2114 ImGui::DragInt4("drag int4", vec4i, 1, 0, 255, NULL, flags);
2115 ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f, NULL, flags);
2116 ImGui::SliderInt4("slider int4", vec4i, 0, 255, NULL, flags);
2117
2118 ImGui::SeparatorText("Ranges");
2119 static float begin = 10, end = 90;
2120 static int begin_i = 100, end_i = 1000;
2121 ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_AlwaysClamp);
2122 ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units");
2123 ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units");
2124
2125 ImGui::TreePop();
2126 }
2127}
2128
2129//-----------------------------------------------------------------------------
2130// [SECTION] DemoWindowWidgetsPlotting()
2131//-----------------------------------------------------------------------------
2132
2133static void DemoWindowWidgetsPlotting()
2134{
2135 // Plot/Graph widgets are not very good.
2136// Consider using a third-party library such as ImPlot: https://github.com/epezent/implot
2137// (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)
2138 if (ImGui::TreeNode("Plotting"))
2139 {
2140 IMGUI_DEMO_MARKER("Widgets/Plotting");
2141 ImGui::Text("Need better plotting and graphing? Consider using ImPlot:");
2142 ImGui::TextLinkOpenURL("https://github.com/epezent/implot");
2143 ImGui::Separator();
2144
2145 static bool animate = true;
2146 ImGui::Checkbox("Animate", &animate);
2147
2148 // Plot as lines and plot as histogram
2149 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
2150 ImGui::PlotLines("Frame Times", arr, IM_COUNTOF(arr));
2151 ImGui::PlotHistogram("Histogram", arr, IM_COUNTOF(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));
2152 //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!");
2153
2154 // Fill an array of contiguous float values to plot
2155 // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
2156 // and the sizeof() of your structure in the "stride" parameter.
2157 static float values[90] = {};
2158 static int values_offset = 0;
2159 static double refresh_time = 0.0;
2160 if (!animate || refresh_time == 0.0)
2161 refresh_time = ImGui::GetTime();
2162 while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
2163 {
2164 static float phase = 0.0f;
2165 values[values_offset] = cosf(phase);
2166 values_offset = (values_offset + 1) % IM_COUNTOF(values);
2167 phase += 0.10f * values_offset;
2168 refresh_time += 1.0f / 60.0f;
2169 }
2170
2171 // Plots can display overlay texts
2172 // (in this example, we will display an average value)
2173 {
2174 float average = 0.0f;
2175 for (int n = 0; n < IM_COUNTOF(values); n++)
2176 average += values[n];
2177 average /= (float)IM_COUNTOF(values);
2178 char overlay[32];
2179 sprintf(overlay, "avg %f", average);
2180 ImGui::PlotLines("Lines", values, IM_COUNTOF(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f));
2181 }
2182
2183 // Use functions to generate output
2184 // FIXME: This is actually VERY awkward because current plot API only pass in indices.
2185 // We probably want an API passing floats and user provide sample rate/count.
2186 struct Funcs
2187 {
2188 static float Sin(void*, int i) { return sinf(i * 0.1f); }
2189 static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }
2190 };
2191 static int func_type = 0, display_count = 70;
2192 ImGui::SeparatorText("Functions");
2193 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
2194 ImGui::Combo("func", &func_type, "Sin\0Saw\0");
2195 ImGui::SameLine();
2196 ImGui::SliderInt("Sample count", &display_count, 1, 400);
2197 float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
2198 ImGui::PlotLines("Lines##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
2199 ImGui::PlotHistogram("Histogram##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
2200
2201 ImGui::TreePop();
2202 }
2203}
2204
2205//-----------------------------------------------------------------------------
2206// [SECTION] DemoWindowWidgetsProgressBars()
2207//-----------------------------------------------------------------------------
2208
2209static void DemoWindowWidgetsProgressBars()
2210{
2211 if (ImGui::TreeNode("Progress Bars"))
2212 {
2213 IMGUI_DEMO_MARKER("Widgets/Progress Bars");
2214 // Animate a simple progress bar
2215 static float progress_accum = 0.0f, progress_dir = 1.0f;
2216 progress_accum += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
2217 if (progress_accum >= +1.1f) { progress_accum = +1.1f; progress_dir *= -1.0f; }
2218 if (progress_accum <= -0.1f) { progress_accum = -0.1f; progress_dir *= -1.0f; }
2219
2220 const float progress = IM_CLAMP(progress_accum, 0.0f, 1.0f);
2221
2222 // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
2223 // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
2224 ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));
2225 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
2226 ImGui::Text("Progress Bar");
2227
2228 char buf[32];
2229 sprintf(buf, "%d/%d", (int)(progress * 1753), 1753);
2230 ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);
2231
2232 // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value.
2233 // Adjust the factor if you want to adjust the animation speed.
2234 ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), "Searching..");
2235 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
2236 ImGui::Text("Indeterminate");
2237
2238 ImGui::TreePop();
2239 }
2240}
2241
2242//-----------------------------------------------------------------------------
2243// [SECTION] DemoWindowWidgetsQueryingStatuses()
2244//-----------------------------------------------------------------------------
2245
2246static void DemoWindowWidgetsQueryingStatuses()
2247{
2248 if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)"))
2249 {
2250 IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)");
2251 // Select an item type
2252 const char* item_names[] =
2253 {
2254 "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat",
2255 "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox"
2256 };
2257 static int item_type = 4;
2258 static bool item_disabled = false;
2259 ImGui::Combo("Item Type", &item_type, item_names, IM_COUNTOF(item_names), IM_COUNTOF(item_names));
2260 ImGui::SameLine();
2261 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().");
2262 ImGui::Checkbox("Item Disabled", &item_disabled);
2263
2264 // Submit selected items so we can query their status in the code following it.
2265 bool ret = false;
2266 static bool b = false;
2267 static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
2268 static char str[16] = {};
2269 if (item_disabled)
2270 ImGui::BeginDisabled(true);
2271 if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction
2272 if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button
2273 if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater)
2274 if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox
2275 if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item
2276 if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_COUNTOF(str)); } // Testing input text (which handles tabbing)
2277 if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_COUNTOF(str)); } // Testing input text (which uses a child window)
2278 if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input
2279 if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2280 if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2281 if (item_type == 10) { ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item
2282 if (item_type == 11) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)
2283 if (item_type == 12) { ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node
2284 if (item_type == 13) { ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.
2285 if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", &current, items, IM_COUNTOF(items)); }
2286 if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", &current, items, IM_COUNTOF(items), IM_COUNTOF(items)); }
2287
2288 bool hovered_delay_none = ImGui::IsItemHovered();
2289 bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary);
2290 bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort);
2291 bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal);
2292 bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary
2293
2294 // Display the values of IsItemHovered() and other common item state functions.
2295 // Note that the ImGuiHoveredFlags_XXX flags can be combined.
2296 // Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
2297 // we query every state in a single call to avoid storing them and to simplify the code.
2298 ImGui::BulletText(
2299 "Return value = %d\n"
2300 "IsItemFocused() = %d\n"
2301 "IsItemHovered() = %d\n"
2302 "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
2303 "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2304 "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n"
2305 "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n"
2306 "IsItemHovered(_AllowWhenDisabled) = %d\n"
2307 "IsItemHovered(_RectOnly) = %d\n"
2308 "IsItemActive() = %d\n"
2309 "IsItemEdited() = %d\n"
2310 "IsItemActivated() = %d\n"
2311 "IsItemDeactivated() = %d\n"
2312 "IsItemDeactivatedAfterEdit() = %d\n"
2313 "IsItemVisible() = %d\n"
2314 "IsItemClicked() = %d\n"
2315 "IsItemToggledOpen() = %d\n"
2316 "GetItemRectMin() = (%.1f, %.1f)\n"
2317 "GetItemRectMax() = (%.1f, %.1f)\n"
2318 "GetItemRectSize() = (%.1f, %.1f)",
2319 ret,
2320 ImGui::IsItemFocused(),
2321 ImGui::IsItemHovered(),
2322 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2323 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2324 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem),
2325 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow),
2326 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled),
2327 ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),
2328 ImGui::IsItemActive(),
2329 ImGui::IsItemEdited(),
2330 ImGui::IsItemActivated(),
2331 ImGui::IsItemDeactivated(),
2332 ImGui::IsItemDeactivatedAfterEdit(),
2333 ImGui::IsItemVisible(),
2334 ImGui::IsItemClicked(),
2335 ImGui::IsItemToggledOpen(),
2336 ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y,
2337 ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y,
2338 ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y
2339 );
2340 ImGui::BulletText(
2341 "with Hovering Delay or Stationary test:\n"
2342 "IsItemHovered() = %d\n"
2343 "IsItemHovered(_Stationary) = %d\n"
2344 "IsItemHovered(_DelayShort) = %d\n"
2345 "IsItemHovered(_DelayNormal) = %d\n"
2346 "IsItemHovered(_Tooltip) = %d",
2347 hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);
2348
2349 if (item_disabled)
2350 ImGui::EndDisabled();
2351
2352 char buf[1] = "";
2353 ImGui::InputText("unused", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_ReadOnly);
2354 ImGui::SameLine();
2355 HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.");
2356
2357 ImGui::TreePop();
2358 }
2359
2360 if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)"))
2361 {
2362 IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)");
2363 static bool embed_all_inside_a_child_window = false;
2364 ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window);
2365 if (embed_all_inside_a_child_window)
2366 ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Borders);
2367
2368 // Testing IsWindowFocused() function with its various flags.
2369 ImGui::BulletText(
2370 "IsWindowFocused() = %d\n"
2371 "IsWindowFocused(_ChildWindows) = %d\n"
2372 "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n"
2373 "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n"
2374 "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
2375 "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2376 "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
2377 "IsWindowFocused(_RootWindow) = %d\n"
2378 "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n"
2379 "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n"
2380 "IsWindowFocused(_AnyWindow) = %d\n",
2381 ImGui::IsWindowFocused(),
2382 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),
2383 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),
2384 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy),
2385 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
2386 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2387 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
2388 ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow),
2389 ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2390 ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
2391 ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));
2392
2393 // Testing IsWindowHovered() function with its various flags.
2394 ImGui::BulletText(
2395 "IsWindowHovered() = %d\n"
2396 "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
2397 "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2398 "IsWindowHovered(_ChildWindows) = %d\n"
2399 "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n"
2400 "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n"
2401 "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
2402 "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2403 "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
2404 "IsWindowHovered(_RootWindow) = %d\n"
2405 "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
2406 "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n"
2407 "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
2408 "IsWindowHovered(_AnyWindow) = %d\n"
2409 "IsWindowHovered(_Stationary) = %d\n",
2410 ImGui::IsWindowHovered(),
2411 ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2412 ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2413 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),
2414 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),
2415 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy),
2416 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
2417 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2418 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
2419 ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),
2420 ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2421 ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
2422 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2423 ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow),
2424 ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary));
2425
2426 ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Borders);
2427 ImGui::Text("This is another child window for testing the _ChildWindows flag.");
2428 ImGui::EndChild();
2429 if (embed_all_inside_a_child_window)
2430 ImGui::EndChild();
2431
2432 // Calling IsItemHovered() after begin returns the hovered status of the title bar.
2433 // This is useful in particular if you want to create a context menu associated to the title bar of a window.
2434 // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).
2435 static bool test_window = false;
2436 ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window);
2437 if (test_window)
2438 {
2439 // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck.
2440 // Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as "don't skip items"?
2441 ImGui::Begin("Title bar Hovered/Active tests", &test_window);
2442 if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()
2443 {
2444 if (ImGui::MenuItem("Close")) { test_window = false; }
2445 ImGui::EndPopup();
2446 }
2447 ImGui::Text(
2448 "IsItemHovered() after begin = %d (== is title bar hovered)\n"
2449 "IsItemActive() after begin = %d (== is window being clicked/moved)\n",
2450 ImGui::IsItemHovered(), ImGui::IsItemActive());
2451 ImGui::End();
2452 }
2453
2454 ImGui::TreePop();
2455 }
2456}
2457
2458//-----------------------------------------------------------------------------
2459// [SECTION] DemoWindowWidgetsSelectables()
2460//-----------------------------------------------------------------------------
2461
2462static void DemoWindowWidgetsSelectables()
2463{
2464 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
2465 if (ImGui::TreeNode("Selectables"))
2466 {
2467 IMGUI_DEMO_MARKER("Widgets/Selectables");
2468 // Selectable() has 2 overloads:
2469 // - The one taking "bool selected" as a read-only selection information.
2470 // When Selectable() has been clicked it returns true and you can alter selection state accordingly.
2471 // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
2472 // The earlier is more flexible, as in real application your selection may be stored in many different ways
2473 // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
2474 IMGUI_DEMO_MARKER("Widgets/Selectables/Basic");
2475 if (ImGui::TreeNode("Basic"))
2476 {
2477 static bool selection[5] = { false, true, false, false };
2478 ImGui::Selectable("1. I am selectable", &selection[0]);
2479 ImGui::Selectable("2. I am selectable", &selection[1]);
2480 ImGui::Selectable("3. I am selectable", &selection[2]);
2481 if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick))
2482 if (ImGui::IsMouseDoubleClicked(0))
2483 selection[3] = !selection[3];
2484 ImGui::TreePop();
2485 }
2486
2487 IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line");
2488 if (ImGui::TreeNode("Multiple items on the same line"))
2489 {
2490 IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple items on the same line");
2491 // - Using SetNextItemAllowOverlap()
2492 // - Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically.
2493 {
2494 static bool selected[3] = {};
2495 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1");
2496 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2");
2497 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3");
2498 }
2499
2500 // (2)
2501 // - Using ImGuiSelectableFlags_AllowOverlap is a shortcut for calling SetNextItemAllowOverlap()
2502 // - No visible label, display contents inside the selectable bounds.
2503 // - We don't maintain actual selection in this example to keep things simple.
2504 ImGui::Spacing();
2505 {
2506 static bool checked[5] = {};
2507 static int selected_n = 0;
2508 const float color_marker_w = ImGui::CalcTextSize("x").x;
2509 for (int n = 0; n < 5; n++)
2510 {
2511 ImGui::PushID(n);
2512 ImGui::AlignTextToFramePadding();
2513 if (ImGui::Selectable("##selectable", selected_n == n, ImGuiSelectableFlags_AllowOverlap))
2514 selected_n = n;
2515 ImGui::SameLine(0, 0);
2516 ImGui::Checkbox("##check", &checked[n]);
2517 ImGui::SameLine();
2518 ImVec4 color((n & 1) ? 1.0f : 0.2f, (n & 2) ? 1.0f : 0.2f, 0.2f, 1.0f);
2519 ImGui::ColorButton("##color", color, ImGuiColorEditFlags_NoTooltip, ImVec2(color_marker_w, 0));
2520 ImGui::SameLine();
2521 ImGui::Text("Some label");
2522 ImGui::PopID();
2523 }
2524 }
2525
2526 ImGui::TreePop();
2527 }
2528
2529 if (ImGui::TreeNode("In Tables"))
2530 {
2531 IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables");
2532 static bool selected[10] = {};
2533
2534 if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2535 {
2536 for (int i = 0; i < 10; i++)
2537 {
2538 char label[32];
2539 sprintf(label, "Item %d", i);
2540 ImGui::TableNextColumn();
2541 ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap
2542 }
2543 ImGui::EndTable();
2544 }
2545 ImGui::Spacing();
2546 if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2547 {
2548 for (int i = 0; i < 10; i++)
2549 {
2550 char label[32];
2551 sprintf(label, "Item %d", i);
2552 ImGui::TableNextRow();
2553 ImGui::TableNextColumn();
2554 ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns);
2555 ImGui::TableNextColumn();
2556 ImGui::Text("Some other contents");
2557 ImGui::TableNextColumn();
2558 ImGui::Text("123456");
2559 }
2560 ImGui::EndTable();
2561 }
2562 ImGui::TreePop();
2563 }
2564
2565 if (ImGui::TreeNode("Grid"))
2566 {
2567 IMGUI_DEMO_MARKER("Widgets/Selectables/Grid");
2568 static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
2569
2570 // Add in a bit of silly fun...
2571 const float time = (float)ImGui::GetTime();
2572 const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected...
2573 if (winning_state)
2574 ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f)));
2575
2576 const float size = ImGui::CalcTextSize("Sailor").x;
2577 for (int y = 0; y < 4; y++)
2578 for (int x = 0; x < 4; x++)
2579 {
2580 if (x > 0)
2581 ImGui::SameLine();
2582 ImGui::PushID(y * 4 + x);
2583 if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(size, size)))
2584 {
2585 // Toggle clicked cell + toggle neighbors
2586 selected[y][x] ^= 1;
2587 if (x > 0) { selected[y][x - 1] ^= 1; }
2588 if (x < 3) { selected[y][x + 1] ^= 1; }
2589 if (y > 0) { selected[y - 1][x] ^= 1; }
2590 if (y < 3) { selected[y + 1][x] ^= 1; }
2591 }
2592 ImGui::PopID();
2593 }
2594
2595 if (winning_state)
2596 ImGui::PopStyleVar();
2597 ImGui::TreePop();
2598 }
2599 if (ImGui::TreeNode("Alignment"))
2600 {
2601 IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment");
2602 HelpMarker(
2603 "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item "
2604 "basis using PushStyleVar(). You'll probably want to always keep your default situation to "
2605 "left-align otherwise it becomes difficult to layout multiple items on a same line");
2606
2607 static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };
2608 const float size = ImGui::CalcTextSize("(1.0,1.0)").x;
2609 for (int y = 0; y < 3; y++)
2610 {
2611 for (int x = 0; x < 3; x++)
2612 {
2613 ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);
2614 char name[32];
2615 sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y);
2616 if (x > 0) ImGui::SameLine();
2617 ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);
2618 ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(size, size));
2619 ImGui::PopStyleVar();
2620 }
2621 }
2622 ImGui::TreePop();
2623 }
2624 ImGui::TreePop();
2625 }
2626}
2627
2628//-----------------------------------------------------------------------------
2629// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
2630//-----------------------------------------------------------------------------
2631// Multi-selection demos
2632// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select
2633//-----------------------------------------------------------------------------
2634
2635static const char* ExampleNames[] =
2636{
2637 "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper",
2638 "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava",
2639 "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
2640};
2641
2642// Extra functions to add deletion support to ImGuiSelectionBasicStorage
2643struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage
2644{
2645 // Find which item should be Focused after deletion.
2646 // Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
2647 // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
2648 // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
2649 // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.
2650 // - 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.
2651 // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset.
2652 int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count)
2653 {
2654 if (Size == 0)
2655 return -1;
2656
2657 // If focused item is not selected...
2658 const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item
2659 if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx))
2660 {
2661 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.
2662 return focused_idx; // Request to focus same item after deletion.
2663 }
2664
2665 // If focused item is selected: land on first unselected item after focused item.
2666 for (int idx = focused_idx + 1; idx < items_count; idx++)
2667 if (!Contains(GetStorageIdFromIndex(idx)))
2668 return idx;
2669
2670 // If focused item is selected: otherwise return last unselected item before focused item.
2671 for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--)
2672 if (!Contains(GetStorageIdFromIndex(idx)))
2673 return idx;
2674
2675 return -1;
2676 }
2677
2678 // Rewrite item list (delete items) + update selection.
2679 // - Call after EndMultiSelect()
2680 // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data.
2681 template<typename ITEM_TYPE>
2682 void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& items, int item_curr_idx_to_select)
2683 {
2684 // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection).
2685 // If NavId was not part of selection, we will stay on same item.
2686 ImVector<ITEM_TYPE> new_items;
2687 new_items.reserve(items.Size - Size);
2688 int item_next_idx_to_select = -1;
2689 for (int idx = 0; idx < items.Size; idx++)
2690 {
2691 if (!Contains(GetStorageIdFromIndex(idx)))
2692 new_items.push_back(items[idx]);
2693 if (item_curr_idx_to_select == idx)
2694 item_next_idx_to_select = new_items.Size - 1;
2695 }
2696 items.swap(new_items);
2697
2698 // Update selection
2699 Clear();
2700 if (item_next_idx_to_select != -1 && ms_io->NavIdSelected)
2701 SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true);
2702 }
2703};
2704
2705// Example: Implement dual list box storage and interface
2706struct ExampleDualListBox
2707{
2708 ImVector<ImGuiID> Items[2]; // ID is index into ExampleName[]
2709 ImGuiSelectionBasicStorage Selections[2]; // Store ExampleItemId into selection
2710 bool OptKeepSorted = true;
2711
2712 void MoveAll(int src, int dst)
2713 {
2714 IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0));
2715 for (ImGuiID item_id : Items[src])
2716 Items[dst].push_back(item_id);
2717 Items[src].clear();
2718 SortItems(dst);
2719 Selections[src].Swap(Selections[dst]);
2720 Selections[src].Clear();
2721 }
2722 void MoveSelected(int src, int dst)
2723 {
2724 for (int src_n = 0; src_n < Items[src].Size; src_n++)
2725 {
2726 ImGuiID item_id = Items[src][src_n];
2727 if (!Selections[src].Contains(item_id))
2728 continue;
2729 Items[src].erase(&Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap)
2730 Items[dst].push_back(item_id);
2731 src_n--;
2732 }
2733 if (OptKeepSorted)
2734 SortItems(dst);
2735 Selections[src].Swap(Selections[dst]);
2736 Selections[src].Clear();
2737 }
2738 void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side)
2739 {
2740 // In this example we store item id in selection (instead of item index)
2741 Selections[side].UserData = Items[side].Data;
2742 Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; };
2743 Selections[side].ApplyRequests(ms_io);
2744 }
2745 static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)
2746 {
2747 const int* a = (const int*)lhs;
2748 const int* b = (const int*)rhs;
2749 return *a - *b;
2750 }
2751 void SortItems(int n)
2752 {
2753 qsort(Items[n].Data, (size_t)Items[n].Size, sizeof(Items[n][0]), CompareItemsByValue);
2754 }
2755 void Show()
2756 {
2757 //if (ImGui::Checkbox("Sorted", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); }
2758 if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None))
2759 {
2760 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side
2761 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Buttons
2762 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Right side
2763 ImGui::TableNextRow();
2764
2765 int request_move_selected = -1;
2766 int request_move_all = -1;
2767 float child_height_0 = 0.0f;
2768 for (int side = 0; side < 2; side++)
2769 {
2770 // FIXME-MULTISELECT: Dual List Box: Add context menus
2771 // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.
2772 ImVector<ImGuiID>& items = Items[side];
2773 ImGuiSelectionBasicStorage& selection = Selections[side];
2774
2775 ImGui::TableSetColumnIndex((side == 0) ? 0 : 2);
2776 ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size);
2777
2778 // Submit scrolling range to avoid glitches on moving/deletion
2779 const float items_height = ImGui::GetTextLineHeightWithSpacing();
2780 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2781
2782 bool child_visible;
2783 if (side == 0)
2784 {
2785 // Left child is resizable
2786 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), ImVec2(FLT_MAX, FLT_MAX));
2787 child_visible = ImGui::BeginChild("0", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY);
2788 child_height_0 = ImGui::GetWindowSize().y;
2789 }
2790 else
2791 {
2792 // Right child use same height as left one
2793 child_visible = ImGui::BeginChild("1", ImVec2(-FLT_MIN, child_height_0), ImGuiChildFlags_FrameStyle);
2794 }
2795 if (child_visible)
2796 {
2797 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_BoxSelect1d;
2798 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
2799 ApplySelectionRequests(ms_io, side);
2800
2801 for (int item_n = 0; item_n < items.Size; item_n++)
2802 {
2803 ImGuiID item_id = items[item_n];
2804 bool item_is_selected = selection.Contains(item_id);
2805 ImGui::SetNextItemSelectionUserData(item_n);
2806 ImGui::Selectable(ExampleNames[item_id], item_is_selected, ImGuiSelectableFlags_AllowDoubleClick);
2807 if (ImGui::IsItemFocused())
2808 {
2809 // FIXME-MULTISELECT: Dual List Box: Transfer focus
2810 if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))
2811 request_move_selected = side;
2812 if (ImGui::IsMouseDoubleClicked(0)) // FIXME-MULTISELECT: Double-click on multi-selection?
2813 request_move_selected = side;
2814 }
2815 }
2816
2817 ms_io = ImGui::EndMultiSelect();
2818 ApplySelectionRequests(ms_io, side);
2819 }
2820 ImGui::EndChild();
2821 }
2822
2823 // Buttons columns
2824 ImGui::TableSetColumnIndex(1);
2825 ImGui::NewLine();
2826 //ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f };
2827 ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() };
2828
2829 // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized)
2830 if (ImGui::Button(">>", button_sz))
2831 request_move_all = 0;
2832 if (ImGui::Button(">", button_sz))
2833 request_move_selected = 0;
2834 if (ImGui::Button("<", button_sz))
2835 request_move_selected = 1;
2836 if (ImGui::Button("<<", button_sz))
2837 request_move_all = 1;
2838
2839 // Process requests
2840 if (request_move_all != -1)
2841 MoveAll(request_move_all, request_move_all ^ 1);
2842 if (request_move_selected != -1)
2843 MoveSelected(request_move_selected, request_move_selected ^ 1);
2844
2845 // FIXME-MULTISELECT: Support action from outside
2846 /*
2847 if (OptKeepSorted == false)
2848 {
2849 ImGui::NewLine();
2850 if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {}
2851 if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {}
2852 }
2853 */
2854
2855 ImGui::EndTable();
2856 }
2857 }
2858};
2859
2860static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_data)
2861{
2862 if (ImGui::TreeNode("Selection State & Multi-Select"))
2863 {
2864 IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select");
2865 HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
2866
2867 ImGui::BulletText("Wiki page:");
2868 ImGui::SameLine();
2869 ImGui::TextLinkOpenURL("imgui/wiki/Multi-Select", "https://github.com/ocornut/imgui/wiki/Multi-Select");
2870
2871 // Without any fancy API: manage single-selection yourself.
2872 if (ImGui::TreeNode("Single-Select"))
2873 {
2874 IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
2875 static int selected = -1;
2876 for (int n = 0; n < 5; n++)
2877 {
2878 char buf[32];
2879 sprintf(buf, "Object %d", n);
2880 if (ImGui::Selectable(buf, selected == n))
2881 selected = n;
2882 }
2883 ImGui::TreePop();
2884 }
2885
2886 // Demonstrate implementation a most-basic form of multi-selection manually
2887 // This doesn't support the Shift modifier which requires BeginMultiSelect()!
2888 if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)"))
2889 {
2890 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)");
2891 HelpMarker("Hold Ctrl and Click to select multiple items.");
2892 static bool selection[5] = { false, false, false, false, false };
2893 for (int n = 0; n < 5; n++)
2894 {
2895 char buf[32];
2896 sprintf(buf, "Object %d", n);
2897 if (ImGui::Selectable(buf, selection[n]))
2898 {
2899 if (!ImGui::GetIO().KeyCtrl) // Clear selection when Ctrl is not held
2900 memset(selection, 0, sizeof(selection));
2901 selection[n] ^= 1; // Toggle current item
2902 }
2903 }
2904 ImGui::TreePop();
2905 }
2906
2907 // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.
2908 // Shift+Click w/ Ctrl and other standard features are supported.
2909 // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.
2910 if (ImGui::TreeNode("Multi-Select"))
2911 {
2912 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select");
2913 ImGui::Text("Supported features:");
2914 ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space).");
2915 ImGui::BulletText("Ctrl modifier to preserve and toggle selection.");
2916 ImGui::BulletText("Shift modifier for range selection.");
2917 ImGui::BulletText("Ctrl+A to select all.");
2918 ImGui::BulletText("Escape to clear selection.");
2919 ImGui::BulletText("Click and drag to box-select.");
2920 ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.");
2921
2922 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2923 const int ITEMS_COUNT = 50;
2924 static ImGuiSelectionBasicStorage selection;
2925 ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2926
2927 // The BeginChild() has no purpose for selection logic, other that offering a scrolling region.
2928 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2929 {
2930 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2931 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2932 selection.ApplyRequests(ms_io);
2933
2934 for (int n = 0; n < ITEMS_COUNT; n++)
2935 {
2936 char label[64];
2937 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);
2938 bool item_is_selected = selection.Contains((ImGuiID)n);
2939 ImGui::SetNextItemSelectionUserData(n);
2940 ImGui::Selectable(label, item_is_selected);
2941 }
2942
2943 ms_io = ImGui::EndMultiSelect();
2944 selection.ApplyRequests(ms_io);
2945 }
2946 ImGui::EndChild();
2947 ImGui::TreePop();
2948 }
2949
2950 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2951 if (ImGui::TreeNode("Multi-Select (with clipper)"))
2952 {
2953 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)");
2954 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2955 static ImGuiSelectionBasicStorage selection;
2956
2957 ImGui::Text("Added features:");
2958 ImGui::BulletText("Using ImGuiListClipper.");
2959
2960 const int ITEMS_COUNT = 10000;
2961 ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2962 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2963 {
2964 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2965 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2966 selection.ApplyRequests(ms_io);
2967
2968 ImGuiListClipper clipper;
2969 clipper.Begin(ITEMS_COUNT);
2970 if (ms_io->RangeSrcItem != -1)
2971 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2972 while (clipper.Step())
2973 {
2974 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2975 {
2976 char label[64];
2977 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);
2978 bool item_is_selected = selection.Contains((ImGuiID)n);
2979 ImGui::SetNextItemSelectionUserData(n);
2980 ImGui::Selectable(label, item_is_selected);
2981 }
2982 }
2983
2984 ms_io = ImGui::EndMultiSelect();
2985 selection.ApplyRequests(ms_io);
2986 }
2987 ImGui::EndChild();
2988 ImGui::TreePop();
2989 }
2990
2991 // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API.
2992 // In order to support Deletion without any glitches you need to:
2993 // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling.
2994 // - (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.
2995 // - (3) BeginXXXX process
2996 // - (4) Focus process
2997 // - (5) EndXXXX process
2998 if (ImGui::TreeNode("Multi-Select (with deletion)"))
2999 {
3000 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)");
3001 // Storing items data separately from selection data.
3002 // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items)
3003 // Use a custom selection.Adapter: store item identifier in Selection (instead of index)
3004 static ImVector<ImGuiID> items;
3005 static ExampleSelectionWithDeletion selection;
3006 selection.UserData = (void*)&items;
3007 selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector<ImGuiID>* p_items = (ImVector<ImGuiID>*)self->UserData; return (*p_items)[idx]; }; // Index -> ID
3008
3009 ImGui::Text("Added features:");
3010 ImGui::BulletText("Dynamic list with Delete key support.");
3011 ImGui::Text("Selection size: %d/%d", selection.Size, items.Size);
3012
3013 // Initialize default list with 50 items + button to add/remove items.
3014 static ImGuiID items_next_id = 0;
3015 if (items_next_id == 0)
3016 for (ImGuiID n = 0; n < 50; n++)
3017 items.push_back(items_next_id++);
3018 if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } }
3019 ImGui::SameLine();
3020 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(); } }
3021
3022 // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
3023 const float items_height = ImGui::GetTextLineHeightWithSpacing();
3024 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
3025
3026 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3027 {
3028 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
3029 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
3030 selection.ApplyRequests(ms_io);
3031
3032 const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0);
3033 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
3034
3035 for (int n = 0; n < items.Size; n++)
3036 {
3037 const ImGuiID item_id = items[n];
3038 char label[64];
3039 sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_COUNTOF(ExampleNames)]);
3040
3041 bool item_is_selected = selection.Contains(item_id);
3042 ImGui::SetNextItemSelectionUserData(n);
3043 ImGui::Selectable(label, item_is_selected);
3044 if (item_curr_idx_to_focus == n)
3045 ImGui::SetKeyboardFocusHere(-1);
3046 }
3047
3048 // Apply multi-select requests
3049 ms_io = ImGui::EndMultiSelect();
3050 selection.ApplyRequests(ms_io);
3051 if (want_delete)
3052 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
3053 }
3054 ImGui::EndChild();
3055 ImGui::TreePop();
3056 }
3057
3058 // Implement a Dual List Box (#6648)
3059 if (ImGui::TreeNode("Multi-Select (dual list box)"))
3060 {
3061 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)");
3062 // Init default state
3063 static ExampleDualListBox dlb;
3064 if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0)
3065 for (int item_id = 0; item_id < IM_COUNTOF(ExampleNames); item_id++)
3066 dlb.Items[0].push_back((ImGuiID)item_id);
3067
3068 // Show
3069 dlb.Show();
3070
3071 ImGui::TreePop();
3072 }
3073
3074 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
3075 if (ImGui::TreeNode("Multi-Select (in a table)"))
3076 {
3077 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)");
3078 static ImGuiSelectionBasicStorage selection;
3079
3080 const int ITEMS_COUNT = 10000;
3081 ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
3082 if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter, ImVec2(0.0f, ImGui::GetFontSize() * 20)))
3083 {
3084 ImGui::TableSetupColumn("Object");
3085 ImGui::TableSetupColumn("Action");
3086 ImGui::TableSetupScrollFreeze(0, 1);
3087 ImGui::TableHeadersRow();
3088
3089 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
3090 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
3091 selection.ApplyRequests(ms_io);
3092
3093 ImGuiListClipper clipper;
3094 clipper.Begin(ITEMS_COUNT);
3095 if (ms_io->RangeSrcItem != -1)
3096 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
3097 while (clipper.Step())
3098 {
3099 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
3100 {
3101 ImGui::TableNextRow();
3102 ImGui::TableNextColumn();
3103 ImGui::PushID(n);
3104 char label[64];
3105 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);
3106 bool item_is_selected = selection.Contains((ImGuiID)n);
3107 ImGui::SetNextItemSelectionUserData(n);
3108 ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
3109 ImGui::TableNextColumn();
3110 ImGui::SmallButton("hello");
3111 ImGui::PopID();
3112 }
3113 }
3114
3115 ms_io = ImGui::EndMultiSelect();
3116 selection.ApplyRequests(ms_io);
3117 ImGui::EndTable();
3118 }
3119 ImGui::TreePop();
3120 }
3121
3122 if (ImGui::TreeNode("Multi-Select (checkboxes)"))
3123 {
3124 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)");
3125 ImGui::Text("In a list of checkboxes (not selectable):");
3126 ImGui::BulletText("Using _NoAutoSelect + _NoAutoClear flags.");
3127 ImGui::BulletText("Shift+Click to check multiple boxes.");
3128 ImGui::BulletText("Shift+Keyboard to copy current value to other boxes.");
3129
3130 // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper.
3131 static bool items[20] = {};
3132 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape;
3133 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
3134 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
3135 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.
3136
3137 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
3138 {
3139 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_COUNTOF(items));
3140 ImGuiSelectionExternalStorage storage_wrapper;
3141 storage_wrapper.UserData = (void*)items;
3142 storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; };
3143 storage_wrapper.ApplyRequests(ms_io);
3144 for (int n = 0; n < 20; n++)
3145 {
3146 char label[32];
3147 sprintf(label, "Item %d", n);
3148 ImGui::SetNextItemSelectionUserData(n);
3149 ImGui::Checkbox(label, &items[n]);
3150 }
3151 ms_io = ImGui::EndMultiSelect();
3152 storage_wrapper.ApplyRequests(ms_io);
3153 }
3154 ImGui::EndChild();
3155
3156 ImGui::TreePop();
3157 }
3158
3159 // Demonstrate individual selection scopes in same window
3160 if (ImGui::TreeNode("Multi-Select (multiple scopes)"))
3161 {
3162 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)");
3163 // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection
3164 const int SCOPES_COUNT = 3;
3165 const int ITEMS_COUNT = 8; // Per scope
3166 static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT];
3167
3168 // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.
3169 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid;
3170 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
3171 flags &= ~ImGuiMultiSelectFlags_ScopeRect;
3172 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
3173 flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
3174 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);
3175 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
3176
3177 for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++)
3178 {
3179 ImGui::PushID(selection_scope_n);
3180 ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];
3181 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT);
3182 selection->ApplyRequests(ms_io);
3183
3184 ImGui::SeparatorText("Selection scope");
3185 ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT);
3186
3187 for (int n = 0; n < ITEMS_COUNT; n++)
3188 {
3189 char label[64];
3190 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);
3191 bool item_is_selected = selection->Contains((ImGuiID)n);
3192 ImGui::SetNextItemSelectionUserData(n);
3193 ImGui::Selectable(label, item_is_selected);
3194 }
3195
3196 // Apply multi-select requests
3197 ms_io = ImGui::EndMultiSelect();
3198 selection->ApplyRequests(ms_io);
3199 ImGui::PopID();
3200 }
3201 ImGui::TreePop();
3202 }
3203
3204 // See ShowExampleAppAssetsBrowser()
3205 if (ImGui::TreeNode("Multi-Select (tiled assets browser)"))
3206 {
3207 ImGui::Checkbox("Assets Browser", &demo_data->ShowAppAssetsBrowser);
3208 ImGui::Text("(also access from 'Examples->Assets Browser' in menu)");
3209 ImGui::TreePop();
3210 }
3211
3212 // Demonstrate supporting multiple-selection in a tree.
3213 // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly!
3214 // This showcase how SetNextItemSelectionUserData() never assume indices!
3215 // - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request.
3216 // We want this interpolation to match what the user sees: in visible order, skipping closed nodes.
3217 // This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper.
3218 // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you
3219 // are more likely to build an array mapping sequential indices to visible tree nodes, since your
3220 // filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier.
3221 // - Consider this a prototype: we are working toward simplifying some of it.
3222 if (ImGui::TreeNode("Multi-Select (trees)"))
3223 {
3224 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)");
3225 HelpMarker(
3226 "This is rather advanced and experimental. If you are getting started with multi-select, "
3227 "please don't start by looking at how to use it for a tree!\n\n"
3228 "Future versions will try to simplify and formalize some of this.");
3229
3230 struct ExampleTreeFuncs
3231 {
3232 static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection)
3233 {
3234 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3235 tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Enable pressing left to jump to parent
3236 if (node->Childs.Size == 0)
3237 tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf;
3238 if (selection->Contains((ImGuiID)node->UID))
3239 tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3240
3241 // Using SetNextItemStorageID() to specify storage id, so we can easily peek into
3242 // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions.
3243 ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node);
3244 ImGui::SetNextItemStorageID((ImGuiID)node->UID);
3245 if (ImGui::TreeNodeEx(node->Name, tree_node_flags))
3246 {
3247 for (ExampleTreeNode* child : node->Childs)
3248 DrawNode(child, selection);
3249 ImGui::TreePop();
3250 }
3251 else if (ImGui::IsItemToggledOpen())
3252 {
3253 TreeCloseAndUnselectChildNodes(node, selection);
3254 }
3255 }
3256
3257 // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected.
3258 // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node
3259 // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc.
3260 static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0)
3261 {
3262 // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!)
3263 int unselected_count = selection->Contains((ImGuiID)node->UID) ? 1 : 0;
3264 if (depth == 0 || ImGui::TreeNodeGetOpen((ImGuiID)node->UID))
3265 {
3266 for (ExampleTreeNode* child : node->Childs)
3267 unselected_count += TreeCloseAndUnselectChildNodes(child, selection, depth + 1);
3268 ImGui::TreeNodeSetOpen((ImGuiID)node->UID, false);
3269 }
3270
3271 // Select root node if any of its child was selected, otherwise unselect
3272 selection->SetItemSelected((ImGuiID)node->UID, (depth == 0 && unselected_count > 0));
3273 return unselected_count;
3274 }
3275
3276 // Apply multi-selection requests
3277 static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection)
3278 {
3279 for (ImGuiSelectionRequest& req : ms_io->Requests)
3280 {
3281 if (req.Type == ImGuiSelectionRequestType_SetAll)
3282 {
3283 if (req.Selected)
3284 TreeSetAllInOpenNodes(tree, selection, req.Selected);
3285 else
3286 selection->Clear();
3287 }
3288 else if (req.Type == ImGuiSelectionRequestType_SetRange)
3289 {
3290 ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem;
3291 ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem;
3292 for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(node, last_node))
3293 selection->SetItemSelected((ImGuiID)node->UID, req.Selected);
3294 }
3295 }
3296 }
3297
3298 static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected)
3299 {
3300 if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme
3301 selection->SetItemSelected((ImGuiID)node->UID, selected);
3302 if (node->Parent == NULL || ImGui::TreeNodeGetOpen((ImGuiID)node->UID))
3303 for (ExampleTreeNode* child : node->Childs)
3304 TreeSetAllInOpenNodes(child, selection, selected);
3305 }
3306
3307 // Interpolate in *user-visible order* AND only *over opened nodes*.
3308 // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler.
3309 // Here the tricks are that:
3310 // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion.
3311 // this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)'
3312 // which would only be called when crossing from child to a parent, aka not too much.
3313 // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack,
3314 // making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location.
3315 static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node)
3316 {
3317 // Reached last node
3318 if (curr_node == last_node)
3319 return NULL;
3320
3321 // Recurse into childs. Query storage to tell if the node is open.
3322 if (curr_node->Childs.Size > 0 && ImGui::TreeNodeGetOpen((ImGuiID)curr_node->UID))
3323 return curr_node->Childs[0];
3324
3325 // Next sibling, then into our own parent
3326 while (curr_node->Parent != NULL)
3327 {
3328 if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size)
3329 return curr_node->Parent->Childs[curr_node->IndexInParent + 1];
3330 curr_node = curr_node->Parent;
3331 }
3332 return NULL;
3333 }
3334
3335 }; // ExampleTreeFuncs
3336
3337 static ImGuiSelectionBasicStorage selection;
3338 if (demo_data->DemoTree == NULL)
3339 demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once
3340 ImGui::Text("Selection size: %d", selection.Size);
3341
3342 if (ImGui::BeginChild("##Tree", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3343 {
3344 ExampleTreeNode* tree = demo_data->DemoTree;
3345 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d;
3346 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1);
3347 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);
3348 for (ExampleTreeNode* node : tree->Childs)
3349 ExampleTreeFuncs::DrawNode(node, &selection);
3350 ms_io = ImGui::EndMultiSelect();
3351 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);
3352 }
3353 ImGui::EndChild();
3354
3355 ImGui::TreePop();
3356 }
3357
3358 // Advanced demonstration of BeginMultiSelect()
3359 // - Showcase clipping.
3360 // - Showcase deletion.
3361 // - Showcase basic drag and drop.
3362 // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
3363 // - Showcase using inside a table.
3364 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
3365 if (ImGui::TreeNode("Multi-Select (advanced)"))
3366 {
3367 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)");
3368 // Options
3369 enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
3370 static bool use_clipper = true;
3371 static bool use_deletion = true;
3372 static bool use_drag_drop = true;
3373 static bool show_in_table = false;
3374 static bool show_color_button = true;
3375 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
3376 static WidgetType widget_type = WidgetType_Selectable;
3377
3378 if (ImGui::TreeNode("Options"))
3379 {
3380 if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; }
3381 ImGui::SameLine();
3382 if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; }
3383 ImGui::SameLine();
3384 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.");
3385 ImGui::Checkbox("Enable clipper", &use_clipper);
3386 ImGui::Checkbox("Enable deletion", &use_deletion);
3387 ImGui::Checkbox("Enable drag & drop", &use_drag_drop);
3388 ImGui::Checkbox("Show in a table", &show_in_table);
3389 ImGui::Checkbox("Show color button", &show_color_button);
3390 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect);
3391 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll);
3392 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect);
3393 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
3394 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
3395 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect);
3396 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectOnRightClick", &flags, ImGuiMultiSelectFlags_NoSelectOnRightClick);
3397 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
3398 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d);
3399 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll);
3400 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape);
3401 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);
3402 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
3403 flags &= ~ImGuiMultiSelectFlags_ScopeRect;
3404 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
3405 flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
3406 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnAuto", &flags, ImGuiMultiSelectFlags_SelectOnAuto))
3407 flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnAuto);
3408 ImGui::SameLine(); HelpMarker("Apply selection on mouse down when clicking on unselected item, on mouse up when clicking on selected item. (Default)");
3409 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickAlways", &flags, ImGuiMultiSelectFlags_SelectOnClickAlways))
3410 flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnClickAlways);
3411 ImGui::SameLine(); HelpMarker("Prevents Drag and Drop from being used on multi-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection. (Excel style behavior)");
3412 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease))
3413 flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnClickRelease);
3414 ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection.");
3415 ImGui::TreePop();
3416 }
3417
3418 // Initialize default list with 1000 items.
3419 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
3420 static ImVector<int> items;
3421 static int items_next_id = 0;
3422 if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } }
3423 static ExampleSelectionWithDeletion selection;
3424 static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu
3425
3426 ImGui::Text("Selection size: %d/%d", selection.Size, items.Size);
3427
3428 const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing();
3429 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
3430 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3431 {
3432 ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());
3433 if (widget_type == WidgetType_TreeNode)
3434 ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
3435
3436 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
3437 selection.ApplyRequests(ms_io);
3438
3439 const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu;
3440 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
3441 request_deletion_from_menu = false;
3442
3443 if (show_in_table)
3444 {
3445 if (widget_type == WidgetType_TreeNode)
3446 ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f));
3447 ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
3448 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f);
3449 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f);
3450 //ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
3451 }
3452
3453 ImGuiListClipper clipper;
3454 if (use_clipper)
3455 {
3456 clipper.Begin(items.Size);
3457 if (item_curr_idx_to_focus != -1)
3458 clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped.
3459 if (ms_io->RangeSrcItem != -1)
3460 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
3461 }
3462
3463 while (!use_clipper || clipper.Step())
3464 {
3465 const int item_begin = use_clipper ? clipper.DisplayStart : 0;
3466 const int item_end = use_clipper ? clipper.DisplayEnd : items.Size;
3467 for (int n = item_begin; n < item_end; n++)
3468 {
3469 if (show_in_table)
3470 ImGui::TableNextColumn();
3471
3472 const int item_id = items[n];
3473 const char* item_category = ExampleNames[item_id % IM_COUNTOF(ExampleNames)];
3474 char label[64];
3475 sprintf(label, "Object %05d: %s", item_id, item_category);
3476
3477 // IMPORTANT: for deletion refocus to work we need object ID to be stable,
3478 // aka not depend on their index in the list. Here we use our persistent item_id
3479 // instead of index to build a unique ID that will persist.
3480 // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).
3481 ImGui::PushID(item_id);
3482
3483 // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part
3484 // of the selection scope doesn't erroneously alter our selection.
3485 if (show_color_button)
3486 {
3487 ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK;
3488 ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz);
3489 ImGui::SameLine();
3490 }
3491
3492 // Submit item
3493 bool item_is_selected = selection.Contains((ImGuiID)n);
3494 bool item_is_open = false;
3495 ImGui::SetNextItemSelectionUserData(n);
3496 if (widget_type == WidgetType_Selectable)
3497 {
3498 ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None);
3499 }
3500 else if (widget_type == WidgetType_TreeNode)
3501 {
3502 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3503 if (item_is_selected)
3504 tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3505 item_is_open = ImGui::TreeNodeEx(label, tree_node_flags);
3506 }
3507
3508 // Focus (for after deletion)
3509 if (item_curr_idx_to_focus == n)
3510 ImGui::SetKeyboardFocusHere(-1);
3511
3512 // Drag and Drop
3513 if (use_drag_drop && ImGui::BeginDragDropSource())
3514 {
3515 // Create payload with full selection OR single unselected item.
3516 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
3517 if (ImGui::GetDragDropPayload() == NULL)
3518 {
3519 ImVector<int> payload_items;
3520 void* it = NULL;
3521 ImGuiID id = 0;
3522 if (!item_is_selected)
3523 payload_items.push_back(item_id);
3524 else
3525 while (selection.GetNextSelectedItem(&it, &id))
3526 payload_items.push_back((int)id);
3527 ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
3528 }
3529
3530 // Display payload content in tooltip
3531 const ImGuiPayload* payload = ImGui::GetDragDropPayload();
3532 const int* payload_items = (int*)payload->Data;
3533 const int payload_count = (int)payload->DataSize / (int)sizeof(int);
3534 if (payload_count == 1)
3535 ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_COUNTOF(ExampleNames)]);
3536 else
3537 ImGui::Text("Dragging %d objects", payload_count);
3538
3539 ImGui::EndDragDropSource();
3540 }
3541
3542 if (widget_type == WidgetType_TreeNode && item_is_open)
3543 ImGui::TreePop();
3544
3545 // Right-click: context menu
3546 if (ImGui::BeginPopupContextItem())
3547 {
3548 ImGui::BeginDisabled(!use_deletion || selection.Size == 0);
3549 sprintf(label, "Delete %d item(s)###DeleteSelected", selection.Size);
3550 if (ImGui::Selectable(label))
3551 request_deletion_from_menu = true;
3552 ImGui::EndDisabled();
3553 ImGui::Selectable("Close");
3554 ImGui::EndPopup();
3555 }
3556
3557 // Demo content within a table
3558 if (show_in_table)
3559 {
3560 ImGui::TableNextColumn();
3561 ImGui::SetNextItemWidth(-FLT_MIN);
3562 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
3563 ImGui::InputText("##NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly);
3564 ImGui::PopStyleVar();
3565 }
3566
3567 ImGui::PopID();
3568 }
3569 if (!use_clipper)
3570 break;
3571 }
3572
3573 if (show_in_table)
3574 {
3575 ImGui::EndTable();
3576 if (widget_type == WidgetType_TreeNode)
3577 ImGui::PopStyleVar();
3578 }
3579
3580 // Apply multi-select requests
3581 ms_io = ImGui::EndMultiSelect();
3582 selection.ApplyRequests(ms_io);
3583 if (want_delete)
3584 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
3585
3586 if (widget_type == WidgetType_TreeNode)
3587 ImGui::PopStyleVar();
3588 }
3589 ImGui::EndChild();
3590 ImGui::TreePop();
3591 }
3592 ImGui::TreePop();
3593 }
3594}
3595
3596//-----------------------------------------------------------------------------
3597// [SECTION] DemoWindowWidgetsTabs()
3598//-----------------------------------------------------------------------------
3599
3600static void EditTabBarFittingPolicyFlags(ImGuiTabBarFlags* p_flags)
3601{
3602 if ((*p_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
3603 *p_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
3604 if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyMixed", p_flags, ImGuiTabBarFlags_FittingPolicyMixed))
3605 *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyMixed);
3606 if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyShrink", p_flags, ImGuiTabBarFlags_FittingPolicyShrink))
3607 *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyShrink);
3608 if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", p_flags, ImGuiTabBarFlags_FittingPolicyScroll))
3609 *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
3610}
3611
3612static void DemoWindowWidgetsTabs()
3613{
3614 if (ImGui::TreeNode("Tabs"))
3615 {
3616 IMGUI_DEMO_MARKER("Widgets/Tabs");
3617 if (ImGui::TreeNode("Basic"))
3618 {
3619 IMGUI_DEMO_MARKER("Widgets/Tabs/Basic");
3620 ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
3621 if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3622 {
3623 if (ImGui::BeginTabItem("Avocado"))
3624 {
3625 ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah");
3626 ImGui::EndTabItem();
3627 }
3628 if (ImGui::BeginTabItem("Broccoli"))
3629 {
3630 ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah");
3631 ImGui::EndTabItem();
3632 }
3633 if (ImGui::BeginTabItem("Cucumber"))
3634 {
3635 ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah");
3636 ImGui::EndTabItem();
3637 }
3638 ImGui::EndTabBar();
3639 }
3640 ImGui::Separator();
3641 ImGui::TreePop();
3642 }
3643
3644 if (ImGui::TreeNode("Advanced & Close Button"))
3645 {
3646 IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button");
3647 // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).
3648 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;
3649 ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", &tab_bar_flags, ImGuiTabBarFlags_Reorderable);
3650 ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs);
3651 ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);
3652 ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);
3653 ImGui::CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline);
3654 EditTabBarFittingPolicyFlags(&tab_bar_flags);
3655
3656 // Tab Bar
3657 ImGui::AlignTextToFramePadding();
3658 ImGui::Text("Opened:");
3659 const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" };
3660 static bool opened[4] = { true, true, true, true }; // Persistent user state
3661 for (int n = 0; n < IM_COUNTOF(opened); n++)
3662 {
3663 ImGui::SameLine();
3664 ImGui::Checkbox(names[n], &opened[n]);
3665 }
3666
3667 // Passing a bool* to BeginTabItem() is similar to passing one to Begin():
3668 // the underlying bool will be set to false when the tab is closed.
3669 if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3670 {
3671 for (int n = 0; n < IM_COUNTOF(opened); n++)
3672 if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None))
3673 {
3674 ImGui::Text("This is the %s tab!", names[n]);
3675 if (n & 1)
3676 ImGui::Text("I am an odd tab.");
3677 ImGui::EndTabItem();
3678 }
3679 ImGui::EndTabBar();
3680 }
3681 ImGui::Separator();
3682 ImGui::TreePop();
3683 }
3684
3685 if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags"))
3686 {
3687 IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags");
3688 static ImVector<int> active_tabs;
3689 static int next_tab_id = 0;
3690 if (next_tab_id == 0) // Initialize with some default tabs
3691 for (int i = 0; i < 3; i++)
3692 active_tabs.push_back(next_tab_id++);
3693
3694 // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.
3695 // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...
3696 // but they tend to make more sense together)
3697 static bool show_leading_button = true;
3698 static bool show_trailing_button = true;
3699 ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button);
3700 ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button);
3701
3702 // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
3703 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink;
3704 EditTabBarFittingPolicyFlags(&tab_bar_flags);
3705
3706 if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3707 {
3708 // Demo a Leading TabItemButton(): click the "?" button to open a menu
3709 if (show_leading_button)
3710 if (ImGui::TabItemButton("?", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))
3711 ImGui::OpenPopup("MyHelpMenu");
3712 if (ImGui::BeginPopup("MyHelpMenu"))
3713 {
3714 ImGui::Selectable("Hello!");
3715 ImGui::EndPopup();
3716 }
3717
3718 // Demo Trailing Tabs: click the "+" button to add a new tab.
3719 // (In your app you may want to use a font icon instead of the "+")
3720 // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end.
3721 if (show_trailing_button)
3722 if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))
3723 active_tabs.push_back(next_tab_id++); // Add new tab
3724
3725 // Submit our regular tabs
3726 for (int n = 0; n < active_tabs.Size; )
3727 {
3728 bool open = true;
3729 char name[16];
3730 snprintf(name, IM_COUNTOF(name), "%04d", active_tabs[n]);
3731 if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None))
3732 {
3733 ImGui::Text("This is the %s tab!", name);
3734 ImGui::EndTabItem();
3735 }
3736
3737 if (!open)
3738 active_tabs.erase(active_tabs.Data + n);
3739 else
3740 n++;
3741 }
3742
3743 ImGui::EndTabBar();
3744 }
3745 ImGui::Separator();
3746 ImGui::TreePop();
3747 }
3748 ImGui::TreePop();
3749 }
3750}
3751
3752//-----------------------------------------------------------------------------
3753// [SECTION] DemoWindowWidgetsText()
3754//-----------------------------------------------------------------------------
3755
3756static void DemoWindowWidgetsText()
3757{
3758 if (ImGui::TreeNode("Text"))
3759 {
3760 IMGUI_DEMO_MARKER("Widgets/Text");
3761 if (ImGui::TreeNode("Colorful Text"))
3762 {
3763 IMGUI_DEMO_MARKER("Widgets/Text/Colored Text");
3764 // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
3765 ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Pink");
3766 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Yellow");
3767 ImGui::TextDisabled("Disabled");
3768 ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle.");
3769 ImGui::TreePop();
3770 }
3771
3772 if (ImGui::TreeNode("Font Size"))
3773 {
3774 IMGUI_DEMO_MARKER("Widgets/Text/Font Size");
3775 ImGuiStyle& style = ImGui::GetStyle();
3776 const float global_scale = style.FontScaleMain * style.FontScaleDpi;
3777 ImGui::Text("style.FontScaleMain = %0.2f", style.FontScaleMain);
3778 ImGui::Text("style.FontScaleDpi = %0.2f", style.FontScaleDpi);
3779 ImGui::Text("global_scale = ~%0.2f", global_scale); // This is not technically accurate as internal scales may apply, but conceptually let's pretend it is.
3780 ImGui::Text("FontSize = %0.2f", ImGui::GetFontSize());
3781
3782 ImGui::SeparatorText("");
3783 static float custom_size = 16.0f;
3784 ImGui::SliderFloat("custom_size", &custom_size, 10.0f, 100.0f, "%.0f");
3785 ImGui::Text("ImGui::PushFont(nullptr, custom_size);");
3786 ImGui::PushFont(NULL, custom_size);
3787 ImGui::Text("FontSize = %.2f (== %.2f * global_scale)", ImGui::GetFontSize(), custom_size);
3788 ImGui::PopFont();
3789
3790 ImGui::SeparatorText("");
3791 static float custom_scale = 1.0f;
3792 ImGui::SliderFloat("custom_scale", &custom_scale, 0.5f, 4.0f, "%.2f");
3793 ImGui::Text("ImGui::PushFont(nullptr, style.FontSizeBase * custom_scale);");
3794 ImGui::PushFont(NULL, style.FontSizeBase * custom_scale);
3795 ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), custom_scale);
3796 ImGui::PopFont();
3797
3798 ImGui::SeparatorText("");
3799 for (float scaling = 0.5f; scaling <= 4.0f; scaling += 0.5f)
3800 {
3801 ImGui::PushFont(NULL, style.FontSizeBase * scaling);
3802 ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), scaling);
3803 ImGui::PopFont();
3804 }
3805
3806 ImGui::TreePop();
3807 }
3808
3809 if (ImGui::TreeNode("Word Wrapping"))
3810 {
3811 IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping");
3812 // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
3813 ImGui::TextWrapped(
3814 "This text should automatically wrap on the edge of the window. The current implementation "
3815 "for text wrapping follows simple rules suitable for English and possibly other languages.");
3816 ImGui::Spacing();
3817
3818 static float wrap_width = 200.0f;
3819 ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f");
3820
3821 ImDrawList* draw_list = ImGui::GetWindowDrawList();
3822 for (int n = 0; n < 2; n++)
3823 {
3824 ImGui::Text("Test paragraph %d:", n);
3825 ImVec2 pos = ImGui::GetCursorScreenPos();
3826 ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);
3827 ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());
3828 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
3829 if (n == 0)
3830 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);
3831 else
3832 ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh");
3833
3834 // Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
3835 draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));
3836 draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255));
3837 ImGui::PopTextWrapPos();
3838 }
3839
3840 ImGui::TreePop();
3841 }
3842
3843 if (ImGui::TreeNode("UTF-8 Text"))
3844 {
3845 IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text");
3846 // UTF-8 test with Japanese characters
3847 // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
3848 // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
3849 // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
3850 // can save your source files as 'UTF-8 without signature').
3851 // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
3852 // CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
3853 // Don't do this in your application! Please use u8"text in any language" in your application!
3854 // Note that characters values are preserved even by InputText() if the font cannot be displayed,
3855 // so you can safely copy & paste garbled characters into another application.
3856 ImGui::TextWrapped(
3857 "CJK text will only appear if the font was loaded with the appropriate CJK character ranges. "
3858 "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. "
3859 "Read docs/FONTS.md for details.");
3860 ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
3861 ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
3862 static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
3863 //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis
3864 ImGui::InputText("UTF-8 input", buf, IM_COUNTOF(buf));
3865 ImGui::TreePop();
3866 }
3867 ImGui::TreePop();
3868 }
3869}
3870
3871//-----------------------------------------------------------------------------
3872// [SECTION] DemoWindowWidgetsTextFilter()
3873//-----------------------------------------------------------------------------
3874
3875static void DemoWindowWidgetsTextFilter()
3876{
3877 if (ImGui::TreeNode("Text Filter"))
3878 {
3879 IMGUI_DEMO_MARKER("Widgets/Text Filter");
3880 // Helper class to easy setup a text filter.
3881 // You may want to implement a more feature-full filtering scheme in your own application.
3882 HelpMarker("Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.");
3883 static ImGuiTextFilter filter;
3884 ImGui::Text("Filter usage:\n"
3885 " \"\" display all lines\n"
3886 " \"xxx\" display lines containing \"xxx\"\n"
3887 " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n"
3888 " \"-xxx\" hide lines containing \"xxx\"");
3889 filter.Draw();
3890 const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" };
3891 for (int i = 0; i < IM_COUNTOF(lines); i++)
3892 if (filter.PassFilter(lines[i]))
3893 ImGui::BulletText("%s", lines[i]);
3894 ImGui::TreePop();
3895 }
3896}
3897
3898//-----------------------------------------------------------------------------
3899// [SECTION] DemoWindowWidgetsTextInput()
3900//-----------------------------------------------------------------------------
3901
3902static void DemoWindowWidgetsTextInput()
3903{
3904 // To wire InputText() with std::string or any other custom string type,
3905 // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
3906 if (ImGui::TreeNode("Text Input"))
3907 {
3908 IMGUI_DEMO_MARKER("Widgets/Text Input");
3909 if (ImGui::TreeNode("Multi-line Text Input"))
3910 {
3911 IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input");
3912 // WE ARE USING A FIXED-SIZE BUFFER FOR SIMPLICITY HERE.
3913 // If you want to use InputText() with std::string or any custom dynamic string type:
3914 // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp
3915 // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.
3916 static char text[1024 * 16] =
3917 "/*\n"
3918 " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"
3919 " the hexadecimal encoding of one offending instruction,\n"
3920 " more formally, the invalid operand with locked CMPXCHG8B\n"
3921 " instruction bug, is a design flaw in the majority of\n"
3922 " Intel Pentium, Pentium MMX, and Pentium OverDrive\n"
3923 " processors (all in the P5 microarchitecture).\n"
3924 "*/\n\n"
3925 "label:\n"
3926 "\tlock cmpxchg8b eax\n";
3927
3928 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
3929 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)");
3930 ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
3931 ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
3932 ImGui::SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h");
3933 ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput);
3934 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.");
3935 ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);
3936 ImGui::InputTextMultiline("##source", text, IM_COUNTOF(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
3937 ImGui::TreePop();
3938 }
3939
3940 if (ImGui::TreeNode("Filtered Text Input"))
3941 {
3942 IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input");
3943 struct TextFilters
3944 {
3945 // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)
3946 static int FilterCasingSwap(ImGuiInputTextCallbackData* data)
3947 {
3948 if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase
3949 else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase
3950 return 0;
3951 }
3952
3953 // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)
3954 static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)
3955 {
3956 if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar))
3957 return 0;
3958 return 1;
3959 }
3960 };
3961
3962 static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_COUNTOF(buf1));
3963 static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CharsDecimal);
3964 static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
3965 static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_COUNTOF(buf4), ImGuiInputTextFlags_CharsUppercase);
3966 static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_COUNTOF(buf5), ImGuiInputTextFlags_CharsNoBlank);
3967 static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_COUNTOF(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.
3968 static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_COUNTOF(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.
3969 ImGui::TreePop();
3970 }
3971
3972 if (ImGui::TreeNode("Password Input"))
3973 {
3974 IMGUI_DEMO_MARKER("Widgets/Text Input/Password input");
3975 static char password[64] = "password123";
3976 ImGui::InputText("password", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password);
3977 ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
3978 ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password);
3979 ImGui::InputText("password (clear)", password, IM_COUNTOF(password));
3980 ImGui::TreePop();
3981 }
3982
3983 if (ImGui::TreeNode("Completion, History, Edit Callbacks"))
3984 {
3985 IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks");
3986 struct Funcs
3987 {
3988 static int MyCallback(ImGuiInputTextCallbackData* data)
3989 {
3990 if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)
3991 {
3992 data->InsertChars(data->CursorPos, "..");
3993 }
3994 else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)
3995 {
3996 if (data->EventKey == ImGuiKey_UpArrow)
3997 {
3998 data->DeleteChars(0, data->BufTextLen);
3999 data->InsertChars(0, "Pressed Up!");
4000 data->SelectAll();
4001 }
4002 else if (data->EventKey == ImGuiKey_DownArrow)
4003 {
4004 data->DeleteChars(0, data->BufTextLen);
4005 data->InsertChars(0, "Pressed Down!");
4006 data->SelectAll();
4007 }
4008 }
4009 else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)
4010 {
4011 // Toggle casing of first character
4012 char c = data->Buf[0];
4013 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32;
4014 data->BufDirty = true;
4015
4016 // Increment a counter
4017 int* p_int = (int*)data->UserData;
4018 *p_int = *p_int + 1;
4019 }
4020 return 0;
4021 }
4022 };
4023 static char buf1[64];
4024 ImGui::InputText("Completion", buf1, IM_COUNTOF(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);
4025 ImGui::SameLine(); HelpMarker(
4026 "Here we append \"..\" each time Tab is pressed. "
4027 "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
4028
4029 static char buf2[64];
4030 ImGui::InputText("History", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);
4031 ImGui::SameLine(); HelpMarker(
4032 "Here we replace and select text each time Up/Down are pressed. "
4033 "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
4034
4035 static char buf3[64];
4036 static int edit_count = 0;
4037 ImGui::InputText("Edit", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);
4038 ImGui::SameLine(); HelpMarker(
4039 "Here we toggle the casing of the first character on every edit + count edits.");
4040 ImGui::SameLine(); ImGui::Text("(%d)", edit_count);
4041
4042 ImGui::TreePop();
4043 }
4044
4045 if (ImGui::TreeNode("Resize Callback"))
4046 {
4047 IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback");
4048 // To wire InputText() with std::string or any other custom string type,
4049 // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
4050 // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
4051 HelpMarker(
4052 "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n"
4053 "See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
4054 struct Funcs
4055 {
4056 static int MyResizeCallback(ImGuiInputTextCallbackData* data)
4057 {
4058 if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
4059 {
4060 ImVector<char>* my_str = (ImVector<char>*)data->UserData;
4061 IM_ASSERT(my_str->begin() == data->Buf);
4062 my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
4063 data->Buf = my_str->begin();
4064 }
4065 return 0;
4066 }
4067
4068 // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
4069 // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
4070 static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
4071 {
4072 IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
4073 return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str);
4074 }
4075 };
4076
4077 // For this demo we are using ImVector as a string container.
4078 // Note that because we need to store a terminating zero character, our size/capacity are 1 more
4079 // than usually reported by a typical string class.
4080 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
4081 ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
4082
4083 static ImVector<char> my_str;
4084 if (my_str.empty())
4085 my_str.push_back(0);
4086 Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
4087 ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
4088 ImGui::TreePop();
4089 }
4090
4091 if (ImGui::TreeNode("Eliding, Alignment"))
4092 {
4093 IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment");
4094 static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp";
4095 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft;
4096 ImGui::CheckboxFlags("ImGuiInputTextFlags_ElideLeft", &flags, ImGuiInputTextFlags_ElideLeft);
4097 ImGui::InputText("Path", buf1, IM_COUNTOF(buf1), flags);
4098 ImGui::TreePop();
4099 }
4100
4101 if (ImGui::TreeNode("Miscellaneous"))
4102 {
4103 IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous");
4104 static char buf1[16];
4105 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll;
4106 ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll);
4107 ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
4108 ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo);
4109 ImGui::InputText("Hello", buf1, IM_COUNTOF(buf1), flags);
4110 ImGui::TreePop();
4111 }
4112
4113 ImGui::TreePop();
4114 }
4115
4116}
4117
4118//-----------------------------------------------------------------------------
4119// [SECTION] DemoWindowWidgetsTooltips()
4120//-----------------------------------------------------------------------------
4121
4122static void DemoWindowWidgetsTooltips()
4123{
4124 if (ImGui::TreeNode("Tooltips"))
4125 {
4126 IMGUI_DEMO_MARKER("Widgets/Tooltips");
4127 // Tooltips are windows following the mouse. They do not take focus away.
4128 ImGui::SeparatorText("General");
4129
4130 // Typical use cases:
4131 // - Short-form (text only): SetItemTooltip("Hello");
4132 // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); }
4133
4134 // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); }
4135 // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); }
4136
4137 HelpMarker(
4138 "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n"
4139 "We provide a helper SetItemTooltip() function to perform the two with standards flags.");
4140
4141 ImVec2 sz = ImVec2(-FLT_MIN, 0.0f);
4142
4143 ImGui::Button("Basic", sz);
4144 ImGui::SetItemTooltip("I am a tooltip");
4145
4146 ImGui::Button("Fancy", sz);
4147 if (ImGui::BeginItemTooltip())
4148 {
4149 ImGui::Text("I am a fancy tooltip");
4150 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
4151 ImGui::PlotLines("Curve", arr, IM_COUNTOF(arr));
4152 ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime()));
4153 ImGui::EndTooltip();
4154 }
4155
4156 ImGui::SeparatorText("Always On");
4157
4158 // Showcase NOT relying on a IsItemHovered() to emit a tooltip.
4159 // Here the tooltip is always emitted when 'always_on == true'.
4160 static int always_on = 0;
4161 ImGui::RadioButton("Off", &always_on, 0);
4162 ImGui::SameLine();
4163 ImGui::RadioButton("Always On (Simple)", &always_on, 1);
4164 ImGui::SameLine();
4165 ImGui::RadioButton("Always On (Advanced)", &always_on, 2);
4166 if (always_on == 1)
4167 ImGui::SetTooltip("I am following you around.");
4168 else if (always_on == 2 && ImGui::BeginTooltip())
4169 {
4170 ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f));
4171 ImGui::EndTooltip();
4172 }
4173
4174 ImGui::SeparatorText("Custom");
4175
4176 HelpMarker(
4177 "Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize "
4178 "tooltip activation details across your application. You may however decide to use custom "
4179 "flags for a specific tooltip instance.");
4180
4181 // The following examples are passed for documentation purpose but may not be useful to most users.
4182 // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from
4183 // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.
4184 // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.
4185 ImGui::Button("Manual", sz);
4186 if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
4187 ImGui::SetTooltip("I am a manually emitted tooltip.");
4188
4189 ImGui::Button("DelayNone", sz);
4190 if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone))
4191 ImGui::SetTooltip("I am a tooltip with no delay.");
4192
4193 ImGui::Button("DelayShort", sz);
4194 if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))
4195 ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort);
4196
4197 ImGui::Button("DelayLong", sz);
4198 if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay))
4199 ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal);
4200
4201 ImGui::Button("Stationary", sz);
4202 if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
4203 ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating.");
4204
4205 // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',
4206 // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.
4207 ImGui::BeginDisabled();
4208 ImGui::Button("Disabled item", sz);
4209 if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
4210 ImGui::SetTooltip("I am a tooltip for a disabled item.");
4211 ImGui::EndDisabled();
4212
4213 ImGui::TreePop();
4214 }
4215}
4216
4217//-----------------------------------------------------------------------------
4218// [SECTION] DemoWindowWidgetsTreeNodes()
4219//-----------------------------------------------------------------------------
4220
4221static void DemoWindowWidgetsTreeNodes()
4222{
4223 if (ImGui::TreeNode("Tree Nodes"))
4224 {
4225 IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
4226 // See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
4227 if (ImGui::TreeNode("Basic Trees"))
4228 {
4229 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic Trees");
4230 for (int i = 0; i < 5; i++)
4231 {
4232 // Use SetNextItemOpen() so set the default state of a node to be open. We could
4233 // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
4234 if (i == 0)
4235 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
4236
4237 // Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict.
4238 // An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)',
4239 // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine.
4240 ImGui::PushID(i);
4241 if (ImGui::TreeNode("", "Child %d", i))
4242 {
4243 ImGui::Text("blah blah");
4244 ImGui::SameLine();
4245 if (ImGui::SmallButton("button")) {}
4246 ImGui::TreePop();
4247 }
4248 ImGui::PopID();
4249 }
4250 ImGui::TreePop();
4251 }
4252
4253 if (ImGui::TreeNode("Hierarchy Lines"))
4254 {
4255 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy Lines");
4256 static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;
4257 HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
4258 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
4259 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
4260 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
4261
4262 if (ImGui::TreeNodeEx("Parent", base_flags))
4263 {
4264 if (ImGui::TreeNodeEx("Child 1", base_flags))
4265 {
4266 ImGui::Button("Button for Child 1");
4267 ImGui::TreePop();
4268 }
4269 if (ImGui::TreeNodeEx("Child 2", base_flags))
4270 {
4271 ImGui::Button("Button for Child 2");
4272 ImGui::TreePop();
4273 }
4274 ImGui::Text("Remaining contents");
4275 ImGui::Text("Remaining contents");
4276 ImGui::TreePop();
4277 }
4278
4279 ImGui::TreePop();
4280 }
4281
4282 if (ImGui::TreeNode("Clipping Large Trees"))
4283 {
4284 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Clipping Large Trees");
4285 ImGui::TextWrapped(
4286 "- Using ImGuiListClipper with trees is a less easy than on arrays or grids.\n"
4287 "- Refer to 'Demo->Examples->Property Editor' for an example of how to do that.\n"
4288 "- Discuss in #3823");
4289 ImGui::TreePop();
4290 }
4291
4292 if (ImGui::TreeNode("Selectable Nodes"))
4293 {
4294 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Selectable Nodes");
4295 HelpMarker(
4296 "Manually implemented selectable nodes.\n"
4297 "Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.\n\n"
4298 "You may also use the multi-select API (see 'Demo->Widgets->Selection State & Multi-Select') for more advanced multi-selection features.");
4299
4300 // Hold in 'selection_mask' a simple representation of what may be user-side selection state.
4301 // - You may retain selection state inside or outside your objects in whatever format you see fit.
4302 // You may use ImGuiSelectionBasicStorage which is conceptually close to a set<> of identifiers.
4303 // - We record which node was clicked and then apply selection at the end of the loop.
4304 // - This is a manual and simplified reimplementation of multi-selection, which the full
4305 // BeginMultiSelect() API implements better, but which is not trivial to wire for trees.
4306 static int selection_mask = 0x00;
4307 int node_clicked_idx = -1;
4308 for (int node_n = 0; node_n < 6; node_n++)
4309 {
4310 // Disable the default "open on single-click behavior" + set Selected flag according to our selection.
4311 // To alter selection we use if 'IsItemClicked() && !IsItemToggledOpen()', so clicking on an arrow doesn't alter selection.
4312 // In a BeginMultiSelect()/EndMultiSelect() we could use IsItemToggledSelection() but here we reimplement and use our own logic.
4313 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
4314 if (selection_mask & (1 << node_n))
4315 flags |= ImGuiTreeNodeFlags_Selected;
4316
4317 bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, flags, "Selectable Node %d", node_n);
4318 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4319 node_clicked_idx = node_n;
4320 if (is_open)
4321 {
4322 ImGui::BulletText("<Node contents here>");
4323 ImGui::TreePop();
4324 }
4325 }
4326 if (node_clicked_idx != -1)
4327 {
4328 // Update selection state (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
4329 if (ImGui::GetIO().KeyCtrl)
4330 selection_mask ^= (1 << node_clicked_idx); // Ctrl+Click to toggle
4331 else //if (!(selection_mask & (1 << node_clicked_idx))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
4332 selection_mask = (1 << node_clicked_idx); // Click to single-select
4333 }
4334 ImGui::TreePop();
4335 }
4336
4337 if (ImGui::TreeNode("Advanced"))
4338 {
4339 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced");
4340 static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
4341 static bool align_label_with_current_x_position = false;
4342 static bool use_drag_and_drop = false;
4343 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
4344 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);
4345 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.");
4346 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth);
4347 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &base_flags, ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::SameLine(); HelpMarker("Reduce hit area to the text label and a bit of margin.");
4348 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only.");
4349 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap);
4350 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)");
4351 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_FramePadding", &base_flags, ImGuiTreeNodeFlags_FramePadding);
4352 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent);
4353
4354 HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
4355 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
4356 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
4357 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
4358
4359 ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
4360 ImGui::Checkbox("Make Tree Nodes as drag & drop sources", &use_drag_and_drop);
4361 if (align_label_with_current_x_position)
4362 ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
4363
4364 for (int node_n = 0; node_n < 6; node_n++)
4365 {
4366 ImGuiTreeNodeFlags node_flags = base_flags;
4367 if (node_n < 3)
4368 {
4369 // Items 0..2 are Tree Node
4370 bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Node %d", node_n);
4371 if (use_drag_and_drop && ImGui::BeginDragDropSource())
4372 {
4373 ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0);
4374 ImGui::Text("This is a drag and drop source");
4375 ImGui::EndDragDropSource();
4376 }
4377 if (node_n == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
4378 {
4379 // Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
4380 ImGui::SameLine();
4381 if (ImGui::SmallButton("button")) {}
4382 }
4383 if (is_open)
4384 {
4385 ImGui::BulletText("Blah blah\nBlah Blah");
4386 ImGui::SameLine();
4387 ImGui::SmallButton("Button");
4388 ImGui::TreePop();
4389 }
4390 }
4391 else
4392 {
4393 // Items 3..5 are Tree Leaves
4394 // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
4395 // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
4396 node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
4397 ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Leaf %d", node_n);
4398 if (use_drag_and_drop && ImGui::BeginDragDropSource())
4399 {
4400 ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0);
4401 ImGui::Text("This is a drag and drop source");
4402 ImGui::EndDragDropSource();
4403 }
4404 }
4405 }
4406 if (align_label_with_current_x_position)
4407 ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
4408 ImGui::TreePop();
4409 }
4410 ImGui::TreePop();
4411 }
4412}
4413
4414//-----------------------------------------------------------------------------
4415// [SECTION] DemoWindowWidgetsVerticalSliders()
4416//-----------------------------------------------------------------------------
4417
4418static void DemoWindowWidgetsVerticalSliders()
4419{
4420 if (ImGui::TreeNode("Vertical Sliders"))
4421 {
4422 IMGUI_DEMO_MARKER("Widgets/Vertical Sliders");
4423 const float spacing = 4;
4424 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
4425
4426 static int int_value = 0;
4427 ImGui::VSliderInt("##int", ImVec2(18, 160), &int_value, 0, 5);
4428 ImGui::SameLine();
4429
4430 static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };
4431 ImGui::PushID("set1");
4432 for (int i = 0; i < 7; i++)
4433 {
4434 if (i > 0) ImGui::SameLine();
4435 ImGui::PushID(i);
4436 ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f));
4437 ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f));
4438 ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f));
4439 ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f));
4440 ImGui::VSliderFloat("##v", ImVec2(18, 160), &values[i], 0.0f, 1.0f, "");
4441 if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4442 ImGui::SetTooltip("%.3f", values[i]);
4443 ImGui::PopStyleColor(4);
4444 ImGui::PopID();
4445 }
4446 ImGui::PopID();
4447
4448 ImGui::SameLine();
4449 ImGui::PushID("set2");
4450 static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f };
4451 const int rows = 3;
4452 const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));
4453 for (int nx = 0; nx < 4; nx++)
4454 {
4455 if (nx > 0) ImGui::SameLine();
4456 ImGui::BeginGroup();
4457 for (int ny = 0; ny < rows; ny++)
4458 {
4459 ImGui::PushID(nx * rows + ny);
4460 ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, "");
4461 if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4462 ImGui::SetTooltip("%.3f", values2[nx]);
4463 ImGui::PopID();
4464 }
4465 ImGui::EndGroup();
4466 }
4467 ImGui::PopID();
4468
4469 ImGui::SameLine();
4470 ImGui::PushID("set3");
4471 for (int i = 0; i < 4; i++)
4472 {
4473 if (i > 0) ImGui::SameLine();
4474 ImGui::PushID(i);
4475 ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40);
4476 ImGui::VSliderFloat("##v", ImVec2(40, 160), &values[i], 0.0f, 1.0f, "%.2f\nsec");
4477 ImGui::PopStyleVar();
4478 ImGui::PopID();
4479 }
4480 ImGui::PopID();
4481 ImGui::PopStyleVar();
4482 ImGui::TreePop();
4483 }
4484}
4485
4486//-----------------------------------------------------------------------------
4487// [SECTION] DemoWindowWidgets()
4488//-----------------------------------------------------------------------------
4489
4490static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)
4491{
4492 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
4493 if (!ImGui::CollapsingHeader("Widgets"))
4494 return;
4495 // IMGUI_DEMO_MARKER("Widgets");
4496
4497 const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom
4498 if (disable_all)
4499 ImGui::BeginDisabled();
4500
4501 DemoWindowWidgetsBasic();
4502 DemoWindowWidgetsBullets();
4503 DemoWindowWidgetsCollapsingHeaders();
4504 DemoWindowWidgetsComboBoxes();
4505 DemoWindowWidgetsColorAndPickers();
4506 DemoWindowWidgetsDataTypes();
4507
4508 if (disable_all)
4509 ImGui::EndDisabled();
4510 DemoWindowWidgetsDisableBlocks(demo_data);
4511 if (disable_all)
4512 ImGui::BeginDisabled();
4513
4514 DemoWindowWidgetsDragAndDrop();
4515 DemoWindowWidgetsDragsAndSliders();
4516 DemoWindowWidgetsFonts();
4517 DemoWindowWidgetsImages();
4518 DemoWindowWidgetsListBoxes();
4519 DemoWindowWidgetsMultiComponents();
4520 DemoWindowWidgetsPlotting();
4521 DemoWindowWidgetsProgressBars();
4522 DemoWindowWidgetsQueryingStatuses();
4523 DemoWindowWidgetsSelectables();
4524 DemoWindowWidgetsSelectionAndMultiSelect(demo_data);
4525 DemoWindowWidgetsTabs();
4526 DemoWindowWidgetsText();
4527 DemoWindowWidgetsTextFilter();
4528 DemoWindowWidgetsTextInput();
4529 DemoWindowWidgetsTooltips();
4530 DemoWindowWidgetsTreeNodes();
4531 DemoWindowWidgetsVerticalSliders();
4532
4533 if (disable_all)
4534 ImGui::EndDisabled();
4535}
4536
4537//-----------------------------------------------------------------------------
4538// [SECTION] DemoWindowLayout()
4539//-----------------------------------------------------------------------------
4540
4541static void DemoWindowLayout()
4542{
4543 if (!ImGui::CollapsingHeader("Layout & Scrolling"))
4544 return;
4545
4546 if (ImGui::TreeNode("Child windows"))
4547 {
4548 IMGUI_DEMO_MARKER("Layout/Child windows");
4549 ImGui::SeparatorText("Child windows");
4550
4551 HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.");
4552 static bool disable_mouse_wheel = false;
4553 static bool disable_menu = false;
4554 ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel);
4555 ImGui::Checkbox("Disable Menu", &disable_menu);
4556
4557 // Child 1: no border, enable horizontal scrollbar
4558 {
4559 ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
4560 if (disable_mouse_wheel)
4561 window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4562 ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags);
4563 for (int i = 0; i < 100; i++)
4564 ImGui::Text("%04d: scrollable region", i);
4565 ImGui::EndChild();
4566 }
4567
4568 ImGui::SameLine();
4569
4570 // Child 2: rounded border
4571 {
4572 ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
4573 if (disable_mouse_wheel)
4574 window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4575 if (!disable_menu)
4576 window_flags |= ImGuiWindowFlags_MenuBar;
4577 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
4578 ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Borders, window_flags);
4579 if (!disable_menu && ImGui::BeginMenuBar())
4580 {
4581 if (ImGui::BeginMenu("Menu"))
4582 {
4583 ShowExampleMenuFile();
4584 ImGui::EndMenu();
4585 }
4586 ImGui::EndMenuBar();
4587 }
4588 if (ImGui::BeginTable("split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))
4589 {
4590 for (int i = 0; i < 100; i++)
4591 {
4592 char buf[32];
4593 sprintf(buf, "%03d", i);
4594 ImGui::TableNextColumn();
4595 ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
4596 }
4597 ImGui::EndTable();
4598 }
4599 ImGui::EndChild();
4600 ImGui::PopStyleVar();
4601 }
4602
4603 // Child 3: manual-resize
4604 ImGui::SeparatorText("Manual-resize");
4605 {
4606 HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
4607 //if (ImGui::Button("Set Height to 200"))
4608 // ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
4609
4610 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
4611 if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
4612 for (int n = 0; n < 10; n++)
4613 ImGui::Text("Line %04d", n);
4614 ImGui::PopStyleColor();
4615 ImGui::EndChild();
4616 }
4617
4618 // Child 4: auto-resizing height with a limit
4619 ImGui::SeparatorText("Auto-resize with constraints");
4620 {
4621 static int draw_lines = 3;
4622 static int max_height_in_lines = 10;
4623 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4624 ImGui::DragInt("Lines Count", &draw_lines, 0.2f);
4625 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4626 ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f);
4627
4628 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));
4629 if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY))
4630 for (int n = 0; n < draw_lines; n++)
4631 ImGui::Text("Line %04d", n);
4632 ImGui::EndChild();
4633 }
4634
4635 ImGui::SeparatorText("Misc/Advanced");
4636
4637 // Demonstrate a few extra things
4638 // - Changing ImGuiCol_ChildBg (which is transparent black in default styles)
4639 // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
4640 // You can also call SetNextWindowPos() to position the child window. The parent window will effectively
4641 // layout from this position.
4642 // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
4643 // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.
4644 {
4645 static int offset_x = 0;
4646 static bool override_bg_color = true;
4647 static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;
4648 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4649 ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000);
4650 ImGui::Checkbox("Override ChildBg color", &override_bg_color);
4651 ImGui::CheckboxFlags("ImGuiChildFlags_Borders", &child_flags, ImGuiChildFlags_Borders);
4652 ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding);
4653 ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX);
4654 ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY);
4655 ImGui::CheckboxFlags("ImGuiChildFlags_FrameStyle", &child_flags, ImGuiChildFlags_FrameStyle);
4656 ImGui::SameLine(); HelpMarker("Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.");
4657 if (child_flags & ImGuiChildFlags_FrameStyle)
4658 override_bg_color = false;
4659
4660 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
4661 if (override_bg_color)
4662 ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
4663 ImGui::BeginChild("Red", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None);
4664 if (override_bg_color)
4665 ImGui::PopStyleColor();
4666
4667 for (int n = 0; n < 50; n++)
4668 ImGui::Text("Some test %d", n);
4669 ImGui::EndChild();
4670 bool child_is_hovered = ImGui::IsItemHovered();
4671 ImVec2 child_rect_min = ImGui::GetItemRectMin();
4672 ImVec2 child_rect_max = ImGui::GetItemRectMax();
4673 ImGui::Text("Hovered: %d", child_is_hovered);
4674 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);
4675 }
4676
4677 ImGui::TreePop();
4678 }
4679
4680 if (ImGui::TreeNode("Widgets Width"))
4681 {
4682 IMGUI_DEMO_MARKER("Layout/Widgets Width");
4683 static float f = 0.0f;
4684 static bool show_indented_items = true;
4685 ImGui::Checkbox("Show indented items", &show_indented_items);
4686
4687 // Use SetNextItemWidth() to set the width of a single upcoming item.
4688 // Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
4689 // In real code use you'll probably want to choose width values that are proportional to your font size
4690 // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
4691
4692 ImGui::Text("SetNextItemWidth/PushItemWidth(100)");
4693 ImGui::SameLine(); HelpMarker("Fixed width.");
4694 ImGui::PushItemWidth(100);
4695 ImGui::DragFloat("float##1b", &f);
4696 if (show_indented_items)
4697 {
4698 ImGui::Indent();
4699 ImGui::DragFloat("float (indented)##1b", &f);
4700 ImGui::Unindent();
4701 }
4702 ImGui::PopItemWidth();
4703
4704 ImGui::Text("SetNextItemWidth/PushItemWidth(-100)");
4705 ImGui::SameLine(); HelpMarker("Align to right edge minus 100");
4706 ImGui::PushItemWidth(-100);
4707 ImGui::DragFloat("float##2a", &f);
4708 if (show_indented_items)
4709 {
4710 ImGui::Indent();
4711 ImGui::DragFloat("float (indented)##2b", &f);
4712 ImGui::Unindent();
4713 }
4714 ImGui::PopItemWidth();
4715
4716 ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)");
4717 ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)");
4718 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f);
4719 ImGui::DragFloat("float##3a", &f);
4720 if (show_indented_items)
4721 {
4722 ImGui::Indent();
4723 ImGui::DragFloat("float (indented)##3b", &f);
4724 ImGui::Unindent();
4725 }
4726 ImGui::PopItemWidth();
4727
4728 ImGui::Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)");
4729 ImGui::SameLine(); HelpMarker("Align to right edge minus half");
4730 ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
4731 ImGui::DragFloat("float##4a", &f);
4732 if (show_indented_items)
4733 {
4734 ImGui::Indent();
4735 ImGui::DragFloat("float (indented)##4b", &f);
4736 ImGui::Unindent();
4737 }
4738 ImGui::PopItemWidth();
4739
4740 ImGui::Text("SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))");
4741 ImGui::PushItemWidth(-IM_MIN(ImGui::GetFontSize() * 12, ImGui::GetContentRegionAvail().x * 0.40f));
4742 ImGui::DragFloat("float##5a", &f);
4743 if (show_indented_items)
4744 {
4745 ImGui::Indent();
4746 ImGui::DragFloat("float (indented)##5b", &f);
4747 ImGui::Unindent();
4748 }
4749 ImGui::PopItemWidth();
4750
4751 // Demonstrate using PushItemWidth to surround three items.
4752 // Calling SetNextItemWidth() before each of them would have the same effect.
4753 ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)");
4754 ImGui::SameLine(); HelpMarker("Align to right edge");
4755 ImGui::PushItemWidth(-FLT_MIN);
4756 ImGui::DragFloat("##float6a", &f);
4757 if (show_indented_items)
4758 {
4759 ImGui::Indent();
4760 ImGui::DragFloat("float (indented)##6b", &f);
4761 ImGui::Unindent();
4762 }
4763 ImGui::PopItemWidth();
4764
4765 ImGui::TreePop();
4766 }
4767
4768 if (ImGui::TreeNode("Basic Horizontal Layout"))
4769 {
4770 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout");
4771 ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)");
4772
4773 // Text
4774 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine");
4775 ImGui::Text("Two items: Hello"); ImGui::SameLine();
4776 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
4777
4778 // Adjust spacing
4779 ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20);
4780 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
4781
4782 // Button
4783 ImGui::AlignTextToFramePadding();
4784 ImGui::Text("Normal buttons"); ImGui::SameLine();
4785 ImGui::Button("Banana"); ImGui::SameLine();
4786 ImGui::Button("Apple"); ImGui::SameLine();
4787 ImGui::Button("Corniflower");
4788
4789 // Button
4790 ImGui::Text("Small buttons"); ImGui::SameLine();
4791 ImGui::SmallButton("Like this one"); ImGui::SameLine();
4792 ImGui::Text("can fit within a text block.");
4793
4794 // Aligned to arbitrary position. Easy/cheap column.
4795 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)");
4796 ImGui::Text("Aligned");
4797 ImGui::SameLine(150); ImGui::Text("x=150");
4798 ImGui::SameLine(300); ImGui::Text("x=300");
4799 ImGui::Text("Aligned");
4800 ImGui::SameLine(150); ImGui::SmallButton("x=150");
4801 ImGui::SameLine(300); ImGui::SmallButton("x=300");
4802
4803 // Checkbox
4804 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)");
4805 static bool c1 = false, c2 = false, c3 = false, c4 = false;
4806 ImGui::Checkbox("My", &c1); ImGui::SameLine();
4807 ImGui::Checkbox("Tailor", &c2); ImGui::SameLine();
4808 ImGui::Checkbox("Is", &c3); ImGui::SameLine();
4809 ImGui::Checkbox("Rich", &c4);
4810
4811 // Various
4812 static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;
4813 ImGui::PushItemWidth(ImGui::CalcTextSize("AAAAAAA").x);
4814 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" };
4815 static int item = -1;
4816 ImGui::Combo("Combo", &item, items, IM_COUNTOF(items)); ImGui::SameLine();
4817 ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine();
4818 ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine();
4819 ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f);
4820
4821 ImGui::Text("Lists:");
4822 static int selection[4] = { 0, 1, 2, 3 };
4823 for (int i = 0; i < 4; i++)
4824 {
4825 if (i > 0) ImGui::SameLine();
4826 ImGui::PushID(i);
4827 ImGui::ListBox("", &selection[i], items, IM_COUNTOF(items));
4828 ImGui::PopID();
4829 //ImGui::SetItemTooltip("ListBox %d hovered", i);
4830 }
4831 ImGui::PopItemWidth();
4832
4833 // Dummy
4834 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy");
4835 ImVec2 button_sz(40, 40);
4836 ImGui::Button("A", button_sz); ImGui::SameLine();
4837 ImGui::Dummy(button_sz); ImGui::SameLine();
4838 ImGui::Button("B", button_sz);
4839
4840 // Manually wrapping
4841 // (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
4842 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping");
4843 ImGui::Text("Manual wrapping:");
4844 ImGuiStyle& style = ImGui::GetStyle();
4845 int buttons_count = 20;
4846 float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
4847 for (int n = 0; n < buttons_count; n++)
4848 {
4849 ImGui::PushID(n);
4850 ImGui::Button("Box", button_sz);
4851 float last_button_x2 = ImGui::GetItemRectMax().x;
4852 float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line
4853 if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)
4854 ImGui::SameLine();
4855 ImGui::PopID();
4856 }
4857
4858 ImGui::TreePop();
4859 }
4860
4861 if (ImGui::TreeNode("Groups"))
4862 {
4863 IMGUI_DEMO_MARKER("Layout/Groups");
4864 HelpMarker(
4865 "BeginGroup() basically locks the horizontal position for new line. "
4866 "EndGroup() bundles the whole group so that you can use \"item\" functions such as "
4867 "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
4868 ImGui::BeginGroup();
4869 {
4870 ImGui::BeginGroup();
4871 ImGui::Button("AAA");
4872 ImGui::SameLine();
4873 ImGui::Button("BBB");
4874 ImGui::SameLine();
4875 ImGui::BeginGroup();
4876 ImGui::Button("CCC");
4877 ImGui::Button("DDD");
4878 ImGui::EndGroup();
4879 ImGui::SameLine();
4880 ImGui::Button("EEE");
4881 ImGui::EndGroup();
4882 ImGui::SetItemTooltip("First group hovered");
4883 }
4884 // Capture the group size and create widgets using the same size
4885 ImVec2 size = ImGui::GetItemRectSize();
4886 const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };
4887 ImGui::PlotHistogram("##values", values, IM_COUNTOF(values), 0, NULL, 0.0f, 1.0f, size);
4888
4889 ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4890 ImGui::SameLine();
4891 ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4892 ImGui::EndGroup();
4893 ImGui::SameLine();
4894
4895 ImGui::Button("LEVERAGE\nBUZZWORD", size);
4896 ImGui::SameLine();
4897
4898 if (ImGui::BeginListBox("List", size))
4899 {
4900 ImGui::Selectable("Selected", true);
4901 ImGui::Selectable("Not Selected", false);
4902 ImGui::EndListBox();
4903 }
4904
4905 ImGui::TreePop();
4906 }
4907
4908 if (ImGui::TreeNode("Text Baseline Alignment"))
4909 {
4910 IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment");
4911 {
4912 ImGui::BulletText("Text baseline:");
4913 ImGui::SameLine(); HelpMarker(
4914 "This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. "
4915 "Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets.");
4916 ImGui::Indent();
4917
4918 ImGui::Text("KO Blahblah"); ImGui::SameLine();
4919 ImGui::Button("Some framed item"); ImGui::SameLine();
4920 HelpMarker("Baseline of button will look misaligned with text..");
4921
4922 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4923 // (because we don't know what's coming after the Text() statement, we need to move the text baseline
4924 // down by FramePadding.y ahead of time)
4925 ImGui::AlignTextToFramePadding();
4926 ImGui::Text("OK Blahblah"); ImGui::SameLine();
4927 ImGui::Button("Some framed item##2"); ImGui::SameLine();
4928 HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y");
4929
4930 // SmallButton() uses the same vertical padding as Text
4931 ImGui::Button("TEST##1"); ImGui::SameLine();
4932 ImGui::Text("TEST"); ImGui::SameLine();
4933 ImGui::SmallButton("TEST##2");
4934
4935 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4936 ImGui::AlignTextToFramePadding();
4937 ImGui::Text("Text aligned to framed item"); ImGui::SameLine();
4938 ImGui::Button("Item##1"); ImGui::SameLine();
4939 ImGui::Text("Item"); ImGui::SameLine();
4940 ImGui::SmallButton("Item##2"); ImGui::SameLine();
4941 ImGui::Button("Item##3");
4942
4943 ImGui::Unindent();
4944 }
4945
4946 ImGui::Spacing();
4947
4948 {
4949 ImGui::BulletText("Multi-line text:");
4950 ImGui::Indent();
4951 ImGui::Text("One\nTwo\nThree"); ImGui::SameLine();
4952 ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4953 ImGui::Text("Banana");
4954
4955 ImGui::Text("Banana"); ImGui::SameLine();
4956 ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4957 ImGui::Text("One\nTwo\nThree");
4958
4959 ImGui::Button("HOP##1"); ImGui::SameLine();
4960 ImGui::Text("Banana"); ImGui::SameLine();
4961 ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4962 ImGui::Text("Banana");
4963
4964 ImGui::Button("HOP##2"); ImGui::SameLine();
4965 ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4966 ImGui::Text("Banana");
4967 ImGui::Unindent();
4968 }
4969
4970 ImGui::Spacing();
4971
4972 {
4973 ImGui::BulletText("Misc items:");
4974 ImGui::Indent();
4975
4976 // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
4977 ImGui::Button("80x80", ImVec2(80, 80));
4978 ImGui::SameLine();
4979 ImGui::Button("50x50", ImVec2(50, 50));
4980 ImGui::SameLine();
4981 ImGui::Button("Button()");
4982 ImGui::SameLine();
4983 ImGui::SmallButton("SmallButton()");
4984
4985 // Tree
4986 // (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline)
4987 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
4988 ImGui::Button("Button##1"); // Will make line higher
4989 ImGui::SameLine(0.0f, spacing);
4990 if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone))
4991 {
4992 // Placeholder tree data
4993 for (int i = 0; i < 6; i++)
4994 ImGui::BulletText("Item %d..", i);
4995 ImGui::TreePop();
4996 }
4997
4998 const float padding = (float)(int)(ImGui::GetFontSize() * 1.20f); // Large padding
4999 ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, padding);
5000 ImGui::Button("Button##2");
5001 ImGui::PopStyleVar();
5002 ImGui::SameLine(0.0f, spacing);
5003 if (ImGui::TreeNodeEx("Node##2", ImGuiTreeNodeFlags_DrawLinesNone))
5004 ImGui::TreePop();
5005
5006 // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
5007 // Otherwise you can use SmallButton() (smaller fit).
5008 ImGui::AlignTextToFramePadding();
5009
5010 // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
5011 // other contents "inside" the node.
5012 bool node_open = ImGui::TreeNode("Node##3");
5013 ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##3");
5014 if (node_open)
5015 {
5016 // Placeholder tree data
5017 for (int i = 0; i < 6; i++)
5018 ImGui::BulletText("Item %d..", i);
5019 ImGui::TreePop();
5020 }
5021
5022 // Bullet
5023 ImGui::Button("Button##4");
5024 ImGui::SameLine(0.0f, spacing);
5025 ImGui::BulletText("Bullet text");
5026
5027 ImGui::AlignTextToFramePadding();
5028 ImGui::BulletText("Node");
5029 ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##5");
5030 ImGui::Unindent();
5031 }
5032
5033 ImGui::TreePop();
5034 }
5035
5036 if (ImGui::TreeNode("Scrolling"))
5037 {
5038 IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical");
5039 // Vertical scroll functions
5040 HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.");
5041
5042 static int track_item = 50;
5043 static bool enable_track = true;
5044 static bool enable_extra_decorations = false;
5045 static float scroll_to_off_px = 0.0f;
5046 static float scroll_to_pos_px = 200.0f;
5047
5048 ImGui::Checkbox("Decoration", &enable_extra_decorations);
5049
5050 ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
5051 enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d");
5052 ImGui::SameLine();
5053 ImGui::Checkbox("Track", &enable_track);
5054
5055 bool scroll_to_off = ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px");
5056 ImGui::SameLine();
5057 scroll_to_off |= ImGui::Button("Scroll Offset");
5058
5059 bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");
5060 ImGui::SameLine();
5061 scroll_to_pos |= ImGui::Button("Scroll To Pos");
5062 ImGui::PopItemWidth();
5063
5064 if (scroll_to_off || scroll_to_pos)
5065 enable_track = false;
5066
5067 ImGuiStyle& style = ImGui::GetStyle();
5068 float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;
5069 if (child_w < 1.0f)
5070 child_w = 1.0f;
5071 ImGui::PushID("##VerticalScrolling");
5072 for (int i = 0; i < 5; i++)
5073 {
5074 if (i > 0) ImGui::SameLine();
5075 ImGui::BeginGroup();
5076 const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" };
5077 ImGui::TextUnformatted(names[i]);
5078
5079 const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
5080 const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
5081 const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Borders, child_flags);
5082 if (ImGui::BeginMenuBar())
5083 {
5084 ImGui::TextUnformatted("abc");
5085 ImGui::EndMenuBar();
5086 }
5087 if (scroll_to_off)
5088 ImGui::SetScrollY(scroll_to_off_px);
5089 if (scroll_to_pos)
5090 ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);
5091 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
5092 {
5093 for (int item = 0; item < 100; item++)
5094 {
5095 if (enable_track && item == track_item)
5096 {
5097 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
5098 ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom
5099 }
5100 else
5101 {
5102 ImGui::Text("Item %d", item);
5103 }
5104 }
5105 }
5106 float scroll_y = ImGui::GetScrollY();
5107 float scroll_max_y = ImGui::GetScrollMaxY();
5108 ImGui::EndChild();
5109 ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y);
5110 ImGui::EndGroup();
5111 }
5112 ImGui::PopID();
5113
5114 // Horizontal scroll functions
5115 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal");
5116 ImGui::Spacing();
5117 HelpMarker(
5118 "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
5119 "Because the clipping rectangle of most window hides half worth of WindowPadding on the "
5120 "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the "
5121 "equivalent SetScrollFromPosY(+1) wouldn't.");
5122 ImGui::PushID("##HorizontalScrolling");
5123 for (int i = 0; i < 5; i++)
5124 {
5125 float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
5126 ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
5127 ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
5128 bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Borders, child_flags);
5129 if (scroll_to_off)
5130 ImGui::SetScrollX(scroll_to_off_px);
5131 if (scroll_to_pos)
5132 ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f);
5133 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
5134 {
5135 for (int item = 0; item < 100; item++)
5136 {
5137 if (item > 0)
5138 ImGui::SameLine();
5139 if (enable_track && item == track_item)
5140 {
5141 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
5142 ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right
5143 }
5144 else
5145 {
5146 ImGui::Text("Item %d", item);
5147 }
5148 }
5149 }
5150 float scroll_x = ImGui::GetScrollX();
5151 float scroll_max_x = ImGui::GetScrollMaxX();
5152 ImGui::EndChild();
5153 ImGui::SameLine();
5154 const char* names[] = { "Left", "25%", "Center", "75%", "Right" };
5155 ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x);
5156 ImGui::Spacing();
5157 }
5158 ImGui::PopID();
5159
5160 // Miscellaneous Horizontal Scrolling Demo
5161 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)");
5162 HelpMarker(
5163 "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n"
5164 "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().");
5165 static int lines = 7;
5166 ImGui::SliderInt("Lines", &lines, 1, 15);
5167 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
5168 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
5169 ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
5170 ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar);
5171 for (int line = 0; line < lines; line++)
5172 {
5173 // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
5174 // If you want to create your own time line for a real application you may be better off manipulating
5175 // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
5176 // yourself. You may also want to use the lower-level ImDrawList API.
5177 int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);
5178 for (int n = 0; n < num_buttons; n++)
5179 {
5180 if (n > 0) ImGui::SameLine();
5181 ImGui::PushID(n + line * 1000);
5182 char num_buf[16];
5183 sprintf(num_buf, "%d", n);
5184 const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf;
5185 float hue = n * 0.05f;
5186 ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f));
5187 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f));
5188 ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f));
5189 ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f));
5190 ImGui::PopStyleColor(3);
5191 ImGui::PopID();
5192 }
5193 }
5194 float scroll_x = ImGui::GetScrollX();
5195 float scroll_max_x = ImGui::GetScrollMaxX();
5196 ImGui::EndChild();
5197 ImGui::PopStyleVar(2);
5198 float scroll_x_delta = 0.0f;
5199 ImGui::SmallButton("<<");
5200 if (ImGui::IsItemActive())
5201 scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;
5202 ImGui::SameLine();
5203 ImGui::Text("Scroll from code"); ImGui::SameLine();
5204 ImGui::SmallButton(">>");
5205 if (ImGui::IsItemActive())
5206 scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;
5207 ImGui::SameLine();
5208 ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x);
5209 if (scroll_x_delta != 0.0f)
5210 {
5211 // Demonstrate a trick: you can use Begin to set yourself in the context of another window
5212 // (here we are already out of your child window)
5213 ImGui::BeginChild("scrolling");
5214 ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
5215 ImGui::EndChild();
5216 }
5217 ImGui::Spacing();
5218
5219 static bool show_horizontal_contents_size_demo_window = false;
5220 ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window);
5221
5222 if (show_horizontal_contents_size_demo_window)
5223 {
5224 static bool show_h_scrollbar = true;
5225 static bool show_button = true;
5226 static bool show_tree_nodes = true;
5227 static bool show_text_wrapped = false;
5228 static bool show_columns = true;
5229 static bool show_tab_bar = true;
5230 static bool show_child = false;
5231 static bool explicit_content_size = false;
5232 static float contents_size_x = 300.0f;
5233 if (explicit_content_size)
5234 ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));
5235 ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);
5236 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window");
5237 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0));
5238 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0));
5239 HelpMarker(
5240 "Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n"
5241 "Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles.");
5242 ImGui::Checkbox("H-scrollbar", &show_h_scrollbar);
5243 ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten)
5244 ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width
5245 ImGui::Checkbox("Text wrapped", &show_text_wrapped);// Will grow and use contents size
5246 ImGui::Checkbox("Columns", &show_columns); // Will use contents size
5247 ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size
5248 ImGui::Checkbox("Child", &show_child); // Will grow and use contents size
5249 ImGui::Checkbox("Explicit content size", &explicit_content_size);
5250 ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY());
5251 if (explicit_content_size)
5252 {
5253 ImGui::SameLine();
5254 ImGui::SetNextItemWidth(ImGui::CalcTextSize("123456").x);
5255 ImGui::DragFloat("##csx", &contents_size_x);
5256 ImVec2 p = ImGui::GetCursorScreenPos();
5257 ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);
5258 ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);
5259 ImGui::Dummy(ImVec2(0, 10));
5260 }
5261 ImGui::PopStyleVar(2);
5262 ImGui::Separator();
5263 if (show_button)
5264 {
5265 ImGui::Button("this is a 300-wide button", ImVec2(300, 0));
5266 }
5267 if (show_tree_nodes)
5268 {
5269 bool open = true;
5270 if (ImGui::TreeNode("this is a tree node"))
5271 {
5272 if (ImGui::TreeNode("another one of those tree node..."))
5273 {
5274 ImGui::Text("Some tree contents");
5275 ImGui::TreePop();
5276 }
5277 ImGui::TreePop();
5278 }
5279 ImGui::CollapsingHeader("CollapsingHeader", &open);
5280 }
5281 if (show_text_wrapped)
5282 {
5283 ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle.");
5284 }
5285 if (show_columns)
5286 {
5287 ImGui::Text("Tables:");
5288 if (ImGui::BeginTable("table", 4, ImGuiTableFlags_Borders))
5289 {
5290 for (int n = 0; n < 4; n++)
5291 {
5292 ImGui::TableNextColumn();
5293 ImGui::Text("Width %.2f", ImGui::GetContentRegionAvail().x);
5294 }
5295 ImGui::EndTable();
5296 }
5297 ImGui::Text("Columns:");
5298 ImGui::Columns(4);
5299 for (int n = 0; n < 4; n++)
5300 {
5301 ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
5302 ImGui::NextColumn();
5303 }
5304 ImGui::Columns(1);
5305 }
5306 if (show_tab_bar && ImGui::BeginTabBar("Hello"))
5307 {
5308 if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); }
5309 if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); }
5310 if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); }
5311 if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); }
5312 ImGui::EndTabBar();
5313 }
5314 if (show_child)
5315 {
5316 ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Borders);
5317 ImGui::EndChild();
5318 }
5319 ImGui::End();
5320 }
5321
5322 ImGui::TreePop();
5323 }
5324
5325 if (ImGui::TreeNode("Text Clipping"))
5326 {
5327 IMGUI_DEMO_MARKER("Layout/Text Clipping");
5328 static ImVec2 size(100.0f, 100.0f);
5329 static ImVec2 offset(30.0f, 30.0f);
5330 ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f");
5331 ImGui::TextWrapped("(Click and drag to scroll)");
5332
5333 HelpMarker(
5334 "(Left) Using ImGui::PushClipRect():\n"
5335 "Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
5336 "(use this if you want your clipping rectangle to affect interactions)\n\n"
5337 "(Center) Using ImDrawList::PushClipRect():\n"
5338 "Will alter ImDrawList rendering only.\n"
5339 "(use this as a shortcut if you are only using ImDrawList calls)\n\n"
5340 "(Right) Using ImDrawList::AddText() with a fine ClipRect:\n"
5341 "Will alter only this specific ImDrawList::AddText() rendering.\n"
5342 "This is often used internally to avoid altering the clipping rectangle and minimize draw calls.");
5343
5344 for (int n = 0; n < 3; n++)
5345 {
5346 if (n > 0)
5347 ImGui::SameLine();
5348
5349 ImGui::PushID(n);
5350 ImGui::InvisibleButton("##canvas", size);
5351 if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))
5352 {
5353 offset.x += ImGui::GetIO().MouseDelta.x;
5354 offset.y += ImGui::GetIO().MouseDelta.y;
5355 }
5356 ImGui::PopID();
5357 if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.
5358 continue;
5359
5360 const ImVec2 p0 = ImGui::GetItemRectMin();
5361 const ImVec2 p1 = ImGui::GetItemRectMax();
5362 const char* text_str = "Line 1 hello\nLine 2 clip me!";
5363 const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
5364 ImDrawList* draw_list = ImGui::GetWindowDrawList();
5365 switch (n)
5366 {
5367 case 0:
5368 ImGui::PushClipRect(p0, p1, true);
5369 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5370 draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
5371 ImGui::PopClipRect();
5372 break;
5373 case 1:
5374 draw_list->PushClipRect(p0, p1, true);
5375 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5376 draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
5377 draw_list->PopClipRect();
5378 break;
5379 case 2:
5380 ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
5381 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5382 draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
5383 break;
5384 }
5385 }
5386
5387 ImGui::TreePop();
5388 }
5389
5390 if (ImGui::TreeNode("Overlap Mode"))
5391 {
5392 IMGUI_DEMO_MARKER("Layout/Overlap Mode");
5393 static bool enable_allow_overlap = true;
5394
5395 HelpMarker(
5396 "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n"
5397 "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. "
5398 "Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.");
5399 ImGui::Checkbox("Enable AllowOverlap", &enable_allow_overlap);
5400
5401 ImVec2 button1_pos = ImGui::GetCursorScreenPos();
5402 ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f);
5403 if (enable_allow_overlap)
5404 ImGui::SetNextItemAllowOverlap();
5405 ImGui::Button("Button 1", ImVec2(80, 80));
5406 ImGui::SetCursorScreenPos(button2_pos);
5407 ImGui::Button("Button 2", ImVec2(80, 80));
5408
5409 // This is typically used with width-spanning items.
5410 // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut
5411 // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)
5412 if (enable_allow_overlap)
5413 ImGui::SetNextItemAllowOverlap();
5414 ImGui::Selectable("Some Selectable", false);
5415 ImGui::SameLine();
5416 ImGui::SmallButton("++");
5417
5418 ImGui::TreePop();
5419 }
5420}
5421
5422//-----------------------------------------------------------------------------
5423// [SECTION] DemoWindowPopups()
5424//-----------------------------------------------------------------------------
5425
5426static void DemoWindowPopups()
5427{
5428 if (!ImGui::CollapsingHeader("Popups & Modal windows"))
5429 return;
5430
5431 // The properties of popups windows are:
5432 // - They block normal mouse hovering detection outside them. (*)
5433 // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
5434 // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
5435 // we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
5436 // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even
5437 // when normally blocked by a popup.
5438 // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
5439 // popups at any time.
5440
5441 // Typical use for regular windows:
5442 // 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();
5443 // Typical use for popups:
5444 // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { [...] EndPopup(); }
5445
5446 // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
5447 // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
5448
5449 if (ImGui::TreeNode("Popups"))
5450 {
5451 IMGUI_DEMO_MARKER("Popups/Popups");
5452 ImGui::TextWrapped(
5453 "When a popup is active, it inhibits interacting with windows that are behind the popup. "
5454 "Clicking outside the popup closes it.");
5455
5456 static int selected_fish = -1;
5457 const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" };
5458 static bool toggles[] = { true, false, false, false, false };
5459
5460 // Simple selection popup (if you want to show the current selection inside the Button itself,
5461 // you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
5462 if (ImGui::Button("Select.."))
5463 ImGui::OpenPopup("my_select_popup");
5464 ImGui::SameLine();
5465 ImGui::TextUnformatted(selected_fish == -1 ? "<None>" : names[selected_fish]);
5466 if (ImGui::BeginPopup("my_select_popup"))
5467 {
5468 ImGui::SeparatorText("Aquarium");
5469 for (int i = 0; i < IM_COUNTOF(names); i++)
5470 if (ImGui::Selectable(names[i]))
5471 selected_fish = i;
5472 ImGui::EndPopup();
5473 }
5474
5475 // Showing a menu with toggles
5476 if (ImGui::Button("Toggle.."))
5477 ImGui::OpenPopup("my_toggle_popup");
5478 if (ImGui::BeginPopup("my_toggle_popup"))
5479 {
5480 for (int i = 0; i < IM_COUNTOF(names); i++)
5481 ImGui::MenuItem(names[i], "", &toggles[i]);
5482 if (ImGui::BeginMenu("Sub-menu"))
5483 {
5484 ImGui::MenuItem("Click me");
5485 ImGui::EndMenu();
5486 }
5487
5488 ImGui::Separator();
5489 ImGui::Text("Tooltip here");
5490 ImGui::SetItemTooltip("I am a tooltip over a popup");
5491
5492 if (ImGui::Button("Stacked Popup"))
5493 ImGui::OpenPopup("another popup");
5494 if (ImGui::BeginPopup("another popup"))
5495 {
5496 for (int i = 0; i < IM_COUNTOF(names); i++)
5497 ImGui::MenuItem(names[i], "", &toggles[i]);
5498 if (ImGui::BeginMenu("Sub-menu"))
5499 {
5500 ImGui::MenuItem("Click me");
5501 if (ImGui::Button("Stacked Popup"))
5502 ImGui::OpenPopup("another popup");
5503 if (ImGui::BeginPopup("another popup"))
5504 {
5505 ImGui::Text("I am the last one here.");
5506 ImGui::EndPopup();
5507 }
5508 ImGui::EndMenu();
5509 }
5510 ImGui::EndPopup();
5511 }
5512 ImGui::EndPopup();
5513 }
5514
5515 // Call the more complete ShowExampleMenuFile which we use in various places of this demo
5516 if (ImGui::Button("With a menu.."))
5517 ImGui::OpenPopup("my_file_popup");
5518 if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar))
5519 {
5520 if (ImGui::BeginMenuBar())
5521 {
5522 if (ImGui::BeginMenu("File"))
5523 {
5524 ShowExampleMenuFile();
5525 ImGui::EndMenu();
5526 }
5527 if (ImGui::BeginMenu("Edit"))
5528 {
5529 ImGui::MenuItem("Dummy");
5530 ImGui::EndMenu();
5531 }
5532 ImGui::EndMenuBar();
5533 }
5534 ImGui::Text("Hello from popup!");
5535 ImGui::Button("This is a dummy button..");
5536 ImGui::EndPopup();
5537 }
5538
5539 ImGui::TreePop();
5540 }
5541
5542 if (ImGui::TreeNode("Context menus"))
5543 {
5544 IMGUI_DEMO_MARKER("Popups/Context menus");
5545 HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.");
5546
5547 // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
5548 // if (id == 0)
5549 // id = GetItemID(); // Use last item id
5550 // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
5551 // OpenPopup(id);
5552 // return BeginPopup(id);
5553 // For advanced uses you may want to replicate and customize this code.
5554 // See more details in BeginPopupContextItem().
5555
5556 // Example 1
5557 // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
5558 // and BeginPopupContextItem() will use the last item ID as the popup ID.
5559 {
5560 const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" };
5561 static int selected = -1;
5562 for (int n = 0; n < 5; n++)
5563 {
5564 if (ImGui::Selectable(names[n], selected == n))
5565 selected = n;
5566 if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
5567 {
5568 selected = n;
5569 ImGui::Text("This is a popup for \"%s\"!", names[n]);
5570 if (ImGui::Button("Close"))
5571 ImGui::CloseCurrentPopup();
5572 ImGui::EndPopup();
5573 }
5574 ImGui::SetItemTooltip("Right-click to open popup");
5575 }
5576 }
5577
5578 // Example 2
5579 // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().
5580 // Using an explicit identifier is also convenient if you want to activate the popups from different locations.
5581 {
5582 HelpMarker("Text() elements don't have stable identifiers so we need to provide one.");
5583 static float value = 0.5f;
5584 ImGui::Text("Value = %.3f <-- (1) right-click this text", value);
5585 if (ImGui::BeginPopupContextItem("my popup"))
5586 {
5587 if (ImGui::Selectable("Set to zero")) value = 0.0f;
5588 if (ImGui::Selectable("Set to PI")) value = 3.1415f;
5589 ImGui::SetNextItemWidth(-FLT_MIN);
5590 ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f);
5591 ImGui::EndPopup();
5592 }
5593
5594 // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
5595 // Here we make it that right-clicking this other text element opens the same popup as above.
5596 // The popup itself will be submitted by the code above.
5597 ImGui::Text("(2) Or right-click this text");
5598 ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight);
5599
5600 // Back to square one: manually open the same popup.
5601 if (ImGui::Button("(3) Or click this button"))
5602 ImGui::OpenPopup("my popup");
5603 }
5604
5605 // Example 3
5606 // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),
5607 // we need to make sure your item identifier is stable.
5608 // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).
5609 {
5610 HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.");
5611 static char name[32] = "Label1";
5612 char buf[64];
5613 sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
5614 ImGui::Button(buf);
5615 if (ImGui::BeginPopupContextItem())
5616 {
5617 ImGui::Text("Edit name:");
5618 ImGui::InputText("##edit", name, IM_COUNTOF(name));
5619 if (ImGui::Button("Close"))
5620 ImGui::CloseCurrentPopup();
5621 ImGui::EndPopup();
5622 }
5623 ImGui::SameLine(); ImGui::Text("(<-- right-click here)");
5624 }
5625
5626 ImGui::TreePop();
5627 }
5628
5629 if (ImGui::TreeNode("Modals"))
5630 {
5631 IMGUI_DEMO_MARKER("Popups/Modals");
5632 ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside.");
5633
5634 if (ImGui::Button("Delete.."))
5635 ImGui::OpenPopup("Delete?");
5636
5637 // Always center this window when appearing
5638 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
5639 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
5640
5641 if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
5642 {
5643 ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!");
5644 ImGui::Separator();
5645
5646 //static int unused_i = 0;
5647 //ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0");
5648
5649 static bool dont_ask_me_next_time = false;
5650 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
5651 ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time);
5652 ImGui::PopStyleVar();
5653
5654 if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5655 ImGui::SetItemDefaultFocus();
5656 ImGui::SameLine();
5657 if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5658 ImGui::EndPopup();
5659 }
5660
5661 if (ImGui::Button("Stacked modals.."))
5662 ImGui::OpenPopup("Stacked 1");
5663 if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar))
5664 {
5665 if (ImGui::BeginMenuBar())
5666 {
5667 if (ImGui::BeginMenu("File"))
5668 {
5669 if (ImGui::MenuItem("Some menu item")) {}
5670 ImGui::EndMenu();
5671 }
5672 ImGui::EndMenuBar();
5673 }
5674 ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.");
5675
5676 // Testing behavior of widgets stacking their own regular popups over the modal.
5677 static int item = 1;
5678 static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
5679 ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
5680 ImGui::ColorEdit4("Color", color);
5681
5682 if (ImGui::Button("Add another modal.."))
5683 ImGui::OpenPopup("Stacked 2");
5684
5685 // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
5686 // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
5687 // of the bool actually doesn't matter here.
5688 bool unused_open = true;
5689 if (ImGui::BeginPopupModal("Stacked 2", &unused_open))
5690 {
5691 ImGui::Text("Hello from Stacked The Second!");
5692 ImGui::ColorEdit4("Color", color); // Allow opening another nested popup
5693 if (ImGui::Button("Close"))
5694 ImGui::CloseCurrentPopup();
5695 ImGui::EndPopup();
5696 }
5697
5698 if (ImGui::Button("Close"))
5699 ImGui::CloseCurrentPopup();
5700 ImGui::EndPopup();
5701 }
5702
5703 ImGui::TreePop();
5704 }
5705
5706 if (ImGui::TreeNode("Menus inside a regular window"))
5707 {
5708 IMGUI_DEMO_MARKER("Popups/Menus inside a regular window");
5709 ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
5710 ImGui::Separator();
5711
5712 ImGui::MenuItem("Menu item", "Ctrl+M");
5713 if (ImGui::BeginMenu("Menu inside a regular window"))
5714 {
5715 ShowExampleMenuFile();
5716 ImGui::EndMenu();
5717 }
5718 ImGui::Separator();
5719 ImGui::TreePop();
5720 }
5721}
5722
5723// Dummy data structure that we use for the Table demo.
5724// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)
5725namespace
5726{
5727// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.
5728// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.
5729// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)
5730// If you don't use sorting, you will generally never care about giving column an ID!
5731enum MyItemColumnID
5732{
5733 MyItemColumnID_ID,
5734 MyItemColumnID_Name,
5735 MyItemColumnID_Action,
5736 MyItemColumnID_Quantity,
5737 MyItemColumnID_Description
5738};
5739
5740struct MyItem
5741{
5742 int ID;
5743 const char* Name;
5744 int Quantity;
5745
5746 // We have a problem which is affecting _only this demo_ and should not affect your code:
5747 // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
5748 // however qsort doesn't allow passing user data to comparing function.
5749 // As a workaround, we are storing the sort specs in a static/global for the comparing function to access.
5750 // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.
5751 // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called
5752 // very often by the sorting algorithm it would be a little wasteful.
5753 static const ImGuiTableSortSpecs* s_current_sort_specs;
5754
5755 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count)
5756 {
5757 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
5758 if (items_count > 1)
5759 qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs);
5760 s_current_sort_specs = NULL;
5761 }
5762
5763 // Compare function to be used by qsort()
5764 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
5765 {
5766 const MyItem* a = (const MyItem*)lhs;
5767 const MyItem* b = (const MyItem*)rhs;
5768 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
5769 {
5770 // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()
5771 // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!
5772 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
5773 int delta = 0;
5774 switch (sort_spec->ColumnUserID)
5775 {
5776 case MyItemColumnID_ID: delta = (a->ID - b->ID); break;
5777 case MyItemColumnID_Name: delta = (strcmp(a->Name, b->Name)); break;
5778 case MyItemColumnID_Quantity: delta = (a->Quantity - b->Quantity); break;
5779 case MyItemColumnID_Description: delta = (strcmp(a->Name, b->Name)); break;
5780 default: IM_ASSERT(0); break;
5781 }
5782 if (delta > 0)
5783 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
5784 if (delta < 0)
5785 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
5786 }
5787
5788 // qsort() is instable so always return a way to differentiate items.
5789 // Your own compare function may want to avoid fallback on implicit sort specs.
5790 // e.g. a Name compare if it wasn't already part of the sort specs.
5791 return a->ID - b->ID;
5792 }
5793};
5794const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;
5795}
5796
5797// Make the UI compact because there are so many fields
5798static void PushStyleCompact()
5799{
5800 ImGuiStyle& style = ImGui::GetStyle();
5801 ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, (float)(int)(style.FramePadding.y * 0.60f));
5802 ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, (float)(int)(style.ItemSpacing.y * 0.60f));
5803}
5804
5805static void PopStyleCompact()
5806{
5807 ImGui::PopStyleVar(2);
5808}
5809
5810// Show a combo box with a choice of sizing policies
5811static void EditTableSizingFlags(ImGuiTableFlags* p_flags)
5812{
5813 struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };
5814 static const EnumDesc policies[] =
5815 {
5816 { 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." },
5817 { ImGuiTableFlags_SizingFixedFit, "ImGuiTableFlags_SizingFixedFit", "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." },
5818 { ImGuiTableFlags_SizingFixedSame, "ImGuiTableFlags_SizingFixedSame", "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." },
5819 { ImGuiTableFlags_SizingStretchProp, "ImGuiTableFlags_SizingStretchProp", "Columns default to _WidthStretch with weights proportional to their widths." },
5820 { ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." }
5821 };
5822 int idx;
5823 for (idx = 0; idx < IM_COUNTOF(policies); idx++)
5824 if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))
5825 break;
5826 const char* preview_text = (idx < IM_COUNTOF(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : "";
5827 if (ImGui::BeginCombo("Sizing Policy", preview_text))
5828 {
5829 for (int n = 0; n < IM_COUNTOF(policies); n++)
5830 if (ImGui::Selectable(policies[n].Name, idx == n))
5831 *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;
5832 ImGui::EndCombo();
5833 }
5834 ImGui::SameLine();
5835 ImGui::TextDisabled("(?)");
5836 if (ImGui::BeginItemTooltip())
5837 {
5838 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f);
5839 for (int m = 0; m < IM_COUNTOF(policies); m++)
5840 {
5841 ImGui::Separator();
5842 ImGui::Text("%s:", policies[m].Name);
5843 ImGui::Separator();
5844 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);
5845 ImGui::TextUnformatted(policies[m].Tooltip);
5846 }
5847 ImGui::PopTextWrapPos();
5848 ImGui::EndTooltip();
5849 }
5850}
5851
5852static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)
5853{
5854 ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)");
5855 ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide);
5856 ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort);
5857 if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch))
5858 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);
5859 if (ImGui::CheckboxFlags("_WidthFixed", p_flags, ImGuiTableColumnFlags_WidthFixed))
5860 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);
5861 ImGui::CheckboxFlags("_NoResize", p_flags, ImGuiTableColumnFlags_NoResize);
5862 ImGui::CheckboxFlags("_NoReorder", p_flags, ImGuiTableColumnFlags_NoReorder);
5863 ImGui::CheckboxFlags("_NoHide", p_flags, ImGuiTableColumnFlags_NoHide);
5864 ImGui::CheckboxFlags("_NoClip", p_flags, ImGuiTableColumnFlags_NoClip);
5865 ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort);
5866 ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending);
5867 ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending);
5868 ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel);
5869 ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth);
5870 ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending);
5871 ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending);
5872 ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0");
5873 ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0");
5874 ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader);
5875}
5876
5877static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)
5878{
5879 ImGui::CheckboxFlags("_IsEnabled", &flags, ImGuiTableColumnFlags_IsEnabled);
5880 ImGui::CheckboxFlags("_IsVisible", &flags, ImGuiTableColumnFlags_IsVisible);
5881 ImGui::CheckboxFlags("_IsSorted", &flags, ImGuiTableColumnFlags_IsSorted);
5882 ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered);
5883}
5884
5885//-----------------------------------------------------------------------------
5886// [SECTION] DemoWindowTables()
5887//-----------------------------------------------------------------------------
5888
5889static void DemoWindowTables()
5890{
5891 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
5892 if (!ImGui::CollapsingHeader("Tables & Columns"))
5893 return;
5894
5895 // Using those as a base value to create width/height that are factor of the size of our font
5896 const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
5897 const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
5898
5899 ImGui::PushID("Tables");
5900
5901 int open_action = -1;
5902 if (ImGui::Button("Expand all"))
5903 open_action = 1;
5904 ImGui::SameLine();
5905 if (ImGui::Button("Collapse all"))
5906 open_action = 0;
5907 ImGui::SameLine();
5908
5909 // Options
5910 static bool disable_indent = false;
5911 ImGui::Checkbox("Disable tree indentation", &disable_indent);
5912 ImGui::SameLine();
5913 HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width.");
5914 ImGui::Separator();
5915 if (disable_indent)
5916 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f);
5917
5918 // About Styling of tables
5919 // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.
5920 // There are however a few settings that a shared and part of the ImGuiStyle structure:
5921 // style.CellPadding // Padding within each cell
5922 // style.Colors[ImGuiCol_TableHeaderBg] // Table header background
5923 // style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders
5924 // style.Colors[ImGuiCol_TableBorderLight] // Table inner borders
5925 // style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even rows)
5926 // style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)
5927
5928 // Demos
5929 if (open_action != -1)
5930 ImGui::SetNextItemOpen(open_action != 0);
5931 if (ImGui::TreeNode("Basic"))
5932 {
5933 IMGUI_DEMO_MARKER("Tables/Basic");
5934 // Here we will showcase three different ways to output a table.
5935 // They are very simple variations of a same thing!
5936
5937 // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
5938 // In many situations, this is the most flexible and easy to use pattern.
5939 HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
5940 if (ImGui::BeginTable("table1", 3))
5941 {
5942 for (int row = 0; row < 4; row++)
5943 {
5944 ImGui::TableNextRow();
5945 for (int column = 0; column < 3; column++)
5946 {
5947 ImGui::TableSetColumnIndex(column);
5948 ImGui::Text("Row %d Column %d", row, column);
5949 }
5950 }
5951 ImGui::EndTable();
5952 }
5953
5954 // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().
5955 // This is generally more convenient when you have code manually submitting the contents of each column.
5956 HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.");
5957 if (ImGui::BeginTable("table2", 3))
5958 {
5959 for (int row = 0; row < 4; row++)
5960 {
5961 ImGui::TableNextRow();
5962 ImGui::TableNextColumn();
5963 ImGui::Text("Row %d", row);
5964 ImGui::TableNextColumn();
5965 ImGui::Text("Some contents");
5966 ImGui::TableNextColumn();
5967 ImGui::Text("123.456");
5968 }
5969 ImGui::EndTable();
5970 }
5971
5972 // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),
5973 // as TableNextColumn() will automatically wrap around and create new rows as needed.
5974 // This is generally more convenient when your cells all contains the same type of data.
5975 HelpMarker(
5976 "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains "
5977 "the same type of contents.\n This is also more similar to the old NextColumn() function of the "
5978 "Columns API, and provided to facilitate the Columns->Tables API transition.");
5979 if (ImGui::BeginTable("table3", 3))
5980 {
5981 for (int item = 0; item < 14; item++)
5982 {
5983 ImGui::TableNextColumn();
5984 ImGui::Text("Item %d", item);
5985 }
5986 ImGui::EndTable();
5987 }
5988
5989 ImGui::TreePop();
5990 }
5991
5992 if (open_action != -1)
5993 ImGui::SetNextItemOpen(open_action != 0);
5994 if (ImGui::TreeNode("Borders, background"))
5995 {
5996 IMGUI_DEMO_MARKER("Tables/Borders, background");
5997 // Expose a few Borders related flags interactively
5998 enum ContentsType { CT_Text, CT_FillButton };
5999 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
6000 static bool display_headers = false;
6001 static int contents_type = CT_Text;
6002
6003 PushStyleCompact();
6004 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
6005 ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
6006 ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH");
6007 ImGui::Indent();
6008
6009 ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
6010 ImGui::Indent();
6011 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
6012 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
6013 ImGui::Unindent();
6014
6015 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
6016 ImGui::Indent();
6017 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
6018 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
6019 ImGui::Unindent();
6020
6021 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter);
6022 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner);
6023 ImGui::Unindent();
6024
6025 ImGui::AlignTextToFramePadding(); ImGui::Text("Cell contents:");
6026 ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text);
6027 ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton);
6028 ImGui::Checkbox("Display headers", &display_headers);
6029 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
6030 PopStyleCompact();
6031
6032 if (ImGui::BeginTable("table1", 3, flags))
6033 {
6034 // Display headers so we can inspect their interaction with borders
6035 // (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)
6036 if (display_headers)
6037 {
6038 ImGui::TableSetupColumn("One");
6039 ImGui::TableSetupColumn("Two");
6040 ImGui::TableSetupColumn("Three");
6041 ImGui::TableHeadersRow();
6042 }
6043
6044 for (int row = 0; row < 5; row++)
6045 {
6046 ImGui::TableNextRow();
6047 for (int column = 0; column < 3; column++)
6048 {
6049 ImGui::TableSetColumnIndex(column);
6050 char buf[32];
6051 sprintf(buf, "Hello %d,%d", column, row);
6052 if (contents_type == CT_Text)
6053 ImGui::TextUnformatted(buf);
6054 else if (contents_type == CT_FillButton)
6055 ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
6056 }
6057 }
6058 ImGui::EndTable();
6059 }
6060 ImGui::TreePop();
6061 }
6062
6063 if (open_action != -1)
6064 ImGui::SetNextItemOpen(open_action != 0);
6065 if (ImGui::TreeNode("Resizable, stretch"))
6066 {
6067 IMGUI_DEMO_MARKER("Tables/Resizable, stretch");
6068 // By default, if we don't enable ScrollX the sizing policy for each column is "Stretch"
6069 // All columns maintain a sizing weight, and they will occupy all available width.
6070 static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
6071 PushStyleCompact();
6072 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6073 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
6074 ImGui::SameLine(); HelpMarker(
6075 "Using the _Resizable flag automatically enables the _BordersInnerV flag as well, "
6076 "this is why the resize borders are still showing when unchecking this.");
6077 PopStyleCompact();
6078
6079 if (ImGui::BeginTable("table1", 3, flags))
6080 {
6081 for (int row = 0; row < 5; row++)
6082 {
6083 ImGui::TableNextRow();
6084 for (int column = 0; column < 3; column++)
6085 {
6086 ImGui::TableSetColumnIndex(column);
6087 ImGui::Text("Hello %d,%d", column, row);
6088 }
6089 }
6090 ImGui::EndTable();
6091 }
6092 ImGui::TreePop();
6093 }
6094
6095 if (open_action != -1)
6096 ImGui::SetNextItemOpen(open_action != 0);
6097 if (ImGui::TreeNode("Resizable, fixed"))
6098 {
6099 IMGUI_DEMO_MARKER("Tables/Resizable, fixed");
6100 // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)
6101 // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small)
6102 // If there is not enough available width to fit all columns, they will however be resized down.
6103 // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings
6104 HelpMarker(
6105 "Using _Resizable + _SizingFixedFit flags.\n"
6106 "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n"
6107 "Double-click a column border to auto-fit the column to its contents.");
6108 PushStyleCompact();
6109 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
6110 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
6111 PopStyleCompact();
6112
6113 if (ImGui::BeginTable("table1", 3, flags))
6114 {
6115 for (int row = 0; row < 5; row++)
6116 {
6117 ImGui::TableNextRow();
6118 for (int column = 0; column < 3; column++)
6119 {
6120 ImGui::TableSetColumnIndex(column);
6121 ImGui::Text("Hello %d,%d", column, row);
6122 }
6123 }
6124 ImGui::EndTable();
6125 }
6126 ImGui::TreePop();
6127 }
6128
6129 if (open_action != -1)
6130 ImGui::SetNextItemOpen(open_action != 0);
6131 if (ImGui::TreeNode("Resizable, mixed"))
6132 {
6133 IMGUI_DEMO_MARKER("Tables/Resizable, mixed");
6134 HelpMarker(
6135 "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n"
6136 "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.");
6137 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6138
6139 if (ImGui::BeginTable("table1", 3, flags))
6140 {
6141 ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
6142 ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
6143 ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthStretch);
6144 ImGui::TableHeadersRow();
6145 for (int row = 0; row < 5; row++)
6146 {
6147 ImGui::TableNextRow();
6148 for (int column = 0; column < 3; column++)
6149 {
6150 ImGui::TableSetColumnIndex(column);
6151 ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row);
6152 }
6153 }
6154 ImGui::EndTable();
6155 }
6156 if (ImGui::BeginTable("table2", 6, flags))
6157 {
6158 ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
6159 ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
6160 ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);
6161 ImGui::TableSetupColumn("DDD", ImGuiTableColumnFlags_WidthStretch);
6162 ImGui::TableSetupColumn("EEE", ImGuiTableColumnFlags_WidthStretch);
6163 ImGui::TableSetupColumn("FFF", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);
6164 ImGui::TableHeadersRow();
6165 for (int row = 0; row < 5; row++)
6166 {
6167 ImGui::TableNextRow();
6168 for (int column = 0; column < 6; column++)
6169 {
6170 ImGui::TableSetColumnIndex(column);
6171 ImGui::Text("%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row);
6172 }
6173 }
6174 ImGui::EndTable();
6175 }
6176 ImGui::TreePop();
6177 }
6178
6179 if (open_action != -1)
6180 ImGui::SetNextItemOpen(open_action != 0);
6181 if (ImGui::TreeNode("Reorderable, hideable, with headers"))
6182 {
6183 IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers");
6184 HelpMarker(
6185 "Click and drag column headers to reorder columns.\n\n"
6186 "Right-click on a header to open a context menu.");
6187 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;
6188 PushStyleCompact();
6189 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6190 ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
6191 ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
6192 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody);
6193 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)");
6194 ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
6195 PopStyleCompact();
6196
6197 if (ImGui::BeginTable("table1", 3, flags))
6198 {
6199 // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.
6200 // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)
6201 ImGui::TableSetupColumn("One");
6202 ImGui::TableSetupColumn("Two");
6203 ImGui::TableSetupColumn("Three");
6204 ImGui::TableHeadersRow();
6205 for (int row = 0; row < 6; row++)
6206 {
6207 ImGui::TableNextRow();
6208 for (int column = 0; column < 3; column++)
6209 {
6210 ImGui::TableSetColumnIndex(column);
6211 ImGui::Text("Hello %d,%d", column, row);
6212 }
6213 }
6214 ImGui::EndTable();
6215 }
6216
6217 // Use outer_size.x == 0.0f instead of default to make the table as tight as possible
6218 // (only valid when no scrolling and no stretch column)
6219 if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f)))
6220 {
6221 ImGui::TableSetupColumn("One");
6222 ImGui::TableSetupColumn("Two");
6223 ImGui::TableSetupColumn("Three");
6224 ImGui::TableHeadersRow();
6225 for (int row = 0; row < 6; row++)
6226 {
6227 ImGui::TableNextRow();
6228 for (int column = 0; column < 3; column++)
6229 {
6230 ImGui::TableSetColumnIndex(column);
6231 ImGui::Text("Fixed %d,%d", column, row);
6232 }
6233 }
6234 ImGui::EndTable();
6235 }
6236 ImGui::TreePop();
6237 }
6238
6239 if (open_action != -1)
6240 ImGui::SetNextItemOpen(open_action != 0);
6241 if (ImGui::TreeNode("Padding"))
6242 {
6243 IMGUI_DEMO_MARKER("Tables/Padding");
6244 // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.
6245 // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.
6246 HelpMarker(
6247 "We often want outer padding activated when any using features which makes the edges of a column visible:\n"
6248 "e.g.:\n"
6249 "- BorderOuterV\n"
6250 "- any form of row selection\n"
6251 "Because of this, activating BorderOuterV sets the default to PadOuterX. "
6252 "Using PadOuterX or NoPadOuterX you can override the default.\n\n"
6253 "Actual padding values are using style.CellPadding.\n\n"
6254 "In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.");
6255
6256 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;
6257 PushStyleCompact();
6258 ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX);
6259 ImGui::SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)");
6260 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX);
6261 ImGui::SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)");
6262 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX);
6263 ImGui::SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)");
6264 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV);
6265 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV);
6266 static bool show_headers = false;
6267 ImGui::Checkbox("show_headers", &show_headers);
6268 PopStyleCompact();
6269
6270 if (ImGui::BeginTable("table_padding", 3, flags1))
6271 {
6272 if (show_headers)
6273 {
6274 ImGui::TableSetupColumn("One");
6275 ImGui::TableSetupColumn("Two");
6276 ImGui::TableSetupColumn("Three");
6277 ImGui::TableHeadersRow();
6278 }
6279
6280 for (int row = 0; row < 5; row++)
6281 {
6282 ImGui::TableNextRow();
6283 for (int column = 0; column < 3; column++)
6284 {
6285 ImGui::TableSetColumnIndex(column);
6286 if (row == 0)
6287 {
6288 ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
6289 }
6290 else
6291 {
6292 char buf[32];
6293 sprintf(buf, "Hello %d,%d", column, row);
6294 ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
6295 }
6296 //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)
6297 // ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));
6298 }
6299 }
6300 ImGui::EndTable();
6301 }
6302
6303 // Second example: set style.CellPadding to (0.0) or a custom value.
6304 // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...
6305 HelpMarker("Setting style.CellPadding to (0,0) or a custom value.");
6306 static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
6307 static ImVec2 cell_padding(0.0f, 0.0f);
6308 static bool show_widget_frame_bg = true;
6309
6310 PushStyleCompact();
6311 ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders);
6312 ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH);
6313 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV);
6314 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner);
6315 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter);
6316 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg);
6317 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable);
6318 ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg);
6319 ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f");
6320 PopStyleCompact();
6321
6322 ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);
6323 if (ImGui::BeginTable("table_padding_2", 3, flags2))
6324 {
6325 static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells
6326 static bool init = true;
6327 if (!show_widget_frame_bg)
6328 ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
6329 for (int cell = 0; cell < 3 * 5; cell++)
6330 {
6331 ImGui::TableNextColumn();
6332 if (init)
6333 strcpy(text_bufs[cell], "edit me");
6334 ImGui::SetNextItemWidth(-FLT_MIN);
6335 ImGui::PushID(cell);
6336 ImGui::InputText("##cell", text_bufs[cell], IM_COUNTOF(text_bufs[cell]));
6337 ImGui::PopID();
6338 }
6339 if (!show_widget_frame_bg)
6340 ImGui::PopStyleColor();
6341 init = false;
6342 ImGui::EndTable();
6343 }
6344 ImGui::PopStyleVar();
6345
6346 ImGui::TreePop();
6347 }
6348
6349 if (open_action != -1)
6350 ImGui::SetNextItemOpen(open_action != 0);
6351 if (ImGui::TreeNode("Sizing policies"))
6352 {
6353 IMGUI_DEMO_MARKER("Tables/Explicit widths");
6354 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6355 PushStyleCompact();
6356 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
6357 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags1, ImGuiTableFlags_NoHostExtendX);
6358 PopStyleCompact();
6359
6360 static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame };
6361 for (int table_n = 0; table_n < 4; table_n++)
6362 {
6363 ImGui::PushID(table_n);
6364 ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);
6365 EditTableSizingFlags(&sizing_policy_flags[table_n]);
6366
6367 // To make it easier to understand the different sizing policy,
6368 // For each policy: we display one table where the columns have equal contents width,
6369 // and one where the columns have different contents width.
6370 if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1))
6371 {
6372 for (int row = 0; row < 3; row++)
6373 {
6374 ImGui::TableNextRow();
6375 ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6376 ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6377 ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6378 }
6379 ImGui::EndTable();
6380 }
6381 if (ImGui::BeginTable("table2", 3, sizing_policy_flags[table_n] | flags1))
6382 {
6383 for (int row = 0; row < 3; row++)
6384 {
6385 ImGui::TableNextRow();
6386 ImGui::TableNextColumn(); ImGui::Text("AAAA");
6387 ImGui::TableNextColumn(); ImGui::Text("BBBBBBBB");
6388 ImGui::TableNextColumn(); ImGui::Text("CCCCCCCCCCCC");
6389 }
6390 ImGui::EndTable();
6391 }
6392 ImGui::PopID();
6393 }
6394
6395 ImGui::Spacing();
6396 ImGui::TextUnformatted("Advanced");
6397 ImGui::SameLine();
6398 HelpMarker(
6399 "This section allows you to interact and see the effect of various sizing policies "
6400 "depending on whether Scroll is enabled and the contents of your columns.");
6401
6402 enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };
6403 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
6404 static int contents_type = CT_ShowWidth;
6405 static int column_count = 3;
6406
6407 PushStyleCompact();
6408 ImGui::PushID("Advanced");
6409 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
6410 EditTableSizingFlags(&flags);
6411 ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0");
6412 if (contents_type == CT_FillButton)
6413 {
6414 ImGui::SameLine();
6415 HelpMarker(
6416 "Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop "
6417 "where contents width can feed into auto-column width can feed into contents width.");
6418 }
6419 ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp);
6420 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6421 ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
6422 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.");
6423 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
6424 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6425 ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
6426 ImGui::PopItemWidth();
6427 ImGui::PopID();
6428 PopStyleCompact();
6429
6430 if (ImGui::BeginTable("table2", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))
6431 {
6432 for (int cell = 0; cell < 10 * column_count; cell++)
6433 {
6434 ImGui::TableNextColumn();
6435 int column = ImGui::TableGetColumnIndex();
6436 int row = ImGui::TableGetRowIndex();
6437
6438 ImGui::PushID(cell);
6439 char label[32];
6440 static char text_buf[32] = "";
6441 sprintf(label, "Hello %d,%d", column, row);
6442 switch (contents_type)
6443 {
6444 case CT_ShortText: ImGui::TextUnformatted(label); break;
6445 case CT_LongText: ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break;
6446 case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break;
6447 case CT_Button: ImGui::Button(label); break;
6448 case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break;
6449 case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_COUNTOF(text_buf)); break;
6450 }
6451 ImGui::PopID();
6452 }
6453 ImGui::EndTable();
6454 }
6455 ImGui::TreePop();
6456 }
6457
6458 if (open_action != -1)
6459 ImGui::SetNextItemOpen(open_action != 0);
6460 if (ImGui::TreeNode("Vertical scrolling, with clipping"))
6461 {
6462 IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping");
6463 HelpMarker(
6464 "Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n"
6465 "We also demonstrate using ImGuiListClipper to virtualize the submission of many items.");
6466 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6467
6468 PushStyleCompact();
6469 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6470 PopStyleCompact();
6471
6472 // When using ScrollX or ScrollY we need to specify a size for our table container!
6473 // Otherwise by default the table will fit all available space, like a BeginChild() call.
6474 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6475 if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size))
6476 {
6477 ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
6478 ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None);
6479 ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None);
6480 ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None);
6481 ImGui::TableHeadersRow();
6482
6483 // Demonstrate using clipper for large vertical lists
6484 ImGuiListClipper clipper;
6485 clipper.Begin(1000);
6486 while (clipper.Step())
6487 {
6488 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)
6489 {
6490 ImGui::TableNextRow();
6491 for (int column = 0; column < 3; column++)
6492 {
6493 ImGui::TableSetColumnIndex(column);
6494 ImGui::Text("Hello %d,%d", column, row);
6495 }
6496 }
6497 }
6498 ImGui::EndTable();
6499 }
6500 ImGui::TreePop();
6501 }
6502
6503 if (open_action != -1)
6504 ImGui::SetNextItemOpen(open_action != 0);
6505 if (ImGui::TreeNode("Horizontal scrolling"))
6506 {
6507 IMGUI_DEMO_MARKER("Tables/Horizontal scrolling");
6508 HelpMarker(
6509 "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, "
6510 "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n"
6511 "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, "
6512 "because the container window won't automatically extend vertically to fix contents "
6513 "(this may be improved in future versions).");
6514 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6515 static int freeze_cols = 1;
6516 static int freeze_rows = 1;
6517
6518 PushStyleCompact();
6519 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6520 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
6521 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6522 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6523 ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
6524 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6525 ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
6526 PopStyleCompact();
6527
6528 // When using ScrollX or ScrollY we need to specify a size for our table container!
6529 // Otherwise by default the table will fit all available space, like a BeginChild() call.
6530 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6531 if (ImGui::BeginTable("table_scrollx", 7, flags, outer_size))
6532 {
6533 ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
6534 ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()
6535 ImGui::TableSetupColumn("One");
6536 ImGui::TableSetupColumn("Two");
6537 ImGui::TableSetupColumn("Three");
6538 ImGui::TableSetupColumn("Four");
6539 ImGui::TableSetupColumn("Five");
6540 ImGui::TableSetupColumn("Six");
6541 ImGui::TableHeadersRow();
6542 for (int row = 0; row < 20; row++)
6543 {
6544 ImGui::TableNextRow();
6545 for (int column = 0; column < 7; column++)
6546 {
6547 // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.
6548 // Because here we know that:
6549 // - A) all our columns are contributing the same to row height
6550 // - B) column 0 is always visible,
6551 // We only always submit this one column and can skip others.
6552 // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().
6553 if (!ImGui::TableSetColumnIndex(column) && column > 0)
6554 continue;
6555 if (column == 0)
6556 ImGui::Text("Line %d", row);
6557 else
6558 ImGui::Text("Hello world %d,%d", column, row);
6559 }
6560 }
6561 ImGui::EndTable();
6562 }
6563
6564 ImGui::Spacing();
6565 ImGui::TextUnformatted("Stretch + ScrollX");
6566 ImGui::SameLine();
6567 HelpMarker(
6568 "Showcase using Stretch columns + ScrollX together: "
6569 "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n"
6570 "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns "
6571 "along with ScrollX doesn't make sense.");
6572 static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6573 static float inner_width = 1000.0f;
6574 PushStyleCompact();
6575 ImGui::PushID("flags3");
6576 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
6577 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX);
6578 ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f");
6579 ImGui::PopItemWidth();
6580 ImGui::PopID();
6581 PopStyleCompact();
6582 if (ImGui::BeginTable("table2", 7, flags2, outer_size, inner_width))
6583 {
6584 for (int cell = 0; cell < 20 * 7; cell++)
6585 {
6586 ImGui::TableNextColumn();
6587 ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());
6588 }
6589 ImGui::EndTable();
6590 }
6591 ImGui::TreePop();
6592 }
6593
6594 if (open_action != -1)
6595 ImGui::SetNextItemOpen(open_action != 0);
6596 if (ImGui::TreeNode("Columns flags"))
6597 {
6598 IMGUI_DEMO_MARKER("Tables/Columns flags");
6599 // Create a first table just to show all the options/flags we want to make visible in our example!
6600 const int column_count = 3;
6601 const char* column_names[column_count] = { "One", "Two", "Three" };
6602 static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide };
6603 static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags()
6604
6605 if (ImGui::BeginTable("table_columns_flags_checkboxes", column_count, ImGuiTableFlags_None))
6606 {
6607 PushStyleCompact();
6608 for (int column = 0; column < column_count; column++)
6609 {
6610 ImGui::TableNextColumn();
6611 ImGui::PushID(column);
6612 ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns
6613 ImGui::Text("'%s'", column_names[column]);
6614 ImGui::Spacing();
6615 ImGui::Text("Input flags:");
6616 EditTableColumnsFlags(&column_flags[column]);
6617 ImGui::Spacing();
6618 ImGui::Text("Output flags:");
6619 ImGui::BeginDisabled();
6620 ShowTableColumnsStatusFlags(column_flags_out[column]);
6621 ImGui::EndDisabled();
6622 ImGui::PopID();
6623 }
6624 PopStyleCompact();
6625 ImGui::EndTable();
6626 }
6627
6628 // Create the real table we care about for the example!
6629 // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above,
6630 // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible
6631 // + resizing the parent window down).
6632 const ImGuiTableFlags flags
6633 = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
6634 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV
6635 | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;
6636 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);
6637 if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size))
6638 {
6639 bool has_angled_header = false;
6640 for (int column = 0; column < column_count; column++)
6641 {
6642 has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;
6643 ImGui::TableSetupColumn(column_names[column], column_flags[column]);
6644 }
6645 if (has_angled_header)
6646 ImGui::TableAngledHeadersRow();
6647 ImGui::TableHeadersRow();
6648 for (int column = 0; column < column_count; column++)
6649 column_flags_out[column] = ImGui::TableGetColumnFlags(column);
6650 float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);
6651 for (int row = 0; row < 8; row++)
6652 {
6653 // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.
6654 ImGui::Indent(indent_step);
6655 ImGui::TableNextRow();
6656 for (int column = 0; column < column_count; column++)
6657 {
6658 ImGui::TableSetColumnIndex(column);
6659 ImGui::Text("%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column));
6660 }
6661 }
6662 ImGui::Unindent(indent_step * 8.0f);
6663
6664 ImGui::EndTable();
6665 }
6666 ImGui::TreePop();
6667 }
6668
6669 if (open_action != -1)
6670 ImGui::SetNextItemOpen(open_action != 0);
6671 if (ImGui::TreeNode("Columns widths"))
6672 {
6673 IMGUI_DEMO_MARKER("Tables/Columns widths");
6674 HelpMarker("Using TableSetupColumn() to setup default width.");
6675
6676 static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;
6677 PushStyleCompact();
6678 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
6679 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize);
6680 PopStyleCompact();
6681 if (ImGui::BeginTable("table1", 3, flags1))
6682 {
6683 // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.
6684 ImGui::TableSetupColumn("one", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f
6685 ImGui::TableSetupColumn("two", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f
6686 ImGui::TableSetupColumn("three", ImGuiTableColumnFlags_WidthFixed); // Default to auto
6687 ImGui::TableHeadersRow();
6688 for (int row = 0; row < 4; row++)
6689 {
6690 ImGui::TableNextRow();
6691 for (int column = 0; column < 3; column++)
6692 {
6693 ImGui::TableSetColumnIndex(column);
6694 if (row == 0)
6695 ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6696 else
6697 ImGui::Text("Hello %d,%d", column, row);
6698 }
6699 }
6700 ImGui::EndTable();
6701 }
6702
6703 HelpMarker(
6704 "Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, "
6705 "fixed columns with set width may still be shrunk down if there's not enough space in the host.");
6706
6707 static ImGuiTableFlags flags2 = ImGuiTableFlags_None;
6708 PushStyleCompact();
6709 ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags2, ImGuiTableFlags_NoKeepColumnsVisible);
6710 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags2, ImGuiTableFlags_BordersInnerV);
6711 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags2, ImGuiTableFlags_BordersOuterV);
6712 PopStyleCompact();
6713 if (ImGui::BeginTable("table2", 4, flags2))
6714 {
6715 // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns
6716 // will default to ImGuiTableColumnFlags_WidthFixed.
6717 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f);
6718 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
6719 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f);
6720 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
6721 for (int row = 0; row < 5; row++)
6722 {
6723 ImGui::TableNextRow();
6724 for (int column = 0; column < 4; column++)
6725 {
6726 ImGui::TableSetColumnIndex(column);
6727 if (row == 0)
6728 ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6729 else
6730 ImGui::Text("Hello %d,%d", column, row);
6731 }
6732 }
6733 ImGui::EndTable();
6734 }
6735 ImGui::TreePop();
6736 }
6737
6738 if (open_action != -1)
6739 ImGui::SetNextItemOpen(open_action != 0);
6740 if (ImGui::TreeNode("Nested tables"))
6741 {
6742 IMGUI_DEMO_MARKER("Tables/Nested tables");
6743 HelpMarker("This demonstrates embedding a table into another table cell.");
6744
6745 if (ImGui::BeginTable("table_nested1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6746 {
6747 ImGui::TableSetupColumn("A0");
6748 ImGui::TableSetupColumn("A1");
6749 ImGui::TableHeadersRow();
6750
6751 ImGui::TableNextColumn();
6752 ImGui::Text("A0 Row 0");
6753 {
6754 float rows_height = (TEXT_BASE_HEIGHT * 2.0f) + (ImGui::GetStyle().CellPadding.y * 2.0f);
6755 if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6756 {
6757 ImGui::TableSetupColumn("B0");
6758 ImGui::TableSetupColumn("B1");
6759 ImGui::TableHeadersRow();
6760
6761 ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
6762 ImGui::TableNextColumn();
6763 ImGui::Text("B0 Row 0");
6764 ImGui::TableNextColumn();
6765 ImGui::Text("B1 Row 0");
6766 ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
6767 ImGui::TableNextColumn();
6768 ImGui::Text("B0 Row 1");
6769 ImGui::TableNextColumn();
6770 ImGui::Text("B1 Row 1");
6771
6772 ImGui::EndTable();
6773 }
6774 }
6775 ImGui::TableNextColumn(); ImGui::Text("A1 Row 0");
6776 ImGui::TableNextColumn(); ImGui::Text("A0 Row 1");
6777 ImGui::TableNextColumn(); ImGui::Text("A1 Row 1");
6778 ImGui::EndTable();
6779 }
6780 ImGui::TreePop();
6781 }
6782
6783 if (open_action != -1)
6784 ImGui::SetNextItemOpen(open_action != 0);
6785 if (ImGui::TreeNode("Row height"))
6786 {
6787 IMGUI_DEMO_MARKER("Tables/Row height");
6788 HelpMarker(
6789 "You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, "
6790 "so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n"
6791 "We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.");
6792 if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_Borders))
6793 {
6794 for (int row = 0; row < 8; row++)
6795 {
6796 float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row + ImGui::GetStyle().CellPadding.y * 2.0f);
6797 ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);
6798 ImGui::TableNextColumn();
6799 ImGui::Text("min_row_height = %.2f", min_row_height);
6800 }
6801 ImGui::EndTable();
6802 }
6803
6804 HelpMarker(
6805 "Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n"
6806 "Please note that Tables Row Height is not the same thing as Current Line Height, "
6807 "as a table cell may contains multiple lines.");
6808 if (ImGui::BeginTable("table_share_lineheight", 2, ImGuiTableFlags_Borders))
6809 {
6810 ImGui::TableNextRow();
6811 ImGui::TableNextColumn();
6812 ImGui::ColorButton("##1", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));
6813 ImGui::TableNextColumn();
6814 ImGui::Text("Line 1");
6815 ImGui::Text("Line 2");
6816
6817 ImGui::TableNextRow();
6818 ImGui::TableNextColumn();
6819 ImGui::ColorButton("##2", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));
6820 ImGui::TableNextColumn();
6821 ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column
6822 ImGui::Text("Line 1, with SameLine(0,0)");
6823 ImGui::Text("Line 2");
6824
6825 ImGui::EndTable();
6826 }
6827
6828 HelpMarker("Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.");
6829 if (ImGui::BeginTable("table_changing_cellpadding_y", 1, ImGuiTableFlags_Borders))
6830 {
6831 ImGuiStyle& style = ImGui::GetStyle();
6832 for (int row = 0; row < 8; row++)
6833 {
6834 if ((row % 3) == 2)
6835 ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding, 20.0f);
6836 ImGui::TableNextRow(ImGuiTableRowFlags_None);
6837 ImGui::TableNextColumn();
6838 ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y);
6839 if ((row % 3) == 2)
6840 ImGui::PopStyleVar();
6841 }
6842 ImGui::EndTable();
6843 }
6844
6845 ImGui::TreePop();
6846 }
6847
6848 if (open_action != -1)
6849 ImGui::SetNextItemOpen(open_action != 0);
6850 if (ImGui::TreeNode("Outer size"))
6851 {
6852 IMGUI_DEMO_MARKER("Tables/Outer size");
6853 // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY
6854 // Important to that note how the two flags have slightly different behaviors!
6855 ImGui::Text("Using NoHostExtendX and NoHostExtendY:");
6856 PushStyleCompact();
6857 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
6858 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
6859 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.");
6860 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
6861 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.");
6862 PopStyleCompact();
6863
6864 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
6865 if (ImGui::BeginTable("table1", 3, flags, outer_size))
6866 {
6867 for (int row = 0; row < 10; row++)
6868 {
6869 ImGui::TableNextRow();
6870 for (int column = 0; column < 3; column++)
6871 {
6872 ImGui::TableNextColumn();
6873 ImGui::Text("Cell %d,%d", column, row);
6874 }
6875 }
6876 ImGui::EndTable();
6877 }
6878 ImGui::SameLine();
6879 ImGui::Text("Hello!");
6880
6881 ImGui::Spacing();
6882
6883 ImGui::Text("Using explicit size:");
6884 if (ImGui::BeginTable("table2", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6885 {
6886 for (int row = 0; row < 5; row++)
6887 {
6888 ImGui::TableNextRow();
6889 for (int column = 0; column < 3; column++)
6890 {
6891 ImGui::TableNextColumn();
6892 ImGui::Text("Cell %d,%d", column, row);
6893 }
6894 }
6895 ImGui::EndTable();
6896 }
6897 ImGui::SameLine();
6898 if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6899 {
6900 const float rows_height = TEXT_BASE_HEIGHT * 1.5f + ImGui::GetStyle().CellPadding.y * 2.0f;
6901 for (int row = 0; row < 3; row++)
6902 {
6903 ImGui::TableNextRow(0, rows_height);
6904 for (int column = 0; column < 3; column++)
6905 {
6906 ImGui::TableNextColumn();
6907 ImGui::Text("Cell %d,%d", column, row);
6908 }
6909 }
6910 ImGui::EndTable();
6911 }
6912
6913 ImGui::TreePop();
6914 }
6915
6916 if (open_action != -1)
6917 ImGui::SetNextItemOpen(open_action != 0);
6918 if (ImGui::TreeNode("Background color"))
6919 {
6920 IMGUI_DEMO_MARKER("Tables/Background color");
6921 static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;
6922 static int row_bg_type = 1;
6923 static int row_bg_target = 1;
6924 static int cell_bg_type = 1;
6925
6926 PushStyleCompact();
6927 ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
6928 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
6929 ImGui::SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.");
6930 ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0");
6931 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.");
6932 ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0"); ImGui::SameLine(); HelpMarker("We are colorizing cells to B1->C2 here.");
6933 IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);
6934 IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);
6935 IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);
6936 PopStyleCompact();
6937
6938 if (ImGui::BeginTable("table1", 5, flags))
6939 {
6940 for (int row = 0; row < 6; row++)
6941 {
6942 ImGui::TableNextRow();
6943
6944 // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'
6945 // 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.
6946 if (row_bg_type != 0)
6947 {
6948 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?
6949 ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color);
6950 }
6951
6952 // Fill cells
6953 for (int column = 0; column < 5; column++)
6954 {
6955 ImGui::TableSetColumnIndex(column);
6956 ImGui::Text("%c%c", 'A' + row, '0' + column);
6957
6958 // Change background of Cells B1->C2
6959 // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'
6960 // (the CellBg color will be blended over the RowBg and ColumnBg colors)
6961 // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.
6962 if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)
6963 {
6964 ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f));
6965 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);
6966 }
6967 }
6968 }
6969 ImGui::EndTable();
6970 }
6971 ImGui::TreePop();
6972 }
6973
6974 if (open_action != -1)
6975 ImGui::SetNextItemOpen(open_action != 0);
6976 if (ImGui::TreeNode("Tree view"))
6977 {
6978 IMGUI_DEMO_MARKER("Tables/Tree view");
6979 static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
6980
6981 static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull;
6982 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanFullWidth);
6983 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanLabelWidth);
6984 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanAllColumns);
6985 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_LabelSpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_LabelSpanAllColumns);
6986 ImGui::SameLine(); HelpMarker("Useful if you know that you aren't displaying contents in other columns");
6987
6988 HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns.");
6989 if (ImGui::BeginTable("3ways", 3, table_flags))
6990 {
6991 // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
6992 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide);
6993 ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
6994 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f);
6995 ImGui::TableHeadersRow();
6996
6997 // Simple storage to output a dummy file-system.
6998 struct MyTreeNode
6999 {
7000 const char* Name;
7001 const char* Type;
7002 int Size;
7003 int ChildIdx;
7004 int ChildCount;
7005 static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
7006 {
7007 ImGui::TableNextRow();
7008 ImGui::TableNextColumn();
7009 const bool is_folder = (node->ChildCount > 0);
7010
7011 ImGuiTreeNodeFlags node_flags = tree_node_flags_base;
7012 if (node != &all_nodes[0])
7013 node_flags &= ~ImGuiTreeNodeFlags_LabelSpanAllColumns; // Only demonstrate this on the root node.
7014
7015 if (is_folder)
7016 {
7017 bool open = ImGui::TreeNodeEx(node->Name, node_flags);
7018 if ((node_flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) == 0)
7019 {
7020 ImGui::TableNextColumn();
7021 ImGui::TextDisabled("--");
7022 ImGui::TableNextColumn();
7023 ImGui::TextUnformatted(node->Type);
7024 }
7025 if (open)
7026 {
7027 for (int child_n = 0; child_n < node->ChildCount; child_n++)
7028 DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes);
7029 ImGui::TreePop();
7030 }
7031 }
7032 else
7033 {
7034 ImGui::TreeNodeEx(node->Name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen);
7035 ImGui::TableNextColumn();
7036 ImGui::Text("%d", node->Size);
7037 ImGui::TableNextColumn();
7038 ImGui::TextUnformatted(node->Type);
7039 }
7040 }
7041 };
7042 static const MyTreeNode nodes[] =
7043 {
7044 { "Root with Long Name", "Folder", -1, 1, 3 }, // 0
7045 { "Music", "Folder", -1, 4, 2 }, // 1
7046 { "Textures", "Folder", -1, 6, 3 }, // 2
7047 { "desktop.ini", "System file", 1024, -1,-1 }, // 3
7048 { "File1_a.wav", "Audio file", 123000, -1,-1 }, // 4
7049 { "File1_b.wav", "Audio file", 456000, -1,-1 }, // 5
7050 { "Image001.png", "Image file", 203128, -1,-1 }, // 6
7051 { "Copy of Image001.png", "Image file", 203256, -1,-1 }, // 7
7052 { "Copy of Image001 (Final2).png","Image file", 203512, -1,-1 }, // 8
7053 };
7054
7055 MyTreeNode::DisplayNode(&nodes[0], nodes);
7056
7057 ImGui::EndTable();
7058 }
7059 ImGui::TreePop();
7060 }
7061
7062 if (open_action != -1)
7063 ImGui::SetNextItemOpen(open_action != 0);
7064 if (ImGui::TreeNode("Item width"))
7065 {
7066 IMGUI_DEMO_MARKER("Tables/Item width");
7067 HelpMarker(
7068 "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n"
7069 "Note that on auto-resizing non-resizable fixed columns, querying the content width for "
7070 "e.g. right-alignment doesn't make sense.");
7071 if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders))
7072 {
7073 ImGui::TableSetupColumn("small");
7074 ImGui::TableSetupColumn("half");
7075 ImGui::TableSetupColumn("right-align");
7076 ImGui::TableHeadersRow();
7077
7078 for (int row = 0; row < 3; row++)
7079 {
7080 ImGui::TableNextRow();
7081 if (row == 0)
7082 {
7083 // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)
7084 ImGui::TableSetColumnIndex(0);
7085 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small
7086 ImGui::TableSetColumnIndex(1);
7087 ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
7088 ImGui::TableSetColumnIndex(2);
7089 ImGui::PushItemWidth(-FLT_MIN); // Right-aligned
7090 }
7091
7092 // Draw our contents
7093 static float dummy_f = 0.0f;
7094 ImGui::PushID(row);
7095 ImGui::TableSetColumnIndex(0);
7096 ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f);
7097 ImGui::TableSetColumnIndex(1);
7098 ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f);
7099 ImGui::TableSetColumnIndex(2);
7100 ImGui::SliderFloat("##float2", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned
7101 ImGui::PopID();
7102 }
7103 ImGui::EndTable();
7104 }
7105 ImGui::TreePop();
7106 }
7107
7108 // Demonstrate using TableHeader() calls instead of TableHeadersRow()
7109 if (open_action != -1)
7110 ImGui::SetNextItemOpen(open_action != 0);
7111 if (ImGui::TreeNode("Custom headers"))
7112 {
7113 IMGUI_DEMO_MARKER("Tables/Custom headers");
7114 const int COLUMNS_COUNT = 3;
7115 if (ImGui::BeginTable("table_custom_headers", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
7116 {
7117 ImGui::TableSetupColumn("Apricot");
7118 ImGui::TableSetupColumn("Banana");
7119 ImGui::TableSetupColumn("Cherry");
7120
7121 // Dummy entire-column selection storage
7122 // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
7123 static bool column_selected[3] = {};
7124
7125 // Instead of calling TableHeadersRow() we'll submit custom headers ourselves.
7126 // (A different approach is also possible:
7127 // - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.
7128 // - Call TableHeadersRow() normally. This will submit TableHeader() with no name.
7129 // - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)
7130 ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
7131 for (int column = 0; column < COLUMNS_COUNT; column++)
7132 {
7133 ImGui::TableSetColumnIndex(column);
7134 const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
7135 ImGui::PushID(column);
7136 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
7137 ImGui::Checkbox("##checkall", &column_selected[column]);
7138 ImGui::PopStyleVar();
7139 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
7140 ImGui::TableHeader(column_name);
7141 ImGui::PopID();
7142 }
7143
7144 // Submit table contents
7145 for (int row = 0; row < 5; row++)
7146 {
7147 ImGui::TableNextRow();
7148 for (int column = 0; column < 3; column++)
7149 {
7150 char buf[32];
7151 sprintf(buf, "Cell %d,%d", column, row);
7152 ImGui::TableSetColumnIndex(column);
7153 ImGui::Selectable(buf, column_selected[column]);
7154 }
7155 }
7156 ImGui::EndTable();
7157 }
7158 ImGui::TreePop();
7159 }
7160
7161 // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers
7162 if (open_action != -1)
7163 ImGui::SetNextItemOpen(open_action != 0);
7164 if (ImGui::TreeNode("Angled headers"))
7165 {
7166 IMGUI_DEMO_MARKER("Tables/Angled headers");
7167 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" };
7168 const int columns_count = IM_COUNTOF(column_names);
7169 const int rows_count = 12;
7170
7171 static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;
7172 static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed;
7173 static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage
7174 static int frozen_cols = 1;
7175 static int frozen_rows = 2;
7176 ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
7177 ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
7178 ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable);
7179 ImGui::CheckboxFlags("_Sortable", &table_flags, ImGuiTableFlags_Sortable);
7180 ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody);
7181 ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn);
7182 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7183 ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2);
7184 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7185 ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2);
7186 ImGui::CheckboxFlags("Disable header contributing to column width", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth);
7187
7188 if (ImGui::TreeNode("Style settings"))
7189 {
7190 ImGui::SameLine();
7191 HelpMarker("Giving access to some ImGuiStyle value in this demo for convenience.");
7192 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7193 ImGui::SliderAngle("style.TableAngledHeadersAngle", &ImGui::GetStyle().TableAngledHeadersAngle, -50.0f, +50.0f);
7194 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7195 ImGui::SliderFloat2("style.TableAngledHeadersTextAlign", (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
7196 ImGui::TreePop();
7197 }
7198
7199 if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))
7200 {
7201 ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
7202 for (int n = 1; n < columns_count; n++)
7203 ImGui::TableSetupColumn(column_names[n], column_flags);
7204 ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);
7205
7206 ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.
7207 ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions.
7208 for (int row = 0; row < rows_count; row++)
7209 {
7210 ImGui::PushID(row);
7211 ImGui::TableNextRow();
7212 ImGui::TableSetColumnIndex(0);
7213 ImGui::AlignTextToFramePadding();
7214 ImGui::Text("Track %d", row);
7215 for (int column = 1; column < columns_count; column++)
7216 if (ImGui::TableSetColumnIndex(column))
7217 {
7218 ImGui::PushID(column);
7219 ImGui::Checkbox("", &bools[row * columns_count + column]);
7220 ImGui::PopID();
7221 }
7222 ImGui::PopID();
7223 }
7224 ImGui::EndTable();
7225 }
7226 ImGui::TreePop();
7227 }
7228
7229 // Demonstrate creating custom context menus inside columns,
7230 // while playing it nice with context menus provided by TableHeadersRow()/TableHeader()
7231 if (open_action != -1)
7232 ImGui::SetNextItemOpen(open_action != 0);
7233 if (ImGui::TreeNode("Context menus"))
7234 {
7235 IMGUI_DEMO_MARKER("Tables/Context menus");
7236 HelpMarker(
7237 "By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n"
7238 "Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.");
7239 static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody;
7240
7241 PushStyleCompact();
7242 ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags1, ImGuiTableFlags_ContextMenuInBody);
7243 PopStyleCompact();
7244
7245 // Context Menus: first example
7246 // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7247 // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)
7248 const int COLUMNS_COUNT = 3;
7249 if (ImGui::BeginTable("table_context_menu", COLUMNS_COUNT, flags1))
7250 {
7251 ImGui::TableSetupColumn("One");
7252 ImGui::TableSetupColumn("Two");
7253 ImGui::TableSetupColumn("Three");
7254
7255 // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.
7256 ImGui::TableHeadersRow();
7257
7258 // Submit dummy contents
7259 for (int row = 0; row < 4; row++)
7260 {
7261 ImGui::TableNextRow();
7262 for (int column = 0; column < COLUMNS_COUNT; column++)
7263 {
7264 ImGui::TableSetColumnIndex(column);
7265 ImGui::Text("Cell %d,%d", column, row);
7266 }
7267 }
7268 ImGui::EndTable();
7269 }
7270
7271 // Context Menus: second example
7272 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7273 // [2.2] Right-click on the ".." to open a custom popup
7274 // [2.3] Right-click in columns to open another custom popup
7275 HelpMarker(
7276 "Demonstrate mixing table context menu (over header), item context button (over button) "
7277 "and custom per-column context menu (over column body).");
7278 ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
7279 if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2))
7280 {
7281 ImGui::TableSetupColumn("One");
7282 ImGui::TableSetupColumn("Two");
7283 ImGui::TableSetupColumn("Three");
7284
7285 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7286 ImGui::TableHeadersRow();
7287 for (int row = 0; row < 4; row++)
7288 {
7289 ImGui::TableNextRow();
7290 for (int column = 0; column < COLUMNS_COUNT; column++)
7291 {
7292 // Submit dummy contents
7293 ImGui::TableSetColumnIndex(column);
7294 ImGui::Text("Cell %d,%d", column, row);
7295 ImGui::SameLine();
7296
7297 // [2.2] Right-click on the ".." to open a custom popup
7298 ImGui::PushID(row * COLUMNS_COUNT + column);
7299 ImGui::SmallButton("..");
7300 if (ImGui::BeginPopupContextItem())
7301 {
7302 ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row);
7303 if (ImGui::Button("Close"))
7304 ImGui::CloseCurrentPopup();
7305 ImGui::EndPopup();
7306 }
7307 ImGui::PopID();
7308 }
7309 }
7310
7311 // [2.3] Right-click anywhere in columns to open another custom popup
7312 // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup
7313 // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping)
7314 int hovered_column = -1;
7315 for (int column = 0; column < COLUMNS_COUNT + 1; column++)
7316 {
7317 ImGui::PushID(column);
7318 if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered)
7319 hovered_column = column;
7320 if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1))
7321 ImGui::OpenPopup("MyPopup");
7322 if (ImGui::BeginPopup("MyPopup"))
7323 {
7324 if (column == COLUMNS_COUNT)
7325 ImGui::Text("This is a custom popup for unused space after the last column.");
7326 else
7327 ImGui::Text("This is a custom popup for Column %d", column);
7328 if (ImGui::Button("Close"))
7329 ImGui::CloseCurrentPopup();
7330 ImGui::EndPopup();
7331 }
7332 ImGui::PopID();
7333 }
7334
7335 ImGui::EndTable();
7336 ImGui::Text("Hovered column: %d", hovered_column);
7337 }
7338 ImGui::TreePop();
7339 }
7340
7341 // Demonstrate creating multiple tables with the same ID
7342 if (open_action != -1)
7343 ImGui::SetNextItemOpen(open_action != 0);
7344 if (ImGui::TreeNode("Synced instances"))
7345 {
7346 IMGUI_DEMO_MARKER("Tables/Synced instances");
7347 HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc.");
7348
7349 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;
7350 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
7351 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
7352 ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit);
7353 ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
7354 for (int n = 0; n < 3; n++)
7355 {
7356 char buf[32];
7357 sprintf(buf, "Synced Table %d", n);
7358 bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen);
7359 if (open && ImGui::BeginTable("Table", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5)))
7360 {
7361 ImGui::TableSetupColumn("One");
7362 ImGui::TableSetupColumn("Two");
7363 ImGui::TableSetupColumn("Three");
7364 ImGui::TableHeadersRow();
7365 const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions.
7366 for (int cell = 0; cell < cell_count; cell++)
7367 {
7368 ImGui::TableNextColumn();
7369 ImGui::Text("this cell %d", cell);
7370 }
7371 ImGui::EndTable();
7372 }
7373 }
7374 ImGui::TreePop();
7375 }
7376
7377 // Demonstrate using Sorting facilities
7378 // This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting.
7379 // Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified)
7380 static const char* template_items_names[] =
7381 {
7382 "Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango",
7383 "Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot"
7384 };
7385 if (open_action != -1)
7386 ImGui::SetNextItemOpen(open_action != 0);
7387 if (ImGui::TreeNode("Sorting"))
7388 {
7389 IMGUI_DEMO_MARKER("Tables/Sorting");
7390 // Create item list
7391 static ImVector<MyItem> items;
7392 if (items.Size == 0)
7393 {
7394 items.resize(50, MyItem());
7395 for (int n = 0; n < items.Size; n++)
7396 {
7397 const int template_n = n % IM_COUNTOF(template_items_names);
7398 MyItem& item = items[n];
7399 item.ID = n;
7400 item.Name = template_items_names[template_n];
7401 item.Quantity = (n * n - n) % 20; // Assign default quantities
7402 }
7403 }
7404
7405 // Options
7406 static ImGuiTableFlags flags =
7407 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7408 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody
7409 | ImGuiTableFlags_ScrollY;
7410 PushStyleCompact();
7411 ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
7412 ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7413 ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
7414 ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7415 PopStyleCompact();
7416
7417 if (ImGui::BeginTable("table_sorting", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f))
7418 {
7419 // Declare columns
7420 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7421 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7422 // Demonstrate using a mixture of flags among available sort-related flags:
7423 // - ImGuiTableColumnFlags_DefaultSort
7424 // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending
7425 // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending
7426 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_ID);
7427 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
7428 ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);
7429 ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity);
7430 ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible
7431 ImGui::TableHeadersRow();
7432
7433 // Sort our data if sort specs have been changed!
7434 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
7435 if (sort_specs->SpecsDirty)
7436 {
7437 MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);
7438 sort_specs->SpecsDirty = false;
7439 }
7440
7441 // Demonstrate using clipper for large vertical lists
7442 ImGuiListClipper clipper;
7443 clipper.Begin(items.Size);
7444 while (clipper.Step())
7445 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7446 {
7447 // Display a data item
7448 MyItem* item = &items[row_n];
7449 ImGui::PushID(item->ID);
7450 ImGui::TableNextRow();
7451 ImGui::TableNextColumn();
7452 ImGui::Text("%04d", item->ID);
7453 ImGui::TableNextColumn();
7454 ImGui::TextUnformatted(item->Name);
7455 ImGui::TableNextColumn();
7456 ImGui::SmallButton("None");
7457 ImGui::TableNextColumn();
7458 ImGui::Text("%d", item->Quantity);
7459 ImGui::PopID();
7460 }
7461 ImGui::EndTable();
7462 }
7463 ImGui::TreePop();
7464 }
7465
7466 // In this example we'll expose most table flags and settings.
7467 // For specific flags and settings refer to the corresponding section for more detailed explanation.
7468 // This section is mostly useful to experiment with combining certain flags or settings with each others.
7469 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]
7470 if (open_action != -1)
7471 ImGui::SetNextItemOpen(open_action != 0);
7472 if (ImGui::TreeNode("Advanced"))
7473 {
7474 IMGUI_DEMO_MARKER("Tables/Advanced");
7475 static ImGuiTableFlags flags =
7476 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable
7477 | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7478 | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody
7479 | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
7480 | ImGuiTableFlags_SizingFixedFit;
7481 static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;
7482
7483 enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };
7484 static int contents_type = CT_SelectableSpanRow;
7485 const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" };
7486 static int freeze_cols = 1;
7487 static int freeze_rows = 1;
7488 static int items_count = IM_COUNTOF(template_items_names) * 2;
7489 static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);
7490 static float row_min_height = 0.0f; // Auto
7491 static float inner_width_with_scroll = 0.0f; // Auto-extend
7492 static bool outer_size_enabled = true;
7493 static bool show_headers = true;
7494 static bool show_wrapped_text = false;
7495 //static ImGuiTextFilter filter;
7496 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing
7497 if (ImGui::TreeNode("Options"))
7498 {
7499 // Make the UI compact because there are so many fields
7500 PushStyleCompact();
7501 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f);
7502
7503 if (ImGui::TreeNodeEx("Features:", ImGuiTreeNodeFlags_DefaultOpen))
7504 {
7505 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
7506 ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
7507 ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
7508 ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", &flags, ImGuiTableFlags_Sortable);
7509 ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", &flags, ImGuiTableFlags_NoSavedSettings);
7510 ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags, ImGuiTableFlags_ContextMenuInBody);
7511 ImGui::TreePop();
7512 }
7513
7514 if (ImGui::TreeNodeEx("Decorations:", ImGuiTreeNodeFlags_DefaultOpen))
7515 {
7516 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
7517 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
7518 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
7519 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
7520 ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
7521 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
7522 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
7523 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
7524 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)");
7525 ImGui::TreePop();
7526 }
7527
7528 if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen))
7529 {
7530 EditTableSizingFlags(&flags);
7531 ImGui::SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical.");
7532 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
7533 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.");
7534 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
7535 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.");
7536 ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible);
7537 ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled.");
7538 ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
7539 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.");
7540 ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
7541 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.");
7542 ImGui::TreePop();
7543 }
7544
7545 if (ImGui::TreeNodeEx("Padding:", ImGuiTreeNodeFlags_DefaultOpen))
7546 {
7547 ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX);
7548 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX);
7549 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX);
7550 ImGui::TreePop();
7551 }
7552
7553 if (ImGui::TreeNodeEx("Scrolling:", ImGuiTreeNodeFlags_DefaultOpen))
7554 {
7555 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
7556 ImGui::SameLine();
7557 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7558 ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
7559 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
7560 ImGui::SameLine();
7561 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7562 ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
7563 ImGui::TreePop();
7564 }
7565
7566 if (ImGui::TreeNodeEx("Sorting:", ImGuiTreeNodeFlags_DefaultOpen))
7567 {
7568 ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
7569 ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7570 ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
7571 ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7572 ImGui::TreePop();
7573 }
7574
7575 if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen))
7576 {
7577 ImGui::Checkbox("show_headers", &show_headers);
7578 ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
7579 ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader);
7580 ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo).");
7581 ImGui::TreePop();
7582 }
7583
7584 if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen))
7585 {
7586 ImGui::Checkbox("show_wrapped_text", &show_wrapped_text);
7587
7588 ImGui::DragFloat2("##OuterSize", &outer_size_value.x);
7589 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
7590 ImGui::Checkbox("outer_size", &outer_size_enabled);
7591 ImGui::SameLine();
7592 HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n"
7593 "- The table is output directly in the parent window.\n"
7594 "- OuterSize.x < 0.0f will right-align the table.\n"
7595 "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n"
7596 "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).");
7597
7598 // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.
7599 // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.
7600 ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);
7601
7602 ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX);
7603 ImGui::SameLine(); HelpMarker("Specify height of the Selectable item.");
7604
7605 ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999);
7606 ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_COUNTOF(contents_type_names));
7607 //filter.Draw("filter");
7608 ImGui::TreePop();
7609 }
7610
7611 ImGui::PopItemWidth();
7612 PopStyleCompact();
7613 ImGui::Spacing();
7614 ImGui::TreePop();
7615 }
7616
7617 // Update item list if we changed the number of items
7618 static ImVector<MyItem> items;
7619 static ImVector<int> selection;
7620 static bool items_need_sort = false;
7621 if (items.Size != items_count)
7622 {
7623 items.resize(items_count, MyItem());
7624 for (int n = 0; n < items_count; n++)
7625 {
7626 const int template_n = n % IM_COUNTOF(template_items_names);
7627 MyItem& item = items[n];
7628 item.ID = n;
7629 item.Name = template_items_names[template_n];
7630 item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities
7631 }
7632 }
7633
7634 const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();
7635 const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;
7636 ImVec2 table_scroll_cur, table_scroll_max; // For debug display
7637 const ImDrawList* table_draw_list = NULL; // "
7638
7639 // Submit table
7640 const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;
7641 if (ImGui::BeginTable("table_advanced", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use))
7642 {
7643 // Declare columns
7644 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7645 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7646 ImGui::TableSetupColumn("ID", columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID);
7647 ImGui::TableSetupColumn("Name", columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
7648 ImGui::TableSetupColumn("Action", columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);
7649 ImGui::TableSetupColumn("Quantity", columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity);
7650 ImGui::TableSetupColumn("Description", columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description);
7651 ImGui::TableSetupColumn("Hidden", columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);
7652 ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
7653
7654 // Sort our data if sort specs have been changed!
7655 ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();
7656 if (sort_specs && sort_specs->SpecsDirty)
7657 items_need_sort = true;
7658 if (sort_specs && items_need_sort && items.Size > 1)
7659 {
7660 MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);
7661 sort_specs->SpecsDirty = false;
7662 }
7663 items_need_sort = false;
7664
7665 // Take note of whether we are currently sorting based on the Quantity field,
7666 // we will use this to trigger sorting when we know the data of this column has been modified.
7667 const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0;
7668
7669 // Show headers
7670 if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)
7671 ImGui::TableAngledHeadersRow();
7672 if (show_headers)
7673 ImGui::TableHeadersRow();
7674
7675 // Show data
7676 // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?
7677#if 1
7678 // Demonstrate using clipper for large vertical lists
7679 ImGuiListClipper clipper;
7680 clipper.Begin(items.Size);
7681 while (clipper.Step())
7682 {
7683 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7684#else
7685 // Without clipper
7686 {
7687 for (int row_n = 0; row_n < items.Size; row_n++)
7688#endif
7689 {
7690 MyItem* item = &items[row_n];
7691 //if (!filter.PassFilter(item->Name))
7692 // continue;
7693
7694 const bool item_is_selected = selection.contains(item->ID);
7695 ImGui::PushID(item->ID);
7696 ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height);
7697
7698 // For the demo purpose we can select among different type of items submitted in the first column
7699 ImGui::TableSetColumnIndex(0);
7700 char label[32];
7701 sprintf(label, "%04d", item->ID);
7702 if (contents_type == CT_Text)
7703 ImGui::TextUnformatted(label);
7704 else if (contents_type == CT_Button)
7705 ImGui::Button(label);
7706 else if (contents_type == CT_SmallButton)
7707 ImGui::SmallButton(label);
7708 else if (contents_type == CT_FillButton)
7709 ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));
7710 else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)
7711 {
7712 ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None;
7713 if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height)))
7714 {
7715 if (ImGui::GetIO().KeyCtrl)
7716 {
7717 if (item_is_selected)
7718 selection.find_erase_unsorted(item->ID);
7719 else
7720 selection.push_back(item->ID);
7721 }
7722 else
7723 {
7724 selection.clear();
7725 selection.push_back(item->ID);
7726 }
7727 }
7728 }
7729
7730 if (ImGui::TableSetColumnIndex(1))
7731 ImGui::TextUnformatted(item->Name);
7732
7733 // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
7734 // and we are currently sorting on the column showing the Quantity.
7735 // To avoid triggering a sort while holding the button, we only trigger it when the button has been released.
7736 // You will probably need some extra logic if you want to automatically sort when a specific entry changes.
7737 if (ImGui::TableSetColumnIndex(2))
7738 {
7739 if (ImGui::SmallButton("Chop")) { item->Quantity += 1; }
7740 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7741 ImGui::SameLine();
7742 if (ImGui::SmallButton("Eat")) { item->Quantity -= 1; }
7743 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7744 }
7745
7746 if (ImGui::TableSetColumnIndex(3))
7747 ImGui::Text("%d", item->Quantity);
7748
7749 ImGui::TableSetColumnIndex(4);
7750 if (show_wrapped_text)
7751 ImGui::TextWrapped("Lorem ipsum dolor sit amet");
7752 else
7753 ImGui::Text("Lorem ipsum dolor sit amet");
7754
7755 if (ImGui::TableSetColumnIndex(5))
7756 ImGui::Text("1234");
7757
7758 ImGui::PopID();
7759 }
7760 }
7761
7762 // Store some info to display debug details below
7763 table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());
7764 table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());
7765 table_draw_list = ImGui::GetWindowDrawList();
7766 ImGui::EndTable();
7767 }
7768 static bool show_debug_details = false;
7769 ImGui::Checkbox("Debug details", &show_debug_details);
7770 if (show_debug_details && table_draw_list)
7771 {
7772 ImGui::SameLine(0.0f, 0.0f);
7773 const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;
7774 if (table_draw_list == parent_draw_list)
7775 ImGui::Text(": DrawCmd: +%d (in same window)",
7776 table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);
7777 else
7778 ImGui::Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)",
7779 table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y);
7780 }
7781 ImGui::TreePop();
7782 }
7783
7784 ImGui::PopID();
7785
7786 DemoWindowColumns();
7787
7788 if (disable_indent)
7789 ImGui::PopStyleVar();
7790}
7791
7792// Demonstrate old/legacy Columns API!
7793// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]
7794static void DemoWindowColumns()
7795{
7796 bool open = ImGui::TreeNode("Legacy Columns API");
7797 ImGui::SameLine();
7798 HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!");
7799 if (!open)
7800 return;
7801
7802 // Basic columns
7803 if (ImGui::TreeNode("Basic"))
7804 {
7805 IMGUI_DEMO_MARKER("Columns (legacy API)/Basic");
7806 ImGui::Text("Without border:");
7807 ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border
7808 ImGui::Separator();
7809 for (int n = 0; n < 14; n++)
7810 {
7811 char label[32];
7812 sprintf(label, "Item %d", n);
7813 if (ImGui::Selectable(label)) {}
7814 //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}
7815 ImGui::NextColumn();
7816 }
7817 ImGui::Columns(1);
7818 ImGui::Separator();
7819
7820 ImGui::Text("With border:");
7821 ImGui::Columns(4, "mycolumns"); // 4-ways, with border
7822 ImGui::Separator();
7823 ImGui::Text("ID"); ImGui::NextColumn();
7824 ImGui::Text("Name"); ImGui::NextColumn();
7825 ImGui::Text("Path"); ImGui::NextColumn();
7826 ImGui::Text("Hovered"); ImGui::NextColumn();
7827 ImGui::Separator();
7828 const char* names[3] = { "One", "Two", "Three" };
7829 const char* paths[3] = { "/path/one", "/path/two", "/path/three" };
7830 static int selected = -1;
7831 for (int i = 0; i < 3; i++)
7832 {
7833 char label[32];
7834 sprintf(label, "%04d", i);
7835 if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))
7836 selected = i;
7837 bool hovered = ImGui::IsItemHovered();
7838 ImGui::NextColumn();
7839 ImGui::Text(names[i]); ImGui::NextColumn();
7840 ImGui::Text(paths[i]); ImGui::NextColumn();
7841 ImGui::Text("%d", hovered); ImGui::NextColumn();
7842 }
7843 ImGui::Columns(1);
7844 ImGui::Separator();
7845 ImGui::TreePop();
7846 }
7847
7848 if (ImGui::TreeNode("Borders"))
7849 {
7850 IMGUI_DEMO_MARKER("Columns (legacy API)/Borders");
7851 // NB: Future columns API should allow automatic horizontal borders.
7852 static bool h_borders = true;
7853 static bool v_borders = true;
7854 static int columns_count = 4;
7855 const int lines_count = 3;
7856 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7857 ImGui::DragInt("##columns_count", &columns_count, 0.1f, 2, 10, "%d columns");
7858 if (columns_count < 2)
7859 columns_count = 2;
7860 ImGui::SameLine();
7861 ImGui::Checkbox("horizontal", &h_borders);
7862 ImGui::SameLine();
7863 ImGui::Checkbox("vertical", &v_borders);
7864 ImGui::Columns(columns_count, NULL, v_borders);
7865 for (int i = 0; i < columns_count * lines_count; i++)
7866 {
7867 if (h_borders && ImGui::GetColumnIndex() == 0)
7868 ImGui::Separator();
7869 ImGui::PushID(i);
7870 ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i);
7871 ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
7872 ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
7873 ImGui::Text("Offset %.2f", ImGui::GetColumnOffset());
7874 ImGui::Text("Long text that is likely to clip");
7875 ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f));
7876 ImGui::PopID();
7877 ImGui::NextColumn();
7878 }
7879 ImGui::Columns(1);
7880 if (h_borders)
7881 ImGui::Separator();
7882 ImGui::TreePop();
7883 }
7884
7885 // Create multiple items in a same cell before switching to next column
7886 if (ImGui::TreeNode("Mixed items"))
7887 {
7888 IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items");
7889 ImGui::Columns(3, "mixed");
7890 ImGui::Separator();
7891
7892 ImGui::Text("Hello");
7893 ImGui::Button("Banana");
7894 ImGui::NextColumn();
7895
7896 ImGui::Text("ImGui");
7897 ImGui::Button("Apple");
7898 static float foo = 1.0f;
7899 ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f");
7900 ImGui::Text("An extra line here.");
7901 ImGui::NextColumn();
7902
7903 ImGui::Text("Sailor");
7904 ImGui::Button("Corniflower");
7905 static float bar = 1.0f;
7906 ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f");
7907 ImGui::NextColumn();
7908
7909 if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7910 if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7911 if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7912 ImGui::Columns(1);
7913 ImGui::Separator();
7914 ImGui::TreePop();
7915 }
7916
7917 // Word wrapping
7918 if (ImGui::TreeNode("Word-wrapping"))
7919 {
7920 IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping");
7921 ImGui::Columns(2, "word-wrapping");
7922 ImGui::Separator();
7923 ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
7924 ImGui::TextWrapped("Hello Left");
7925 ImGui::NextColumn();
7926 ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
7927 ImGui::TextWrapped("Hello Right");
7928 ImGui::Columns(1);
7929 ImGui::Separator();
7930 ImGui::TreePop();
7931 }
7932
7933 if (ImGui::TreeNode("Horizontal Scrolling"))
7934 {
7935 IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling");
7936 ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));
7937 ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);
7938 ImGui::BeginChild("##ScrollingRegion", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar);
7939 ImGui::Columns(10);
7940
7941 // Also demonstrate using clipper for large vertical lists
7942 int ITEMS_COUNT = 2000;
7943 ImGuiListClipper clipper;
7944 clipper.Begin(ITEMS_COUNT);
7945 while (clipper.Step())
7946 {
7947 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
7948 for (int j = 0; j < 10; j++)
7949 {
7950 ImGui::Text("Line %d Column %d...", i, j);
7951 ImGui::NextColumn();
7952 }
7953 }
7954 ImGui::Columns(1);
7955 ImGui::EndChild();
7956 ImGui::TreePop();
7957 }
7958
7959 if (ImGui::TreeNode("Tree"))
7960 {
7961 IMGUI_DEMO_MARKER("Columns (legacy API)/Tree");
7962 ImGui::Columns(2, "tree", true);
7963 for (int x = 0; x < 3; x++)
7964 {
7965 bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x);
7966 ImGui::NextColumn();
7967 ImGui::Text("Node contents");
7968 ImGui::NextColumn();
7969 if (open1)
7970 {
7971 for (int y = 0; y < 3; y++)
7972 {
7973 bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y);
7974 ImGui::NextColumn();
7975 ImGui::Text("Node contents");
7976 if (open2)
7977 {
7978 ImGui::Text("Even more contents");
7979 if (ImGui::TreeNode("Tree in column"))
7980 {
7981 ImGui::Text("The quick brown fox jumps over the lazy dog");
7982 ImGui::TreePop();
7983 }
7984 }
7985 ImGui::NextColumn();
7986 if (open2)
7987 ImGui::TreePop();
7988 }
7989 ImGui::TreePop();
7990 }
7991 }
7992 ImGui::Columns(1);
7993 ImGui::TreePop();
7994 }
7995
7996 ImGui::TreePop();
7997}
7998
7999//-----------------------------------------------------------------------------
8000// [SECTION] DemoWindowInputs()
8001//-----------------------------------------------------------------------------
8002
8003static void DemoWindowInputs()
8004{
8005 if (ImGui::CollapsingHeader("Inputs & Focus"))
8006 {
8007 ImGuiIO& io = ImGui::GetIO();
8008
8009 // Display inputs submitted to ImGuiIO
8010 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
8011 bool inputs_opened = ImGui::TreeNode("Inputs");
8012 ImGui::SameLine();
8013 HelpMarker(
8014 "This is a simplified view. See more detailed input state:\n"
8015 "- in 'Tools->Metrics/Debugger->Inputs'.\n"
8016 "- in 'Tools->Debug Log->IO'.");
8017 if (inputs_opened)
8018 {
8019 IMGUI_DEMO_MARKER("Inputs & Focus/Inputs");
8020 if (ImGui::IsMousePosValid())
8021 ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
8022 else
8023 ImGui::Text("Mouse pos: <INVALID>");
8024 ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
8025 ImGui::Text("Mouse down:");
8026 for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
8027 ImGui::Text("Mouse wheel: %.1f", io.MouseWheel);
8028 ImGui::Text("Mouse clicked count:");
8029 for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); }
8030
8031 // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows
8032 // displaying the data for old/new backends.
8033 // User code should never have to go through such hoops!
8034 // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
8035 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
8036 ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;
8037 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); }
8038 ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
8039 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.
8040
8041 ImGui::TreePop();
8042 }
8043
8044 // Display ImGuiIO output flags
8045 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
8046 bool outputs_opened = ImGui::TreeNode("Outputs");
8047 ImGui::SameLine();
8048 HelpMarker(
8049 "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui "
8050 "to instruct your application of how to route inputs. Typically, when a value is true, it means "
8051 "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n"
8052 "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, "
8053 "and underlying application should ignore mouse inputs (in practice there are many and more subtle "
8054 "rules leading to how those flags are set).");
8055 if (outputs_opened)
8056 {
8057 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs");
8058 ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse);
8059 ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
8060 ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
8061 ImGui::Text("io.WantTextInput: %d", io.WantTextInput);
8062 ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos);
8063 ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible);
8064
8065 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override");
8066 if (ImGui::TreeNode("WantCapture override"))
8067 {
8068 HelpMarker(
8069 "Hovering the colored canvas will override io.WantCaptureXXX fields.\n"
8070 "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering "
8071 "and true when clicking.");
8072 static int capture_override_mouse = -1;
8073 static int capture_override_keyboard = -1;
8074 const char* capture_override_desc[] = { "None", "Set to false", "Set to true" };
8075 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
8076 ImGui::SliderInt("SetNextFrameWantCaptureMouse() on hover", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp);
8077 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
8078 ImGui::SliderInt("SetNextFrameWantCaptureKeyboard() on hover", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp);
8079
8080 ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item
8081 if (ImGui::IsItemHovered() && capture_override_mouse != -1)
8082 ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1);
8083 if (ImGui::IsItemHovered() && capture_override_keyboard != -1)
8084 ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1);
8085
8086 ImGui::TreePop();
8087 }
8088 ImGui::TreePop();
8089 }
8090
8091 // Demonstrate using Shortcut() and Routing Policies.
8092 // The general flow is:
8093 // - Code interested in a chord (e.g. "Ctrl+A") declares their intent.
8094 // - Multiple locations may be interested in same chord! Routing helps find a winner.
8095 // - Every frame, we resolve all claims and assign one owner if the modifiers are matching.
8096 // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route.
8097 // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags.
8098 // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused.
8099 // TL;DR: Most uses will simply be:
8100 // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy.
8101 if (ImGui::TreeNode("Shortcuts"))
8102 {
8103 IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts");
8104 static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat;
8105 static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused;
8106 ImGui::CheckboxFlags("ImGuiInputFlags_Repeat", &route_options, ImGuiInputFlags_Repeat);
8107 ImGui::RadioButton("ImGuiInputFlags_RouteActive", &route_type, ImGuiInputFlags_RouteActive);
8108 ImGui::RadioButton("ImGuiInputFlags_RouteFocused (default)", &route_type, ImGuiInputFlags_RouteFocused);
8109 ImGui::Indent();
8110 ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteFocused);
8111 ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive##0", &route_options, ImGuiInputFlags_RouteOverActive);
8112 ImGui::EndDisabled();
8113 ImGui::Unindent();
8114 ImGui::RadioButton("ImGuiInputFlags_RouteGlobal", &route_type, ImGuiInputFlags_RouteGlobal);
8115 ImGui::Indent();
8116 ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal);
8117 ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverFocused", &route_options, ImGuiInputFlags_RouteOverFocused);
8118 ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive", &route_options, ImGuiInputFlags_RouteOverActive);
8119 ImGui::CheckboxFlags("ImGuiInputFlags_RouteUnlessBgFocused", &route_options, ImGuiInputFlags_RouteUnlessBgFocused);
8120 ImGui::EndDisabled();
8121 ImGui::Unindent();
8122 ImGui::RadioButton("ImGuiInputFlags_RouteAlways", &route_type, ImGuiInputFlags_RouteAlways);
8123 ImGuiInputFlags flags = route_type | route_options; // Merged flags
8124 if (route_type != ImGuiInputFlags_RouteGlobal)
8125 flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused);
8126
8127 ImGui::SeparatorText("Using SetNextItemShortcut()");
8128 ImGui::Text("Ctrl+S");
8129 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, flags | ImGuiInputFlags_Tooltip);
8130 ImGui::Button("Save");
8131 ImGui::Text("Alt+F");
8132 ImGui::SetNextItemShortcut(ImGuiMod_Alt | ImGuiKey_F, flags | ImGuiInputFlags_Tooltip);
8133 static float f = 0.5f;
8134 ImGui::SliderFloat("Factor", &f, 0.0f, 1.0f);
8135
8136 ImGui::SeparatorText("Using Shortcut()");
8137 const float line_height = ImGui::GetTextLineHeightWithSpacing();
8138 const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A;
8139
8140 ImGui::Text("Ctrl+A");
8141 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
8142
8143 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f));
8144
8145 ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true);
8146 ImGui::Text("Press Ctrl+A and see who receives it!");
8147 ImGui::Separator();
8148
8149 // 1: Window polling for Ctrl+A
8150 ImGui::Text("(in WindowA)");
8151 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
8152
8153 // 2: InputText also polling for Ctrl+A: it always uses _RouteFocused internally (gets priority when active)
8154 // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
8155 //char str[16] = "Press Ctrl+A";
8156 //ImGui::Spacing();
8157 //ImGui::InputText("InputTextB", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly);
8158 //ImGuiID item_id = ImGui::GetItemID();
8159 //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused");
8160 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "...");
8161
8162 // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA
8163 ImGui::BeginChild("ChildD", ImVec2(-FLT_MIN, line_height * 4), true);
8164 ImGui::Text("(in ChildD: not using same Shortcut)");
8165 ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused());
8166 ImGui::EndChild();
8167
8168 // 4: Child window polling for Ctrl+A. It is deeper than WindowA and gets priority when focused.
8169 ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true);
8170 ImGui::Text("(in ChildE: using same Shortcut)");
8171 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
8172 ImGui::EndChild();
8173
8174 // 5: In a popup
8175 if (ImGui::Button("Open Popup"))
8176 ImGui::OpenPopup("PopupF");
8177 if (ImGui::BeginPopup("PopupF"))
8178 {
8179 ImGui::Text("(in PopupF)");
8180 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
8181 // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
8182 //ImGui::InputText("InputTextG", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly);
8183 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "...");
8184 ImGui::EndPopup();
8185 }
8186 ImGui::EndChild();
8187 ImGui::PopStyleColor();
8188
8189 ImGui::TreePop();
8190 }
8191
8192 // Display mouse cursors
8193 if (ImGui::TreeNode("Mouse Cursors"))
8194 {
8195 IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors");
8196 const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" };
8197 IM_ASSERT(IM_COUNTOF(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
8198
8199 ImGuiMouseCursor current = ImGui::GetMouseCursor();
8200 const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A";
8201 ImGui::Text("Current mouse cursor = %d: %s", current, cursor_name);
8202 ImGui::BeginDisabled(true);
8203 ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
8204 ImGui::EndDisabled();
8205
8206 ImGui::Text("Hover to see mouse cursors:");
8207 ImGui::SameLine(); HelpMarker(
8208 "Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. "
8209 "If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, "
8210 "otherwise your backend needs to handle it.");
8211 for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)
8212 {
8213 char label[32];
8214 sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]);
8215 ImGui::Bullet(); ImGui::Selectable(label, false);
8216 if (ImGui::IsItemHovered())
8217 ImGui::SetMouseCursor(i);
8218 }
8219 ImGui::TreePop();
8220 }
8221
8222 if (ImGui::TreeNode("Tabbing"))
8223 {
8224 IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing");
8225 ImGui::Text("Use Tab/Shift+Tab to cycle through keyboard editable fields.");
8226 static char buf[32] = "hello";
8227 ImGui::InputText("1", buf, IM_COUNTOF(buf));
8228 ImGui::InputText("2", buf, IM_COUNTOF(buf));
8229 ImGui::InputText("3", buf, IM_COUNTOF(buf));
8230 ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
8231 ImGui::InputText("4 (tab skip)", buf, IM_COUNTOF(buf));
8232 ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
8233 ImGui::PopItemFlag();
8234 ImGui::InputText("5", buf, IM_COUNTOF(buf));
8235 ImGui::TreePop();
8236 }
8237
8238 if (ImGui::TreeNode("Focus from code"))
8239 {
8240 IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code");
8241 bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine();
8242 bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine();
8243 bool focus_3 = ImGui::Button("Focus on 3");
8244 int has_focus = 0;
8245 static char buf[128] = "click on a button to set focus";
8246
8247 if (focus_1) ImGui::SetKeyboardFocusHere();
8248 ImGui::InputText("1", buf, IM_COUNTOF(buf));
8249 if (ImGui::IsItemActive()) has_focus = 1;
8250
8251 if (focus_2) ImGui::SetKeyboardFocusHere();
8252 ImGui::InputText("2", buf, IM_COUNTOF(buf));
8253 if (ImGui::IsItemActive()) has_focus = 2;
8254
8255 ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
8256 if (focus_3) ImGui::SetKeyboardFocusHere();
8257 ImGui::InputText("3 (tab skip)", buf, IM_COUNTOF(buf));
8258 if (ImGui::IsItemActive()) has_focus = 3;
8259 ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
8260 ImGui::PopItemFlag();
8261
8262 if (has_focus)
8263 ImGui::Text("Item with focus: %d", has_focus);
8264 else
8265 ImGui::Text("Item with focus: <none>");
8266
8267 // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item
8268 static float f3[3] = { 0.0f, 0.0f, 0.0f };
8269 int focus_ahead = -1;
8270 if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine();
8271 if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine();
8272 if (ImGui::Button("Focus on Z")) { focus_ahead = 2; }
8273 if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead);
8274 ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f);
8275
8276 ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code.");
8277 ImGui::TreePop();
8278 }
8279
8280 if (ImGui::TreeNode("Dragging"))
8281 {
8282 IMGUI_DEMO_MARKER("Inputs & Focus/Dragging");
8283 ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");
8284 for (int button = 0; button < 3; button++)
8285 {
8286 ImGui::Text("IsMouseDragging(%d):", button);
8287 ImGui::Text(" w/ default threshold: %d,", ImGui::IsMouseDragging(button));
8288 ImGui::Text(" w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f));
8289 ImGui::Text(" w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f));
8290 }
8291
8292 ImGui::Button("Drag Me");
8293 if (ImGui::IsItemActive())
8294 ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor
8295
8296 // Drag operations gets "unlocked" when the mouse has moved past a certain threshold
8297 // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
8298 // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
8299 ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);
8300 ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0);
8301 ImVec2 mouse_delta = io.MouseDelta;
8302 ImGui::Text("GetMouseDragDelta(0):");
8303 ImGui::Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y);
8304 ImGui::Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y);
8305 ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y);
8306 ImGui::TreePop();
8307 }
8308 }
8309}
8310
8311//-----------------------------------------------------------------------------
8312// [SECTION] About Window / ShowAboutWindow()
8313// Access from Dear ImGui Demo -> Tools -> About
8314//-----------------------------------------------------------------------------
8315
8316void ImGui::ShowAboutWindow(bool* p_open)
8317{
8318 if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize))
8319 {
8320 ImGui::End();
8321 return;
8322 }
8323 IMGUI_DEMO_MARKER("Tools/About Dear ImGui");
8324 ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8325
8326 ImGui::TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui");
8327 ImGui::SameLine();
8328 ImGui::TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md");
8329 ImGui::SameLine();
8330 ImGui::TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki");
8331 ImGui::SameLine();
8332 ImGui::TextLinkOpenURL("Extensions", "https://github.com/ocornut/imgui/wiki/Useful-Extensions");
8333 ImGui::SameLine();
8334 ImGui::TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases");
8335 ImGui::SameLine();
8336 ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding");
8337
8338 ImGui::Separator();
8339 ImGui::Text("(c) 2014-2026 Omar Cornut");
8340 ImGui::Text("Developed by Omar Cornut and all Dear ImGui contributors.");
8341 ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information.");
8342 ImGui::Text("If your company uses this, please consider funding the project.");
8343
8344 static bool show_config_info = false;
8345 ImGui::Checkbox("Config/Build Information", &show_config_info);
8346 if (show_config_info)
8347 {
8348 ImGuiIO& io = ImGui::GetIO();
8349 ImGuiStyle& style = ImGui::GetStyle();
8350
8351 bool copy_to_clipboard = ImGui::Button("Copy to clipboard");
8352 ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);
8353 ImGui::BeginChild(ImGui::GetID("cfg_infos"), child_size, ImGuiChildFlags_FrameStyle);
8354 if (copy_to_clipboard)
8355 {
8356 ImGui::LogToClipboard();
8357 ImGui::LogText("// (Copy from the next line. Keep the ``` markers for formatting.)\n");
8358 ImGui::LogText("```cpp\n"); // Back quotes will make text appears without formatting when pasting on GitHub
8359 }
8360
8361 ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8362 ImGui::Separator();
8363 ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));
8364 ImGui::Text("define: __cplusplus=%d", (int)__cplusplus);
8365#ifdef IMGUI_ENABLE_TEST_ENGINE
8366 ImGui::Text("define: IMGUI_ENABLE_TEST_ENGINE");
8367#endif
8368#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8369 ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
8370#endif
8371#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
8372 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
8373#endif
8374#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
8375 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS");
8376#endif
8377#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS
8378 ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS");
8379#endif
8380#ifdef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
8381 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS");
8382#endif
8383#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
8384 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS");
8385#endif
8386#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
8387 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS");
8388#endif
8389#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
8390 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS");
8391#endif
8392#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
8393 ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS");
8394#endif
8395#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS
8396 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS");
8397#endif
8398#ifdef IMGUI_USE_BGRA_PACKED_COLOR
8399 ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR");
8400#endif
8401#ifdef _WIN32
8402 ImGui::Text("define: _WIN32");
8403#endif
8404#ifdef _WIN64
8405 ImGui::Text("define: _WIN64");
8406#endif
8407#ifdef __linux__
8408 ImGui::Text("define: __linux__");
8409#endif
8410#ifdef __APPLE__
8411 ImGui::Text("define: __APPLE__");
8412#endif
8413#ifdef _MSC_VER
8414 ImGui::Text("define: _MSC_VER=%d", _MSC_VER);
8415#endif
8416#ifdef _MSVC_LANG
8417 ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG);
8418#endif
8419#ifdef __MINGW32__
8420 ImGui::Text("define: __MINGW32__");
8421#endif
8422#ifdef __MINGW64__
8423 ImGui::Text("define: __MINGW64__");
8424#endif
8425#ifdef __GNUC__
8426 ImGui::Text("define: __GNUC__=%d", (int)__GNUC__);
8427#endif
8428#ifdef __clang_version__
8429 ImGui::Text("define: __clang_version__=%s", __clang_version__);
8430#endif
8431#ifdef __EMSCRIPTEN__
8432 ImGui::Text("define: __EMSCRIPTEN__");
8433#ifdef __EMSCRIPTEN_MAJOR__
8434 ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_MAJOR__, __EMSCRIPTEN_MINOR__, __EMSCRIPTEN_TINY__);
8435#else
8436 ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
8437#endif
8438#endif
8439#ifdef IMGUI_HAS_VIEWPORT
8440 ImGui::Text("define: IMGUI_HAS_VIEWPORT");
8441#endif
8442#ifdef IMGUI_HAS_DOCK
8443 ImGui::Text("define: IMGUI_HAS_DOCK");
8444#endif
8445#ifdef NDEBUG
8446 ImGui::Text("define: NDEBUG");
8447#endif
8448
8449 // Heuristic to detect no-op IM_ASSERT() macros
8450 // - This is designed so people opening bug reports would convey and notice that they have disabled asserts for Dear ImGui code.
8451 // - 16 is > strlen("((void)(_EXPR))") which we suggested in our imconfig.h template as a possible way to disable.
8452 int assert_runs_expression = 0;
8453 IM_ASSERT(++assert_runs_expression);
8454 int assert_expand_len = (int)strlen(IM_STRINGIFY((IM_ASSERT(true))));
8455 bool assert_maybe_disabled = (!assert_runs_expression || assert_expand_len <= 16);
8456 ImGui::Text("IM_ASSERT: runs expression: %s. expand size: %s%s",
8457 assert_runs_expression ? "OK" : "KO", (assert_expand_len > 16) ? "OK" : "KO", assert_maybe_disabled ? " (MAYBE DISABLED?!)" : "");
8458 if (assert_maybe_disabled)
8459 {
8460 ImGui::SameLine();
8461 HelpMarker("IM_ASSERT() calls assert() by default. Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes!");
8462 }
8463
8464 ImGui::Separator();
8465 ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
8466 ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
8467 ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags);
8468 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard");
8469 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad");
8470 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse");
8471 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange");
8472 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) ImGui::Text(" NoKeyboard");
8473 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) ImGui::Text(" DockingEnable");
8474 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(" ViewportsEnable");
8475 if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor");
8476 if (io.ConfigDpiScaleFonts) ImGui::Text("io.ConfigDpiScaleFonts");
8477 if (io.ConfigDpiScaleViewports) ImGui::Text("io.ConfigDpiScaleViewports");
8478 if (io.ConfigViewportsNoAutoMerge) ImGui::Text("io.ConfigViewportsNoAutoMerge");
8479 if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text("io.ConfigViewportsNoTaskBarIcon");
8480 if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration");
8481 if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent");
8482 if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit");
8483 if (io.ConfigDockingNoDockingOver) ImGui::Text("io.ConfigDockingNoDockingOver");
8484 if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift");
8485 if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar");
8486 if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload");
8487 if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors");
8488 if (io.ConfigNavMoveSetMousePos) ImGui::Text("io.ConfigNavMoveSetMousePos");
8489 if (io.ConfigNavCaptureKeyboard) ImGui::Text("io.ConfigNavCaptureKeyboard");
8490 if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink");
8491 if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges");
8492 if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly");
8493 if (io.ConfigMemoryCompactTimer >= 0.0f) ImGui::Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer);
8494 ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags);
8495 if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad");
8496 if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors");
8497 if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos");
8498 if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) ImGui::Text(" PlatformHasViewports");
8499 if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(" HasMouseHoveredViewport");
8500 if (io.BackendFlags & ImGuiBackendFlags_HasParentViewport) ImGui::Text(" HasParentViewport");
8501 if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset");
8502 if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) ImGui::Text(" RendererHasTextures");
8503 if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports) ImGui::Text(" RendererHasViewports");
8504 ImGui::Separator();
8505 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);
8506 ImGui::Text("io.Fonts->FontLoaderName: %s", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL");
8507 ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
8508 ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
8509 ImGui::Separator();
8510 ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y);
8511 ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize);
8512 ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y);
8513 ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding);
8514 ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize);
8515 ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y);
8516 ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);
8517
8518 if (copy_to_clipboard)
8519 {
8520 ImGui::LogText("\n```\n");
8521 ImGui::LogFinish();
8522 }
8523 ImGui::EndChild();
8524 }
8525 ImGui::End();
8526}
8527
8528//-----------------------------------------------------------------------------
8529// [SECTION] Style Editor / ShowStyleEditor()
8530//-----------------------------------------------------------------------------
8531// - ShowStyleSelector()
8532// - ShowStyleEditor()
8533//-----------------------------------------------------------------------------
8534
8535// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
8536bool ImGui::ShowStyleSelector(const char* label)
8537{
8538 // FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active.
8539 // So we keep track of last active one among our limited selection.
8540 static int style_idx = -1;
8541 const char* style_names[] = { "Dark", "Light", "Classic" };
8542 bool ret = false;
8543 if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_COUNTOF(style_names)) ? style_names[style_idx] : ""))
8544 {
8545 for (int n = 0; n < IM_COUNTOF(style_names); n++)
8546 {
8547 if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav))
8548 {
8549 style_idx = n;
8550 ret = true;
8551 switch (style_idx)
8552 {
8553 case 0: ImGui::StyleColorsDark(); break;
8554 case 1: ImGui::StyleColorsLight(); break;
8555 case 2: ImGui::StyleColorsClassic(); break;
8556 }
8557 }
8558 else if (style_idx == n)
8559 ImGui::SetItemDefaultFocus();
8560 }
8561 ImGui::EndCombo();
8562 }
8563 return ret;
8564}
8565
8566static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
8567{
8568 if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return "DrawLinesNone";
8569 if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return "DrawLinesFull";
8570 if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return "DrawLinesToNodes";
8571 return "";
8572}
8573
8574// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8575void ImGui::ShowStyleEditor(ImGuiStyle* ref)
8576{
8577 IMGUI_DEMO_MARKER("Tools/Style Editor");
8578 // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
8579 // (without a reference style pointer, we will use one compared locally as a reference)
8580 ImGuiStyle& style = GetStyle();
8581 static ImGuiStyle ref_saved_style;
8582
8583 // Default to using internal storage as reference
8584 static bool init = true;
8585 if (init && ref == NULL)
8586 ref_saved_style = style;
8587 init = false;
8588 if (ref == NULL)
8589 ref = &ref_saved_style;
8590
8591 // The logic behind dynamically changing 'max_border_size' is to not encourage people to increase border size too much: it'll likely reveal lots of subtle rendering artifacts and this isn't a priority right now.
8592 // Note that _MainScale is currently internal PLEASE DO NOT USE IN YOUR CODE.
8593 const float default_border_size = (float)(int)style._MainScale;
8594 const float max_border_size = IM_MAX(default_border_size, 2.0f);
8595
8596 PushItemWidth(GetWindowWidth() * 0.50f);
8597
8598 {
8599 // General
8600 SeparatorText("General");
8601 if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
8602 {
8603 BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
8604 BulletText("For instructions, see:");
8605 SameLine();
8606 TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md");
8607 }
8608
8609 if (ShowStyleSelector("Colors##Selector"))
8610 ref_saved_style = style;
8611 ShowFontSelector("Fonts##Selector");
8612 if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f"))
8613 style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
8614 SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize());
8615 DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);
8616 BeginDisabled(GetIO().ConfigDpiScaleFonts);
8617 DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f);
8618 SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
8619 EndDisabled();
8620
8621 // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
8622 if (SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
8623 style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
8624 { bool border = (style.WindowBorderSize > 0.0f); if (Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? default_border_size : 0.0f; } }
8625 SameLine();
8626 { bool border = (style.FrameBorderSize > 0.0f); if (Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? default_border_size : 0.0f; } }
8627 SameLine();
8628 { bool border = (style.PopupBorderSize > 0.0f); if (Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? default_border_size : 0.0f; } }
8629 }
8630
8631 // Save/Revert button
8632 if (Button("Save Ref"))
8633 *ref = ref_saved_style = style;
8634 SameLine();
8635 if (Button("Revert Ref"))
8636 style = *ref;
8637 SameLine();
8638 HelpMarker(
8639 "Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
8640 "Use \"Export\" below to save them somewhere.");
8641
8642 SeparatorText("Details");
8643 if (BeginTabBar("##tabs", ImGuiTabBarFlags_None))
8644 {
8645 if (BeginTabItem("Sizes"))
8646 {
8647 SeparatorText("Main");
8648 SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
8649 SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
8650 SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
8651 SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
8652 SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
8653 SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
8654 SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
8655
8656 SeparatorText("Borders");
8657 SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, max_border_size, "%.0f");
8658 SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, max_border_size, "%.0f");
8659 SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, max_border_size, "%.0f");
8660 SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, max_border_size, "%.0f");
8661
8662 SeparatorText("Rounding");
8663 SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
8664 SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
8665 SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
8666 SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
8667 SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
8668 SliderFloat("MenuItemRounding", &style.MenuItemRounding, 0.0f, 12.0f, "%.0f");
8669 // NB: SelectableRounding is intentionally NOT made visible here. We don't want to encourage people using that.
8670
8671 SeparatorText("Scrollbar");
8672 SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
8673 SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
8674 SliderFloat("ScrollbarPadding", &style.ScrollbarPadding, 0.0f, 10.0f, "%.0f");
8675
8676 SeparatorText("Tabs");
8677 SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, max_border_size, "%.0f");
8678 SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, max_border_size, "%.0f");
8679 SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, IM_MAX(3.0f, max_border_size), "%.0f");
8680 SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
8681 DragFloat("TabMinWidthBase", &style.TabMinWidthBase, 0.5f, 1.0f, 500.0f, "%.0f");
8682 DragFloat("TabMinWidthShrink", &style.TabMinWidthShrink, 0.5f, 1.0f, 500.0f, "%0.f");
8683 DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
8684 DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
8685 SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
8686
8687 SeparatorText("Tables");
8688 SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
8689 SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f);
8690 SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
8691
8692 SeparatorText("Trees");
8693 bool combo_open = BeginCombo("TreeLinesFlags", GetTreeLinesFlagsName(style.TreeLinesFlags));
8694 SameLine();
8695 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.");
8696 if (combo_open)
8697 {
8698 const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes };
8699 for (ImGuiTreeNodeFlags option : options)
8700 if (Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option))
8701 style.TreeLinesFlags = option;
8702 EndCombo();
8703 }
8704 SliderFloat("TreeLinesSize", &style.TreeLinesSize, 0.0f, max_border_size, "%.0f");
8705 SliderFloat("TreeLinesRounding", &style.TreeLinesRounding, 0.0f, 12.0f, "%.0f");
8706
8707 SeparatorText("Windows");
8708 SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
8709 SliderFloat("WindowBorderHoverPadding", &style.WindowBorderHoverPadding, 1.0f, 20.0f, "%.0f");
8710 int window_menu_button_position = style.WindowMenuButtonPosition + 1;
8711 if (Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0"))
8712 style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);
8713
8714 SeparatorText("Widgets");
8715 SliderFloat("ColorMarkerSize", &style.ColorMarkerSize, 0.0f, 8.0f, "%.0f");
8716 Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
8717 SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
8718 SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
8719 SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
8720 SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.");
8721 SliderFloat("SeparatorSize", &style.SeparatorSize, 0.0f, 10.0f, "%.0f");
8722 SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
8723 SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f");
8724 SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f");
8725 SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
8726 SliderFloat("ImageRounding", &style.ImageRounding, 0.0f, 12.0f, "%.0f");
8727 SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, max_border_size, "%.0f");
8728
8729 SeparatorText("Docking");
8730 //SetCursorPosX(GetCursorPosX() + CalcItemWidth() - GetFrameHeight());
8731 Checkbox("DockingNodeHasCloseButton", &style.DockingNodeHasCloseButton);
8732 SliderFloat("DockingSeparatorSize", &style.DockingSeparatorSize, 0.0f, 12.0f, "%.0f");
8733
8734 SeparatorText("Tooltips");
8735 for (int n = 0; n < 2; n++)
8736 if (TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav"))
8737 {
8738 ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;
8739 CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone);
8740 CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort);
8741 CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal);
8742 CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary);
8743 CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay);
8744 TreePop();
8745 }
8746
8747 SeparatorText("Misc");
8748 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.");
8749 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).");
8750
8751 EndTabItem();
8752 }
8753
8754 if (BeginTabItem("Colors"))
8755 {
8756 static int output_dest = 0;
8757 static bool output_only_modified = true;
8758 if (Button("Export"))
8759 {
8760 if (output_dest == 0)
8761 LogToClipboard();
8762 else
8763 LogToTTY();
8764 LogText("ImVec4* colors = GetStyle().Colors;" IM_NEWLINE);
8765 for (int i = 0; i < ImGuiCol_COUNT; i++)
8766 {
8767 const ImVec4& col = style.Colors[i];
8768 const char* name = GetStyleColorName(i);
8769 if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
8770 LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
8771 name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
8772 }
8773 LogFinish();
8774 }
8775 SameLine(); SetNextItemWidth(GetFontSize() * 10); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
8776 SameLine(); Checkbox("Only Modified Colors", &output_only_modified);
8777
8778 static ImGuiTextFilter filter;
8779 filter.Draw("Filter colors", GetFontSize() * 16);
8780
8781 static ImGuiColorEditFlags alpha_flags = 0;
8782 if (RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine();
8783 if (RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } SameLine();
8784 if (RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } SameLine();
8785 HelpMarker(
8786 "In the color list:\n"
8787 "Left-click on color square to open color picker,\n"
8788 "Right-click to open edit options menu.");
8789
8790 SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));
8791 BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
8792 PushItemWidth(GetFontSize() * -12);
8793 for (int i = 0; i < ImGuiCol_COUNT; i++)
8794 {
8795 const char* name = GetStyleColorName(i);
8796 if (!filter.PassFilter(name))
8797 continue;
8798 PushID(i);
8799#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8800 if (Button("?"))
8801 DebugFlashStyleColor((ImGuiCol)i);
8802 SetItemTooltip("Flash given color to identify places where it is used.");
8803 SameLine();
8804#endif
8805 ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);
8806 if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)
8807 {
8808 // Tips: in a real user application, you may want to merge and use an icon font into the main font,
8809 // so instead of "Save"/"Revert" you'd use icons!
8810 // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
8811 SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Save")) { ref->Colors[i] = style.Colors[i]; }
8812 SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Revert")) { style.Colors[i] = ref->Colors[i]; }
8813 }
8814 SameLine(0.0f, style.ItemInnerSpacing.x);
8815 TextUnformatted(name);
8816 PopID();
8817 }
8818 PopItemWidth();
8819 EndChild();
8820
8821 EndTabItem();
8822 }
8823
8824 if (BeginTabItem("Fonts"))
8825 {
8826 ImGuiIO& io = GetIO();
8827 ImFontAtlas* atlas = io.Fonts;
8828 ShowFontAtlas(atlas);
8829
8830 // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
8831 // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows Ctrl+Click text to get out of bounds).
8832 /*
8833 SeparatorText("Legacy Scaling");
8834 const float MIN_SCALE = 0.3f;
8835 const float MAX_SCALE = 2.0f;
8836 HelpMarker(
8837 "Those are old settings provided for convenience.\n"
8838 "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
8839 "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
8840 "Using those settings here will give you poor quality results.");
8841 PushItemWidth(GetFontSize() * 8);
8842 DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
8843 //static float window_scale = 1.0f;
8844 //if (DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
8845 // SetWindowFontScale(window_scale);
8846 PopItemWidth();
8847 */
8848
8849 EndTabItem();
8850 }
8851
8852 if (BeginTabItem("Rendering"))
8853 {
8854 Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
8855 SameLine();
8856 HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
8857
8858 Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
8859 SameLine();
8860 HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
8861
8862 Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
8863 PushItemWidth(GetFontSize() * 8);
8864 DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
8865 if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
8866
8867 // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
8868 DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp);
8869 const bool show_samples = IsItemActive();
8870 if (show_samples)
8871 SetNextWindowPos(GetCursorScreenPos());
8872 if (show_samples && BeginTooltip())
8873 {
8874 TextUnformatted("(R = radius, N = approx number of segments)");
8875 Spacing();
8876 ImDrawList* draw_list = GetWindowDrawList();
8877 const float min_widget_width = CalcTextSize("R: MMM\nN: MMM").x;
8878 for (int n = 0; n < 8; n++)
8879 {
8880 const float RAD_MIN = 5.0f;
8881 const float RAD_MAX = 70.0f;
8882 const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
8883
8884 BeginGroup();
8885
8886 // N is not always exact here due to how PathArcTo() function work internally
8887 Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad));
8888
8889 const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
8890 const float offset_x = floorf(canvas_width * 0.5f);
8891 const float offset_y = floorf(RAD_MAX);
8892
8893 const ImVec2 p1 = GetCursorScreenPos();
8894 draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
8895 Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8896
8897 /*
8898 const ImVec2 p2 = GetCursorScreenPos();
8899 draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
8900 Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8901 */
8902
8903 EndGroup();
8904 SameLine();
8905 }
8906 EndTooltip();
8907 }
8908 SameLine();
8909 HelpMarker("When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically.");
8910
8911 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.
8912 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).");
8913 PopItemWidth();
8914
8915 EndTabItem();
8916 }
8917
8918 EndTabBar();
8919 }
8920 PopItemWidth();
8921}
8922
8923//-----------------------------------------------------------------------------
8924// [SECTION] User Guide / ShowUserGuide()
8925//-----------------------------------------------------------------------------
8926
8927// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8928void ImGui::ShowUserGuide()
8929{
8930 ImGuiIO& io = GetIO();
8931 BulletText("Double-click on title bar to collapse window.");
8932 BulletText(
8933 "Click and drag on lower corner or border to resize window.\n"
8934 "(double-click to auto fit window to its contents)");
8935 BulletText("Ctrl+Click on a slider or drag box to input value as text.");
8936 BulletText("Tab/Shift+Tab to cycle through keyboard editable fields.");
8937 BulletText("Ctrl+Tab/Ctrl+Shift+Tab to focus windows.");
8938 if (io.FontAllowUserScaling)
8939 BulletText("Ctrl+Mouse Wheel to zoom window contents.");
8940 BulletText("While inputting text:\n");
8941 Indent();
8942 BulletText("Ctrl+Left/Right to word jump.");
8943 BulletText("Ctrl+A or double-click to select all.");
8944 BulletText("Ctrl+X/C/V to use clipboard cut/copy/paste.");
8945 BulletText("Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.");
8946 BulletText("Escape to revert.");
8947 Unindent();
8948 BulletText("With Keyboard controls enabled:");
8949 Indent();
8950 BulletText("Arrow keys or Home/End/PageUp/PageDown to navigate.");
8951 BulletText("Space to activate a widget.");
8952 BulletText("Return to input text into a widget.");
8953 BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus.");
8954 BulletText("Alt to jump to the menu layer of a window.");
8955 BulletText("Menu or Shift+F10 to open a context menu.");
8956 Unindent();
8957 BulletText("With Gamepad controls enabled:");
8958 Indent();
8959 BulletText("D-Pad: Navigate / Tweak / Resize (in Windowing mode).");
8960 BulletText("%s Face button: Activate / Open / Toggle. Hold: activate with text input.", io.ConfigNavSwapGamepadButtons ? "East" : "South");
8961 BulletText("%s Face button: Cancel / Close / Exit.", io.ConfigNavSwapGamepadButtons ? "South" : "East");
8962 BulletText("West Face button: Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows).");
8963 BulletText("North Face button: Open Context Menu.");
8964 BulletText("L1/R1: Tweak Slower/Faster, Focus Previous/Next (in Windowing Mode).");
8965 Unindent();
8966}
8967
8968//-----------------------------------------------------------------------------
8969// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
8970//-----------------------------------------------------------------------------
8971// - ShowExampleAppMainMenuBar()
8972// - ShowExampleMenuFile()
8973//-----------------------------------------------------------------------------
8974
8975// Demonstrate creating a "main" fullscreen menu bar and populating it.
8976// Note the difference between BeginMainMenuBar() and BeginMenuBar():
8977// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)
8978// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.
8979static void ShowExampleAppMainMenuBar()
8980{
8981 if (ImGui::BeginMainMenuBar())
8982 {
8983 if (ImGui::BeginMenu("File"))
8984 {
8985 IMGUI_DEMO_MARKER("Menu/File");
8986 ShowExampleMenuFile();
8987 ImGui::EndMenu();
8988 }
8989 if (ImGui::BeginMenu("Edit"))
8990 {
8991 IMGUI_DEMO_MARKER("Menu/Edit");
8992 if (ImGui::MenuItem("Undo", "Ctrl+Z")) {}
8993 if (ImGui::MenuItem("Redo", "Ctrl+Y", false, false)) {} // Disabled item
8994 ImGui::Separator();
8995 if (ImGui::MenuItem("Cut", "Ctrl+X")) {}
8996 if (ImGui::MenuItem("Copy", "Ctrl+C")) {}
8997 if (ImGui::MenuItem("Paste", "Ctrl+V")) {}
8998 ImGui::EndMenu();
8999 }
9000 ImGui::EndMainMenuBar();
9001 }
9002}
9003
9004// Note that shortcuts are currently provided for display only
9005// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
9006static void ShowExampleMenuFile()
9007{
9008 IMGUI_DEMO_MARKER("Examples/Menu");
9009 ImGui::MenuItem("(demo menu)", NULL, false, false);
9010 if (ImGui::MenuItem("New")) {}
9011 if (ImGui::MenuItem("Open", "Ctrl+O")) {}
9012 if (ImGui::BeginMenu("Open Recent"))
9013 {
9014 ImGui::MenuItem("fish_hat.c");
9015 ImGui::MenuItem("fish_hat.inl");
9016 ImGui::MenuItem("fish_hat.h");
9017 if (ImGui::BeginMenu("More.."))
9018 {
9019 ImGui::MenuItem("Hello");
9020 ImGui::MenuItem("Sailor");
9021 if (ImGui::BeginMenu("Recurse.."))
9022 {
9023 ShowExampleMenuFile();
9024 ImGui::EndMenu();
9025 }
9026 ImGui::EndMenu();
9027 }
9028 ImGui::EndMenu();
9029 }
9030 if (ImGui::MenuItem("Save", "Ctrl+S")) {}
9031 if (ImGui::MenuItem("Save As..")) {}
9032
9033 ImGui::Separator();
9034 if (ImGui::BeginMenu("Options"))
9035 {
9036 IMGUI_DEMO_MARKER("Examples/Menu/Options");
9037 static bool enabled = true;
9038 ImGui::MenuItem("Enabled", "", &enabled);
9039 ImGui::BeginChild("child", ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5.0f), ImGuiChildFlags_Borders);
9040 for (int i = 0; i < 10; i++)
9041 ImGui::Text("Scrolling Text %d", i);
9042 ImGui::EndChild();
9043 static float f = 0.5f;
9044 static int n = 0;
9045 ImGui::SliderFloat("Value", &f, 0.0f, 1.0f);
9046 ImGui::InputFloat("Input", &f, 0.1f);
9047 ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0");
9048 ImGui::EndMenu();
9049 }
9050
9051 if (ImGui::BeginMenu("Colors"))
9052 {
9053 IMGUI_DEMO_MARKER("Examples/Menu/Colors");
9054 float sz = ImGui::GetTextLineHeight();
9055 for (int i = 0; i < ImGuiCol_COUNT; i++)
9056 {
9057 const char* name = ImGui::GetStyleColorName((ImGuiCol)i);
9058 ImVec2 p = ImGui::GetCursorScreenPos();
9059 ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i));
9060 ImGui::Dummy(ImVec2(sz, sz));
9061 ImGui::SameLine();
9062 ImGui::MenuItem(name);
9063 }
9064 ImGui::EndMenu();
9065 }
9066
9067 // Here we demonstrate appending again to the "Options" menu (which we already created above)
9068 // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice.
9069 // In a real code-base using it would make senses to use this feature from very different code locations.
9070 if (ImGui::BeginMenu("Options")) // <-- Append!
9071 {
9072 IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu");
9073 static bool b = true;
9074 ImGui::Checkbox("SomeOption", &b);
9075 ImGui::EndMenu();
9076 }
9077
9078 if (ImGui::BeginMenu("Disabled", false)) // Disabled
9079 {
9080 IM_ASSERT(0);
9081 }
9082 if (ImGui::MenuItem("Checked", NULL, true)) {}
9083 ImGui::Separator();
9084 if (ImGui::MenuItem("Quit", "Alt+F4")) {}
9085}
9086
9087//-----------------------------------------------------------------------------
9088// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
9089//-----------------------------------------------------------------------------
9090
9091// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
9092// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
9093struct ExampleAppConsole
9094{
9095 char InputBuf[256];
9096 ImVector<char*> Items;
9097 ImVector<const char*> Commands;
9098 ImVector<char*> History;
9099 int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
9100 ImGuiTextFilter Filter;
9101 bool AutoScroll;
9102 bool ScrollToBottom;
9103
9104 ExampleAppConsole()
9105 {
9106 ClearLog();
9107 memset(InputBuf, 0, sizeof(InputBuf));
9108 HistoryPos = -1;
9109
9110 // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
9111 Commands.push_back("HELP");
9112 Commands.push_back("HISTORY");
9113 Commands.push_back("CLEAR");
9114 Commands.push_back("CLASSIFY");
9115 AutoScroll = true;
9116 ScrollToBottom = false;
9117 AddLog("Welcome to Dear ImGui!");
9118 }
9119 ~ExampleAppConsole()
9120 {
9121 ClearLog();
9122 for (int i = 0; i < History.Size; i++)
9123 ImGui::MemFree(History[i]);
9124 }
9125
9126 // Portable helpers
9127 static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }
9128 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; }
9129 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); }
9130 static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
9131
9132 void ClearLog()
9133 {
9134 for (int i = 0; i < Items.Size; i++)
9135 ImGui::MemFree(Items[i]);
9136 Items.clear();
9137 }
9138
9139 void AddLog(const char* fmt, ...) IM_FMTARGS(2)
9140 {
9141 // FIXME-OPT
9142 char buf[1024];
9143 va_list args;
9144 va_start(args, fmt);
9145 vsnprintf(buf, IM_COUNTOF(buf), fmt, args);
9146 buf[IM_COUNTOF(buf)-1] = 0;
9147 va_end(args);
9148 Items.push_back(Strdup(buf));
9149 }
9150
9151 void Draw(const char* title, bool* p_open)
9152 {
9153 ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
9154 if (!ImGui::Begin(title, p_open))
9155 {
9156 ImGui::End();
9157 return;
9158 }
9159 IMGUI_DEMO_MARKER("Examples/Console");
9160
9161 // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
9162 // So e.g. IsItemHovered() will return true when hovering the title bar.
9163 // Here we create a context menu only available from the title bar.
9164 if (ImGui::BeginPopupContextItem())
9165 {
9166 if (ImGui::MenuItem("Close Console"))
9167 *p_open = false;
9168 ImGui::EndPopup();
9169 }
9170
9171 ImGui::TextWrapped(
9172 "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate "
9173 "implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
9174 ImGui::TextWrapped("Enter 'HELP' for help.");
9175
9176 // TODO: display items starting from the bottom
9177
9178 if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); }
9179 ImGui::SameLine();
9180 if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); }
9181 ImGui::SameLine();
9182 if (ImGui::SmallButton("Clear")) { ClearLog(); }
9183 ImGui::SameLine();
9184 bool copy_to_clipboard = ImGui::SmallButton("Copy");
9185 //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
9186
9187 ImGui::Separator();
9188
9189 // Options menu
9190 if (ImGui::BeginPopup("Options"))
9191 {
9192 ImGui::Checkbox("Auto-scroll", &AutoScroll);
9193 ImGui::EndPopup();
9194 }
9195
9196 // Options, Filter
9197 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_O, ImGuiInputFlags_Tooltip);
9198 if (ImGui::Button("Options"))
9199 ImGui::OpenPopup("Options");
9200 ImGui::SameLine();
9201 Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
9202 ImGui::Separator();
9203
9204 // Reserve enough left-over height for 1 separator + 1 input text
9205 ImGuiStyle& style = ImGui::GetStyle();
9206 const float footer_height_to_reserve = style.SeparatorSize + style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
9207 if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar))
9208 {
9209 if (ImGui::BeginPopupContextWindow())
9210 {
9211 if (ImGui::Selectable("Clear")) ClearLog();
9212 ImGui::EndPopup();
9213 }
9214
9215 // Display every line as a separate entry so we can change their color or add custom widgets.
9216 // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
9217 // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
9218 // to only process visible items. The clipper will automatically measure the height of your first item and then
9219 // "seek" to display only items in the visible area.
9220 // To use the clipper we can replace your standard loop:
9221 // for (int i = 0; i < Items.Size; i++)
9222 // With:
9223 // ImGuiListClipper clipper;
9224 // clipper.Begin(Items.Size);
9225 // while (clipper.Step())
9226 // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9227 // - That your items are evenly spaced (same height)
9228 // - That you have cheap random access to your elements (you can access them given their index,
9229 // without processing all the ones before)
9230 // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
9231 // We would need random-access on the post-filtered list.
9232 // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
9233 // or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
9234 // and appending newly elements as they are inserted. This is left as a task to the user until we can manage
9235 // to improve this example code!
9236 // If your items are of variable height:
9237 // - Split them into same height items would be simpler and facilitate random-seeking into your list.
9238 // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
9239 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing
9240 if (copy_to_clipboard)
9241 ImGui::LogToClipboard();
9242 for (const char* item : Items)
9243 {
9244 if (!Filter.PassFilter(item))
9245 continue;
9246
9247 // Normally you would store more information in your item than just a string.
9248 // (e.g. make Items[] an array of structure, store color/type etc.)
9249 ImVec4 color;
9250 bool has_color = false;
9251 if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
9252 else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
9253 if (has_color)
9254 ImGui::PushStyleColor(ImGuiCol_Text, color);
9255 ImGui::TextUnformatted(item);
9256 if (has_color)
9257 ImGui::PopStyleColor();
9258 }
9259 if (copy_to_clipboard)
9260 ImGui::LogFinish();
9261
9262 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
9263 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
9264 if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
9265 ImGui::SetScrollHereY(1.0f);
9266 ScrollToBottom = false;
9267
9268 ImGui::PopStyleVar();
9269 }
9270 ImGui::EndChild();
9271 ImGui::Separator();
9272
9273 // Command-line
9274 bool reclaim_focus = false;
9275 ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
9276 if (ImGui::InputText("Input", InputBuf, IM_COUNTOF(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))
9277 {
9278 char* s = InputBuf;
9279 Strtrim(s);
9280 if (s[0])
9281 ExecCommand(s);
9282 strcpy(s, "");
9283 reclaim_focus = true;
9284 }
9285
9286 // Auto-focus on window apparition
9287 ImGui::SetItemDefaultFocus();
9288 if (reclaim_focus)
9289 ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
9290
9291 ImGui::End();
9292 }
9293
9294 void ExecCommand(const char* command_line)
9295 {
9296 AddLog("# %s\n", command_line);
9297
9298 // Insert into history. First find match and delete it so it can be pushed to the back.
9299 // This isn't trying to be smart or optimal.
9300 HistoryPos = -1;
9301 for (int i = History.Size - 1; i >= 0; i--)
9302 if (Stricmp(History[i], command_line) == 0)
9303 {
9304 ImGui::MemFree(History[i]);
9305 History.erase(History.begin() + i);
9306 break;
9307 }
9308 History.push_back(Strdup(command_line));
9309
9310 // Process command
9311 if (Stricmp(command_line, "CLEAR") == 0)
9312 {
9313 ClearLog();
9314 }
9315 else if (Stricmp(command_line, "HELP") == 0)
9316 {
9317 AddLog("Commands:");
9318 for (int i = 0; i < Commands.Size; i++)
9319 AddLog("- %s", Commands[i]);
9320 }
9321 else if (Stricmp(command_line, "HISTORY") == 0)
9322 {
9323 int first = History.Size - 10;
9324 for (int i = first > 0 ? first : 0; i < History.Size; i++)
9325 AddLog("%3d: %s\n", i, History[i]);
9326 }
9327 else
9328 {
9329 AddLog("Unknown command: '%s'\n", command_line);
9330 }
9331
9332 // On command input, we scroll to bottom even if AutoScroll==false
9333 ScrollToBottom = true;
9334 }
9335
9336 // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
9337 static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)
9338 {
9339 ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;
9340 return console->TextEditCallback(data);
9341 }
9342
9343 int TextEditCallback(ImGuiInputTextCallbackData* data)
9344 {
9345 //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
9346 switch (data->EventFlag)
9347 {
9348 case ImGuiInputTextFlags_CallbackCompletion:
9349 {
9350 // Example of TEXT COMPLETION
9351
9352 // Locate beginning of current word
9353 const char* word_end = data->Buf + data->CursorPos;
9354 const char* word_start = word_end;
9355 while (word_start > data->Buf)
9356 {
9357 const char c = word_start[-1];
9358 if (c == ' ' || c == '\t' || c == ',' || c == ';')
9359 break;
9360 word_start--;
9361 }
9362
9363 // Build a list of candidates
9364 ImVector<const char*> candidates;
9365 for (int i = 0; i < Commands.Size; i++)
9366 if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
9367 candidates.push_back(Commands[i]);
9368
9369 if (candidates.Size == 0)
9370 {
9371 // No match
9372 AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
9373 }
9374 else if (candidates.Size == 1)
9375 {
9376 // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
9377 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
9378 data->InsertChars(data->CursorPos, candidates[0]);
9379 data->InsertChars(data->CursorPos, " ");
9380 }
9381 else
9382 {
9383 // Multiple matches. Complete as much as we can..
9384 // So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
9385 int match_len = (int)(word_end - word_start);
9386 for (;;)
9387 {
9388 int c = 0;
9389 bool all_candidates_matches = true;
9390 for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
9391 if (i == 0)
9392 c = toupper(candidates[i][match_len]);
9393 else if (c == 0 || c != toupper(candidates[i][match_len]))
9394 all_candidates_matches = false;
9395 if (!all_candidates_matches)
9396 break;
9397 match_len++;
9398 }
9399
9400 if (match_len > 0)
9401 {
9402 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
9403 data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
9404 }
9405
9406 // List matches
9407 AddLog("Possible matches:\n");
9408 for (int i = 0; i < candidates.Size; i++)
9409 AddLog("- %s\n", candidates[i]);
9410 }
9411
9412 break;
9413 }
9414 case ImGuiInputTextFlags_CallbackHistory:
9415 {
9416 // Example of HISTORY
9417 const int prev_history_pos = HistoryPos;
9418 if (data->EventKey == ImGuiKey_UpArrow)
9419 {
9420 if (HistoryPos == -1)
9421 HistoryPos = History.Size - 1;
9422 else if (HistoryPos > 0)
9423 HistoryPos--;
9424 }
9425 else if (data->EventKey == ImGuiKey_DownArrow)
9426 {
9427 if (HistoryPos != -1)
9428 if (++HistoryPos >= History.Size)
9429 HistoryPos = -1;
9430 }
9431
9432 // A better implementation would preserve the data on the current input line along with cursor position.
9433 if (prev_history_pos != HistoryPos)
9434 {
9435 const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
9436 data->DeleteChars(0, data->BufTextLen);
9437 data->InsertChars(0, history_str);
9438 }
9439 }
9440 }
9441 return 0;
9442 }
9443};
9444
9445static void ShowExampleAppConsole(bool* p_open)
9446{
9447 static ExampleAppConsole console;
9448 console.Draw("Example: Console", p_open);
9449}
9450
9451//-----------------------------------------------------------------------------
9452// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer()
9453//-----------------------------------------------------------------------------
9454
9455static void ShowExampleAppImageViewer(bool* p_open)
9456{
9457 ImFontAtlas* atlas = ImGui::GetIO().Fonts;
9458 ImTextureRef tex_ref = atlas->TexRef; // We don't have access to other textures in this demo!
9459 int tex_w = atlas->TexData->Width;
9460 int tex_h = atlas->TexData->Height;
9461 if (ImGui::Begin("Example: Image Viewer", p_open))
9462 {
9463 static ExampleImageViewerData image_viewer;
9464 ExampleImageViewer_DrawOptions(&image_viewer);
9465 ImVec2 canvas_size = ImGui::GetContentRegionAvail();
9466 ImVec2 canvas_min_size = ImGui::IsWindowAppearing() ? ImVec2(3.0f * tex_w, 4.0f * tex_h) : ImVec2(1.0f, 1.0f);
9467 canvas_size = ImVec2(IM_MAX(canvas_size.x, canvas_min_size.x), IM_MAX(canvas_size.y, canvas_min_size.y));
9468 ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, tex_ref, tex_w, tex_h);
9469 }
9470 ImGui::End();
9471}
9472
9473//-----------------------------------------------------------------------------
9474// [SECTION] Example App: Debug Log / ShowExampleAppLog()
9475//-----------------------------------------------------------------------------
9476
9477// Usage:
9478// static ExampleAppLog my_log;
9479// my_log.AddLog("Hello %d world\n", 123);
9480// my_log.Draw("title");
9481struct ExampleAppLog
9482{
9483 ImGuiTextBuffer Buf;
9484 ImGuiTextFilter Filter;
9485 ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
9486 bool AutoScroll; // Keep scrolling if already at the bottom.
9487
9488 ExampleAppLog()
9489 {
9490 AutoScroll = true;
9491 Clear();
9492 }
9493
9494 void Clear()
9495 {
9496 Buf.clear();
9497 LineOffsets.clear();
9498 LineOffsets.push_back(0);
9499 }
9500
9501 void AddLog(const char* fmt, ...) IM_FMTARGS(2)
9502 {
9503 int old_size = Buf.size();
9504 va_list args;
9505 va_start(args, fmt);
9506 Buf.appendfv(fmt, args);
9507 va_end(args);
9508 for (int new_size = Buf.size(); old_size < new_size; old_size++)
9509 if (Buf[old_size] == '\n')
9510 LineOffsets.push_back(old_size + 1);
9511 }
9512
9513 void Draw(const char* title, bool* p_open = NULL)
9514 {
9515 if (!ImGui::Begin(title, p_open))
9516 {
9517 ImGui::End();
9518 return;
9519 }
9520
9521 // Options menu
9522 if (ImGui::BeginPopup("Options"))
9523 {
9524 ImGui::Checkbox("Auto-scroll", &AutoScroll);
9525 ImGui::EndPopup();
9526 }
9527
9528 // Main window
9529 if (ImGui::Button("Options"))
9530 ImGui::OpenPopup("Options");
9531 ImGui::SameLine();
9532 bool clear = ImGui::Button("Clear");
9533 ImGui::SameLine();
9534 bool copy = ImGui::Button("Copy");
9535 ImGui::SameLine();
9536 Filter.Draw("Filter", -100.0f);
9537
9538 ImGui::Separator();
9539
9540 if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))
9541 {
9542 if (clear)
9543 Clear();
9544 if (copy)
9545 ImGui::LogToClipboard();
9546
9547 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9548 const char* buf = Buf.begin();
9549 const char* buf_end = Buf.end();
9550 if (Filter.IsActive())
9551 {
9552 // In this example we don't use the clipper when Filter is enabled.
9553 // This is because we don't have random access to the result of our filter.
9554 // A real application processing logs with ten of thousands of entries may want to store the result of
9555 // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
9556 for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
9557 {
9558 const char* line_start = buf + LineOffsets[line_no];
9559 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9560 if (Filter.PassFilter(line_start, line_end))
9561 ImGui::TextUnformatted(line_start, line_end);
9562 }
9563 }
9564 else
9565 {
9566 // The simplest and easy way to display the entire buffer:
9567 // ImGui::TextUnformatted(buf_begin, buf_end);
9568 // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
9569 // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
9570 // within the visible area.
9571 // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
9572 // on your side is recommended. Using ImGuiListClipper requires
9573 // - A) random access into your data
9574 // - B) items all being the same height,
9575 // both of which we can handle since we have an array pointing to the beginning of each line of text.
9576 // When using the filter (in the block of code above) we don't have random access into the data to display
9577 // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
9578 // it possible (and would be recommended if you want to search through tens of thousands of entries).
9579 ImGuiListClipper clipper;
9580 clipper.Begin(LineOffsets.Size);
9581 while (clipper.Step())
9582 {
9583 for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
9584 {
9585 const char* line_start = buf + LineOffsets[line_no];
9586 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9587 ImGui::TextUnformatted(line_start, line_end);
9588 }
9589 }
9590 clipper.End();
9591 }
9592 ImGui::PopStyleVar();
9593
9594 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
9595 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
9596 if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
9597 ImGui::SetScrollHereY(1.0f);
9598 }
9599 ImGui::EndChild();
9600 ImGui::End();
9601 }
9602};
9603
9604// Demonstrate creating a simple log window with basic filtering.
9605static void ShowExampleAppLog(bool* p_open)
9606{
9607 static ExampleAppLog log;
9608
9609 // For the demo: add a debug button _BEFORE_ the normal log window contents
9610 // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.
9611 // Most of the contents of the window will be added by the log.Draw() call.
9612 ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
9613 ImGui::Begin("Example: Log", p_open);
9614 IMGUI_DEMO_MARKER("Examples/Log");
9615 if (ImGui::SmallButton("[Debug] Add 5 entries"))
9616 {
9617 static int counter = 0;
9618 const char* categories[3] = { "info", "warn", "error" };
9619 const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
9620 for (int n = 0; n < 5; n++)
9621 {
9622 const char* category = categories[counter % IM_COUNTOF(categories)];
9623 const char* word = words[counter % IM_COUNTOF(words)];
9624 log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
9625 ImGui::GetFrameCount(), category, ImGui::GetTime(), word);
9626 counter++;
9627 }
9628 }
9629 ImGui::End();
9630
9631 // Actually call in the regular Log helper (which will Begin() into the same window as we just did)
9632 log.Draw("Example: Log", p_open);
9633}
9634
9635//-----------------------------------------------------------------------------
9636// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
9637//-----------------------------------------------------------------------------
9638
9639// Demonstrate create a window with multiple child windows.
9640static void ShowExampleAppLayout(bool* p_open)
9641{
9642 ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver);
9643 if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar))
9644 {
9645 IMGUI_DEMO_MARKER("Examples/Simple layout");
9646 if (ImGui::BeginMenuBar())
9647 {
9648 if (ImGui::BeginMenu("File"))
9649 {
9650 if (ImGui::MenuItem("Close", "Ctrl+W")) { *p_open = false; }
9651 ImGui::EndMenu();
9652 }
9653 ImGui::EndMenuBar();
9654 }
9655
9656 // Left
9657 static int selected = 0;
9658 {
9659 ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
9660 for (int i = 0; i < 100; i++)
9661 {
9662 char label[128];
9663 sprintf(label, "MyObject %d", i);
9664 if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SelectOnNav))
9665 selected = i;
9666 }
9667 ImGui::EndChild();
9668 }
9669 ImGui::SameLine();
9670
9671 // Right
9672 {
9673 ImGui::BeginGroup();
9674 ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
9675 ImGui::Text("MyObject: %d", selected);
9676 ImGui::Separator();
9677 if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
9678 {
9679 if (ImGui::BeginTabItem("Description"))
9680 {
9681 ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ");
9682 ImGui::EndTabItem();
9683 }
9684 if (ImGui::BeginTabItem("Details"))
9685 {
9686 ImGui::Text("ID: 0123456789");
9687 ImGui::EndTabItem();
9688 }
9689 ImGui::EndTabBar();
9690 }
9691 ImGui::EndChild();
9692 if (ImGui::Button("Revert")) {}
9693 ImGui::SameLine();
9694 if (ImGui::Button("Save")) {}
9695 ImGui::EndGroup();
9696 }
9697 }
9698 ImGui::End();
9699}
9700
9701//-----------------------------------------------------------------------------
9702// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
9703//-----------------------------------------------------------------------------
9704// Some of the interactions are a bit lack-luster:
9705// - We would want pressing validating or leaving the filter to somehow restore focus.
9706// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.
9707// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.
9708//-----------------------------------------------------------------------------
9709
9710struct ExampleAppPropertyEditor
9711{
9712 ImGuiTextFilter Filter;
9713 ExampleTreeNode* SelectedNode = NULL;
9714 bool UseClipper = false;
9715
9716 void Draw(ExampleTreeNode* root_node)
9717 {
9718 IMGUI_DEMO_MARKER("Examples/Property editor");
9719
9720 // Left side: draw tree
9721 // - Currently using a table to benefit from RowBg feature
9722 // - Our tree node are all of equal height, facilitating the use of a clipper.
9723 if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
9724 {
9725 ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
9726 ImGui::Checkbox("Use Clipper", &UseClipper);
9727 ImGui::SameLine();
9728 ImGui::Text("(%d root nodes)", root_node->Childs.Size);
9729 ImGui::SetNextItemWidth(-FLT_MIN);
9730 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
9731 if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll))
9732 Filter.Build();
9733 ImGui::PopItemFlag();
9734
9735 if (ImGui::BeginTable("##list", 1, ImGuiTableFlags_RowBg))
9736 {
9737 if (UseClipper)
9738 DrawClippedTree(root_node);
9739 else
9740 DrawTree(root_node);
9741 ImGui::EndTable();
9742 }
9743 }
9744 ImGui::EndChild();
9745
9746 // Right side: draw properties
9747 ImGui::SameLine();
9748
9749 ImGui::BeginGroup(); // Lock X position
9750 if (ExampleTreeNode* node = SelectedNode)
9751 {
9752 ImGui::Text("%s", node->Name);
9753 ImGui::TextDisabled("UID: 0x%08X", node->UID);
9754 ImGui::Separator();
9755 if (ImGui::BeginTable("##properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))
9756 {
9757 // Push object ID after we entered the table, so table is shared for all objects
9758 ImGui::PushID((int)node->UID);
9759 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
9760 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger
9761 if (node->HasData)
9762 {
9763 // In a typical application, the structure description would be derived from a data-driven system.
9764 // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.
9765 // - Limits and some details are hard-coded to simplify the demo.
9766 for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)
9767 {
9768 ImGui::TableNextRow();
9769 ImGui::PushID(field_desc.Name);
9770 ImGui::TableNextColumn();
9771 ImGui::AlignTextToFramePadding();
9772 ImGui::TextUnformatted(field_desc.Name);
9773 ImGui::TableNextColumn();
9774 void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);
9775 switch (field_desc.DataType)
9776 {
9777 case ImGuiDataType_Bool:
9778 {
9779 IM_ASSERT(field_desc.DataCount == 1);
9780 ImGui::Checkbox("##Editor", (bool*)field_ptr);
9781 break;
9782 }
9783 case ImGuiDataType_S32:
9784 {
9785 int v_min = INT_MIN, v_max = INT_MAX;
9786 ImGui::SetNextItemWidth(-FLT_MIN);
9787 ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max);
9788 break;
9789 }
9790 case ImGuiDataType_Float:
9791 {
9792 float v_min = 0.0f, v_max = 1.0f;
9793 ImGui::SetNextItemWidth(-FLT_MIN);
9794 ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max);
9795 break;
9796 }
9797 case ImGuiDataType_String:
9798 {
9799 ImGui::InputText("##Editor", reinterpret_cast<char*>(field_ptr), 28);
9800 break;
9801 }
9802 }
9803 ImGui::PopID();
9804 }
9805 }
9806 ImGui::PopID();
9807 ImGui::EndTable();
9808 }
9809 }
9810 ImGui::EndGroup();
9811 }
9812
9813 // Custom search filter
9814 // - Here we apply on root node only.
9815 // - This does a case insensitive stristr which is pretty heavy. In a real large-scale app you would likely store a filtered list which in turns would be trivial to linearize.
9816 inline bool IsNodePassingFilter(ExampleTreeNode* node)
9817 {
9818 return node->Parent->Parent != NULL || Filter.PassFilter(node->Name);
9819 }
9820
9821 // Basic version, recursive. This is how you would generally draw a tree.
9822 // - Simple but going to be noticeably costly if you have a large amount of nodes as DrawTreeNode() is called for all of them.
9823 // - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~1.2 ms
9824 // - Unlike arrays or grids which are very easy to clip, trees are currently more difficult to clip.
9825 void DrawTree(ExampleTreeNode* node)
9826 {
9827 for (ExampleTreeNode* child : node->Childs)
9828 if (IsNodePassingFilter(child) && DrawTreeNode(child))
9829 {
9830 DrawTree(child);
9831 ImGui::TreePop();
9832 }
9833 }
9834
9835 // More advanced version. Use a alternative clipping technique: fast-forwarding through non-visible chunks.
9836 // - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~0.1 ms
9837 // (in ExampleTree_CreateDemoTree(), change 'int ROOT_ITEMS_COUNT = 10000' to try with this amount of root nodes).
9838 // - 1. Use clipper with indeterminate count (items_count = INT_MAX): we need to call SeekCursorForItem() at the end once we know the count.
9839 // - 2. Use SetNextItemStorageID() to specify ID used for open/close storage, making it easy to call TreeNodeGetOpen() on any arbitrary node.
9840 // - 3. Linearize tree during traversal: our tree data structure makes it easy to access sibling and parents.
9841 // - Unlike clipping for a regular array or grid which may be done using random access limited to visible areas,
9842 // this technique requires traversing most accessible nodes. This could be made more optimal with extra work,
9843 // but this is a decent simplicity<>speed trade-off.
9844 // See https://github.com/ocornut/imgui/issues/3823 for discussions about this.
9845 void DrawClippedTree(ExampleTreeNode* root_node)
9846 {
9847 ExampleTreeNode* node = root_node->Childs[0]; // First node
9848 ImGuiListClipper clipper;
9849 clipper.Begin(INT_MAX);
9850 while (clipper.Step())
9851 while (clipper.UserIndex < clipper.DisplayEnd && node != NULL)
9852 node = DrawClippedTreeNodeAndAdvanceToNext(&clipper, node);
9853
9854 // Keep going to count nodes and submit final count so we have a reliable scrollbar.
9855 // - One could consider caching this value and only refreshing it occasionally e.g. window is focused and an action occurs.
9856 // - Incorrect but cheap approximation would be to use 'clipper_current_idx = IM_MAX(clipper_current_idx, root_node->Childs.Size)' instead.
9857 // - If either of those is implemented, the general cost will approach zero when scrolling is at the top of the tree.
9858 while (node != NULL)
9859 node = DrawClippedTreeNodeAndAdvanceToNext(&clipper, node);
9860 //clipper.UserIndex = IM_MAX(clipper.UserIndex, root_node->Childs.Size); // <-- Cheap approximation instead of while() loop above.
9861 clipper.SeekCursorForItem(clipper.UserIndex);
9862 }
9863
9864 ExampleTreeNode* DrawClippedTreeNodeAndAdvanceToNext(ImGuiListClipper* clipper, ExampleTreeNode* node)
9865 {
9866 if (IsNodePassingFilter(node))
9867 {
9868 // Draw node if within visible range
9869 bool is_open = false;
9870 if (clipper->UserIndex >= clipper->DisplayStart && clipper->UserIndex < clipper->DisplayEnd)
9871 {
9872 is_open = DrawTreeNode(node);
9873 }
9874 else
9875 {
9876 is_open = (node->Childs.Size > 0 && ImGui::TreeNodeGetOpen((ImGuiID)node->UID));
9877 if (is_open)
9878 ImGui::TreePush(node->Name);
9879 }
9880 clipper->UserIndex++;
9881
9882 // Next node: recurse into childs
9883 if (is_open)
9884 return node->Childs[0];
9885 }
9886
9887 // Next node: next sibling, otherwise move back to parent
9888 while (node != NULL)
9889 {
9890 if (node->IndexInParent + 1 < node->Parent->Childs.Size)
9891 return node->Parent->Childs[node->IndexInParent + 1];
9892 node = node->Parent;
9893 if (node->Parent == NULL)
9894 break;
9895 ImGui::TreePop();
9896 }
9897 return NULL;
9898 }
9899
9900 // To support node with same name we incorporate node->UID into the item ID.
9901 // (this would more naturally be done using PushID(node->UID) + TreeNodeEx(node->Name, tree_flags),
9902 // but it would require in DrawClippedTreeNodeAndAdvanceToNext() to add PushID() before TreePush(), and PopID() after TreePop(),
9903 // so instead we use TreeNodeEx(node->UID, tree_flags, "%s", node->Name) here)
9904 bool DrawTreeNode(ExampleTreeNode* node)
9905 {
9906 ImGui::TableNextRow();
9907 ImGui::TableNextColumn();
9908 ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
9909 tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards
9910 tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support
9911 tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
9912 tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
9913 if (node == SelectedNode)
9914 tree_flags |= ImGuiTreeNodeFlags_Selected; // Draw selection highlight
9915 if (node->Childs.Size == 0)
9916 tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen; // Use _NoTreePushOnOpen + set is_open=false to avoid unnecessarily push/pop on leaves.
9917 if (node->DataMyBool == false)
9918 ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
9919 ImGui::SetNextItemStorageID((ImGuiID)node->UID); // Use node->UID as storage id
9920 bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node->UID, tree_flags, "%s", node->Name);
9921 if (node->Childs.Size == 0)
9922 is_open = false;
9923 if (node->DataMyBool == false)
9924 ImGui::PopStyleColor();
9925 if (ImGui::IsItemFocused())
9926 SelectedNode = node;
9927 return is_open;
9928 }
9929};
9930
9931// Demonstrate creating a simple property editor.
9932static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data)
9933{
9934 ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);
9935 if (!ImGui::Begin("Example: Property editor", p_open))
9936 {
9937 ImGui::End();
9938 return;
9939 }
9940
9941 IMGUI_DEMO_MARKER("Examples/Property Editor");
9942 static ExampleAppPropertyEditor property_editor;
9943 if (demo_data->DemoTree == NULL)
9944 demo_data->DemoTree = ExampleTree_CreateDemoTree();
9945 property_editor.Draw(demo_data->DemoTree);
9946
9947 ImGui::End();
9948}
9949
9950//-----------------------------------------------------------------------------
9951// [SECTION] Example App: Long Text / ShowExampleAppLongText()
9952//-----------------------------------------------------------------------------
9953
9954// Demonstrate/test rendering huge amount of text, and the incidence of clipping.
9955static void ShowExampleAppLongText(bool* p_open)
9956{
9957 ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
9958 if (!ImGui::Begin("Example: Long text display", p_open))
9959 {
9960 ImGui::End();
9961 return;
9962 }
9963 IMGUI_DEMO_MARKER("Examples/Long text display");
9964
9965 static int test_type = 0;
9966 static ImGuiTextBuffer log;
9967 static int lines = 0;
9968 ImGui::Text("Printing unusually long amount of text.");
9969 ImGui::Combo("Test type", &test_type,
9970 "Single call to TextUnformatted()\0"
9971 "Multiple calls to Text(), clipped\0"
9972 "Multiple calls to Text(), not clipped (slow)\0");
9973 ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size());
9974 if (ImGui::Button("Clear")) { log.clear(); lines = 0; }
9975 ImGui::SameLine();
9976 if (ImGui::Button("Add 1000 lines"))
9977 {
9978 for (int i = 0; i < 1000; i++)
9979 log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i);
9980 lines += 1000;
9981 }
9982 ImGui::BeginChild("Log");
9983 switch (test_type)
9984 {
9985 case 0:
9986 // Single call to TextUnformatted() with a big buffer
9987 ImGui::TextUnformatted(log.begin(), log.end());
9988 break;
9989 case 1:
9990 {
9991 // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
9992 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9993 ImGuiListClipper clipper;
9994 clipper.Begin(lines);
9995 while (clipper.Step())
9996 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9997 ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
9998 ImGui::PopStyleVar();
9999 break;
10000 }
10001 case 2:
10002 // Multiple calls to Text(), not clipped (slow)
10003 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
10004 for (int i = 0; i < lines; i++)
10005 ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
10006 ImGui::PopStyleVar();
10007 break;
10008 }
10009 ImGui::EndChild();
10010 ImGui::End();
10011}
10012
10013//-----------------------------------------------------------------------------
10014// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
10015//-----------------------------------------------------------------------------
10016
10017// Demonstrate creating a window which gets auto-resized according to its content.
10018static void ShowExampleAppAutoResize(bool* p_open)
10019{
10020 if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize))
10021 {
10022 ImGui::End();
10023 return;
10024 }
10025 IMGUI_DEMO_MARKER("Examples/Auto-resizing window");
10026
10027 static int lines = 10;
10028 ImGui::TextUnformatted(
10029 "Window will resize every-frame to the size of its content.\n"
10030 "Note that you probably don't want to query the window size to\n"
10031 "output your content because that would create a feedback loop.");
10032 ImGui::SliderInt("Number of lines", &lines, 1, 20);
10033 for (int i = 0; i < lines; i++)
10034 ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally
10035 ImGui::End();
10036}
10037
10038//-----------------------------------------------------------------------------
10039// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
10040//-----------------------------------------------------------------------------
10041
10042// Demonstrate creating a window with custom resize constraints.
10043// Note that size constraints currently don't work on a docked window (when in 'docking' branch)
10044static void ShowExampleAppConstrainedResize(bool* p_open)
10045{
10046 struct CustomConstraints
10047 {
10048 // Helper functions to demonstrate programmatic constraints
10049 // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.
10050 // FIXME: None of the three demos works consistently when resizing from borders.
10051 static void AspectRatio(ImGuiSizeCallbackData* data)
10052 {
10053 float aspect_ratio = *(float*)data->UserData;
10054 data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio);
10055 }
10056 static void Square(ImGuiSizeCallbackData* data)
10057 {
10058 data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);
10059 }
10060 static void Step(ImGuiSizeCallbackData* data)
10061 {
10062 float step = *(float*)data->UserData;
10063 data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step);
10064 }
10065 };
10066
10067 const char* test_desc[] =
10068 {
10069 "Between 100x100 and 500x500",
10070 "At least 100x100",
10071 "Resize vertical + lock current width",
10072 "Resize horizontal + lock current height",
10073 "Width Between 400 and 500",
10074 "Height at least 400",
10075 "Custom: Aspect Ratio 16:9",
10076 "Custom: Always Square",
10077 "Custom: Fixed Steps (100)",
10078 };
10079
10080 // Options
10081 static bool auto_resize = false;
10082 static bool window_padding = true;
10083 static int type = 6; // Aspect Ratio
10084 static int display_lines = 10;
10085
10086 // Submit constraint
10087 float aspect_ratio = 16.0f / 9.0f;
10088 float fixed_step = 100.0f;
10089 if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500)); // Between 100x100 and 500x500
10090 if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100
10091 if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width
10092 if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height
10093 if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width Between and 400 and 500
10094 if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, FLT_MAX)); // Height at least 400
10095 if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio); // Aspect ratio
10096 if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square
10097 if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step); // Fixed Step
10098
10099 // Submit window
10100 if (!window_padding)
10101 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
10102 const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;
10103 const bool window_open = ImGui::Begin("Example: Constrained Resize", p_open, window_flags);
10104 if (!window_padding)
10105 ImGui::PopStyleVar();
10106 IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
10107 if (window_open)
10108 {
10109 if (ImGui::GetIO().KeyShift)
10110 {
10111 // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture)
10112 ImVec2 avail_size = ImGui::GetContentRegionAvail();
10113 ImVec2 pos = ImGui::GetCursorScreenPos();
10114 ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);
10115 ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10));
10116 ImGui::Text("%.2f x %.2f", avail_size.x, avail_size.y);
10117 }
10118 else
10119 {
10120 ImGui::Text("(Hold Shift to display a dummy viewport)");
10121 if (ImGui::IsWindowDocked())
10122 ImGui::Text("Warning: Sizing Constraints won't work if the window is docked!");
10123 if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine();
10124 if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine();
10125 if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); }
10126 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
10127 ImGui::Combo("Constraint", &type, test_desc, IM_COUNTOF(test_desc));
10128 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
10129 ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100);
10130 ImGui::Checkbox("Auto-resize", &auto_resize);
10131 ImGui::Checkbox("Window padding", &window_padding);
10132 for (int i = 0; i < display_lines; i++)
10133 ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, "");
10134 }
10135 }
10136 ImGui::End();
10137}
10138
10139//-----------------------------------------------------------------------------
10140// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
10141//-----------------------------------------------------------------------------
10142
10143// Demonstrate creating a simple static window with no decoration
10144// + a context-menu to choose which corner of the screen to use.
10145static void ShowExampleAppSimpleOverlay(bool* p_open)
10146{
10147 static int location = 0;
10148 ImGuiIO& io = ImGui::GetIO();
10149 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
10150 if (location >= 0)
10151 {
10152 const float PAD = 10.0f;
10153 const ImGuiViewport* viewport = ImGui::GetMainViewport();
10154 ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
10155 ImVec2 work_size = viewport->WorkSize;
10156 ImVec2 window_pos, window_pos_pivot;
10157 window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);
10158 window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);
10159 window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f;
10160 window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f;
10161 ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
10162 ImGui::SetNextWindowViewport(viewport->ID);
10163 window_flags |= ImGuiWindowFlags_NoMove;
10164 }
10165 else if (location == -2)
10166 {
10167 // Center window
10168 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
10169 window_flags |= ImGuiWindowFlags_NoMove;
10170 }
10171 ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
10172 if (ImGui::Begin("Example: Simple overlay", p_open, window_flags))
10173 {
10174 IMGUI_DEMO_MARKER("Examples/Simple overlay"); // Scroll up to the beginning of this function to see overlay flags
10175 ImGui::Text("Simple overlay\n" "(right-click to change position)");
10176 ImGui::Separator();
10177 if (ImGui::IsMousePosValid())
10178 ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
10179 else
10180 ImGui::Text("Mouse Position: <invalid>");
10181 if (ImGui::BeginPopupContextWindow())
10182 {
10183 if (ImGui::MenuItem("Custom", NULL, location == -1)) location = -1;
10184 if (ImGui::MenuItem("Center", NULL, location == -2)) location = -2;
10185 if (ImGui::MenuItem("Top-left", NULL, location == 0)) location = 0;
10186 if (ImGui::MenuItem("Top-right", NULL, location == 1)) location = 1;
10187 if (ImGui::MenuItem("Bottom-left", NULL, location == 2)) location = 2;
10188 if (ImGui::MenuItem("Bottom-right", NULL, location == 3)) location = 3;
10189 if (p_open && ImGui::MenuItem("Close")) *p_open = false;
10190 ImGui::EndPopup();
10191 }
10192 }
10193 ImGui::End();
10194}
10195
10196//-----------------------------------------------------------------------------
10197// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
10198//-----------------------------------------------------------------------------
10199
10200// Demonstrate creating a window covering the entire screen/viewport
10201static void ShowExampleAppFullscreen(bool* p_open)
10202{
10203 static bool use_work_area = true;
10204 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
10205
10206 // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
10207 // Based on your use case you may want one or the other.
10208 const ImGuiViewport* viewport = ImGui::GetMainViewport();
10209 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
10210 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
10211
10212 if (ImGui::Begin("Example: Fullscreen window", p_open, flags))
10213 {
10214 IMGUI_DEMO_MARKER("Examples/Fullscreen window");
10215 ImGui::Checkbox("Use work area instead of main area", &use_work_area);
10216 ImGui::SameLine();
10217 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.");
10218
10219 ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground);
10220 ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration);
10221 ImGui::Indent();
10222 ImGui::CheckboxFlags("ImGuiWindowFlags_NoTitleBar", &flags, ImGuiWindowFlags_NoTitleBar);
10223 ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse);
10224 ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar);
10225 ImGui::Unindent();
10226
10227 if (p_open && ImGui::Button("Close this window"))
10228 *p_open = false;
10229 }
10230 ImGui::End();
10231}
10232
10233//-----------------------------------------------------------------------------
10234// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()
10235//-----------------------------------------------------------------------------
10236
10237// Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation.
10238// This applies to all regular items as well.
10239// Read FAQ section "How can I have multiple widgets with the same label?" for details.
10240static void ShowExampleAppWindowTitles(bool*)
10241{
10242 const ImGuiViewport* viewport = ImGui::GetMainViewport();
10243 const ImVec2 base_pos = viewport->Pos;
10244
10245 // By default, Windows are uniquely identified by their title.
10246 // You can use the "##" and "###" markers to manipulate the display/ID.
10247
10248 // Using "##" to display same title but have unique identifier.
10249 ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver);
10250 ImGui::Begin("Same title as another window##1");
10251 IMGUI_DEMO_MARKER("Examples/Manipulating window titles##1");
10252 ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique.");
10253 ImGui::End();
10254
10255 ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver);
10256 ImGui::Begin("Same title as another window##2");
10257 IMGUI_DEMO_MARKER("Examples/Manipulating window titles##2");
10258 ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique.");
10259 ImGui::End();
10260
10261 // Using "###" to display a changing title but keep a static identifier "AnimatedTitle"
10262 char buf[128];
10263 sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount());
10264 ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver);
10265 ImGui::Begin(buf);
10266 IMGUI_DEMO_MARKER("Examples/Manipulating window titles##3");
10267 ImGui::Text("This window has a changing title.");
10268 ImGui::End();
10269}
10270
10271//-----------------------------------------------------------------------------
10272// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
10273//-----------------------------------------------------------------------------
10274
10275// Add a |_| looking shape
10276static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz)
10277{
10278 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 } };
10279 for (const ImVec2& p : pos_norms)
10280 draw_list->PathLineTo(ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y)));
10281}
10282
10283// Demonstrate using the low-level ImDrawList to draw custom shapes.
10284static void ShowExampleAppCustomRendering(bool* p_open)
10285{
10286 if (!ImGui::Begin("Example: Custom rendering", p_open))
10287 {
10288 ImGui::End();
10289 return;
10290 }
10291 IMGUI_DEMO_MARKER("Examples/Custom rendering");
10292
10293 // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
10294 // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
10295 // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
10296 // exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
10297
10298 if (ImGui::BeginTabBar("##TabBar"))
10299 {
10300 if (ImGui::BeginTabItem("Primitives"))
10301 {
10302 IMGUI_DEMO_MARKER("Examples/Custom rendering/Primitives");
10303 ImGui::PushItemWidth(-ImGui::GetFontSize() * 15);
10304 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10305
10306 // Draw gradients
10307 // (note that those are currently exacerbating our sRGB/Linear issues)
10308 // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..
10309 ImGui::Text("Gradients");
10310 ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
10311 {
10312 ImVec2 p0 = ImGui::GetCursorScreenPos();
10313 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
10314 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));
10315 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));
10316 draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
10317 ImGui::InvisibleButton("##gradient1", gradient_size);
10318 }
10319 {
10320 ImVec2 p0 = ImGui::GetCursorScreenPos();
10321 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
10322 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));
10323 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));
10324 draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
10325 ImGui::InvisibleButton("##gradient2", gradient_size);
10326 }
10327
10328 // Draw a bunch of primitives
10329 ImGui::Text("All primitives");
10330 static float sz = 36.0f;
10331 static float thickness = 3.0f;
10332 static int ngon_sides = 6;
10333 static bool circle_segments_override = false;
10334 static int circle_segments_override_v = 12;
10335 static bool curve_segments_override = false;
10336 static int curve_segments_override_v = 8;
10337 static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
10338 ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 100.0f, "%.0f");
10339 ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f");
10340 ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12);
10341 ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override);
10342 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
10343 circle_segments_override |= ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40);
10344 ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override);
10345 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
10346 curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40);
10347 ImGui::ColorEdit4("Color", &colf.x);
10348
10349 const ImVec2 p = ImGui::GetCursorScreenPos();
10350 const ImU32 col = ImColor(colf);
10351 const float spacing = 10.0f;
10352 const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;
10353 const float rounding = sz / 5.0f;
10354 const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;
10355 const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;
10356 const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves
10357 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) };
10358
10359 float x = p.x + 4.0f;
10360 float y = p.y + 4.0f;
10361 for (int n = 0; n < 2; n++)
10362 {
10363 // First line uses a thickness of 1.0f, second line uses the configurable thickness
10364 float th = (n == 0) ? 1.0f : thickness;
10365 draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon
10366 draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle
10367 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
10368 draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, th); x += sz + spacing; // Square
10369 draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th); x += sz + spacing; // Square with all rounded corners
10370 draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th, corners_tl_br); x += sz + spacing; // Square with two rounded corners
10371 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
10372 //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
10373 PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, th, ImDrawFlags_Closed); x += sz + spacing; // Concave Shape
10374 //draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th);
10375 draw_list->AddLineH(x, x + sz, y, col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
10376 draw_list->AddLineV(x, y, y + sz, col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
10377 draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line
10378
10379 // Path
10380 draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f);
10381 draw_list->PathStroke(col, th);
10382 x += sz + spacing;
10383
10384 // Quadratic Bezier Curve (3 control points)
10385 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);
10386 x += sz + spacing;
10387
10388 // Cubic Bezier Curve (4 control points)
10389 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);
10390
10391 x = p.x + 4;
10392 y += sz + spacing;
10393 }
10394
10395 // Filled shapes
10396 draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides); x += sz + spacing; // N-gon
10397 draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments); x += sz + spacing; // Circle
10398 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
10399 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square
10400 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners
10401 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
10402 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
10403 //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
10404 PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col); x += sz + spacing; // Concave shape
10405 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)
10406 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)
10407 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine)
10408
10409 // Path
10410 draw_list->PathArcTo(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, 3.141592f * -0.5f, 3.141592f);
10411 draw_list->PathFillConvex(col);
10412 x += sz + spacing;
10413
10414 // Quadratic Bezier Curve (3 control points)
10415 draw_list->PathLineTo(ImVec2(x + cp3[0].x, y + cp3[0].y));
10416 draw_list->PathBezierQuadraticCurveTo(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), curve_segments);
10417 draw_list->PathFillConvex(col);
10418 x += sz + spacing;
10419
10420 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));
10421 x += sz + spacing;
10422
10423 ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f));
10424 ImGui::PopItemWidth();
10425 ImGui::EndTabItem();
10426 }
10427
10428 if (ImGui::BeginTabItem("Canvas"))
10429 {
10430 IMGUI_DEMO_MARKER("Examples/Custom rendering/Canvas");
10431 static ImVector<ImVec2> points;
10432 static ImVec2 scrolling(0.0f, 0.0f);
10433 static bool opt_enable_grid = true;
10434 static bool opt_enable_context_menu = true;
10435 static bool adding_line = false;
10436
10437 ImGui::Checkbox("Enable grid", &opt_enable_grid);
10438 ImGui::Checkbox("Enable context menu", &opt_enable_context_menu);
10439 ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
10440
10441 // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
10442 // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
10443 // To use a child window instead we could use, e.g:
10444 // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
10445 // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
10446 // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);
10447 // ImGui::PopStyleColor();
10448 // ImGui::PopStyleVar();
10449 // [...]
10450 // ImGui::EndChild();
10451
10452 // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
10453 ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
10454 ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
10455 if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
10456 if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
10457 ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
10458
10459 // Draw border and background color
10460 ImGuiIO& io = ImGui::GetIO();
10461 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10462 draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));
10463 draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));
10464
10465 // This will catch our interactions
10466 ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
10467 const bool is_hovered = ImGui::IsItemHovered(); // Hovered
10468 const bool is_active = ImGui::IsItemActive(); // Held
10469 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
10470 const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
10471
10472 // Add first and second point
10473 if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
10474 {
10475 points.push_back(mouse_pos_in_canvas);
10476 points.push_back(mouse_pos_in_canvas);
10477 adding_line = true;
10478 }
10479 if (adding_line)
10480 {
10481 points.back() = mouse_pos_in_canvas;
10482 if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
10483 adding_line = false;
10484 }
10485
10486 // Pan (we use a zero mouse threshold when there's no context menu)
10487 // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
10488 const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;
10489 if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan))
10490 {
10491 scrolling.x += io.MouseDelta.x;
10492 scrolling.y += io.MouseDelta.y;
10493 }
10494
10495 // Context menu (under default mouse threshold)
10496 ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
10497 if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
10498 ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
10499 if (ImGui::BeginPopup("context"))
10500 {
10501 if (adding_line)
10502 points.resize(points.size() - 2);
10503 adding_line = false;
10504 if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); }
10505 if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0)) { points.clear(); }
10506 ImGui::EndPopup();
10507 }
10508
10509 // Draw grid + all lines in the canvas
10510 draw_list->PushClipRect(canvas_p0, canvas_p1, true);
10511 if (opt_enable_grid)
10512 {
10513 const float GRID_STEP = 64.0f;
10514 for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
10515 draw_list->AddLineV(canvas_p0.x + x, canvas_p0.y, canvas_p1.y, IM_COL32(200, 200, 200, 40));
10516 for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
10517 draw_list->AddLineH(canvas_p0.x, canvas_p1.x, canvas_p0.y + y, IM_COL32(200, 200, 200, 40));
10518 }
10519 for (int n = 0; n < points.Size; n += 2)
10520 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);
10521 draw_list->PopClipRect();
10522
10523 ImGui::EndTabItem();
10524 }
10525
10526 if (ImGui::BeginTabItem("BG/FG draw lists"))
10527 {
10528 IMGUI_DEMO_MARKER("Examples/Custom rendering/BG & FG draw lists");
10529 static bool draw_bg = true;
10530 static bool draw_fg = true;
10531 ImGui::Checkbox("Draw in Background draw list", &draw_bg);
10532 ImGui::SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows.");
10533 ImGui::Checkbox("Draw in Foreground draw list", &draw_fg);
10534 ImGui::SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows.");
10535 ImVec2 window_pos = ImGui::GetWindowPos();
10536 ImVec2 window_size = ImGui::GetWindowSize();
10537 ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);
10538 if (draw_bg)
10539 ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4);
10540 if (draw_fg)
10541 ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10);
10542 ImGui::EndTabItem();
10543 }
10544
10545 // Demonstrate out-of-order rendering via channels splitting
10546 // We use functions in ImDrawList as each draw list contains a convenience splitter,
10547 // but you can also instantiate your own ImDrawListSplitter if you need to nest them.
10548 if (ImGui::BeginTabItem("Draw Channels"))
10549 {
10550 IMGUI_DEMO_MARKER("Examples/Custom rendering/Draw Channels");
10551 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10552 {
10553 ImGui::Text("Blue shape is drawn first: appears in back");
10554 ImGui::Text("Red shape is drawn after: appears in front");
10555 ImVec2 p0 = ImGui::GetCursorScreenPos();
10556 draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10557 draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10558 ImGui::Dummy(ImVec2(75, 75));
10559 }
10560 ImGui::Separator();
10561 {
10562 ImGui::Text("Blue shape is drawn first, into channel 1: appears in front");
10563 ImGui::Text("Red shape is drawn after, into channel 0: appears in back");
10564 ImVec2 p1 = ImGui::GetCursorScreenPos();
10565
10566 // Create 2 channels and draw a Blue shape THEN a Red shape.
10567 // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.
10568 draw_list->ChannelsSplit(2);
10569 draw_list->ChannelsSetCurrent(1);
10570 draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10571 draw_list->ChannelsSetCurrent(0);
10572 draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10573
10574 // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.
10575 // This works by copying draw indices only (vertices are not copied).
10576 draw_list->ChannelsMerge();
10577 ImGui::Dummy(ImVec2(75, 75));
10578 ImGui::Text("After reordering, contents of channel 0 appears below channel 1.");
10579 }
10580 ImGui::EndTabItem();
10581 }
10582
10583 ImGui::EndTabBar();
10584 }
10585
10586 ImGui::End();
10587}
10588
10589//-----------------------------------------------------------------------------
10590// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
10591//-----------------------------------------------------------------------------
10592
10593struct ImGuiDemoDockspaceArgs
10594{
10595 bool IsFullscreen = true;
10596 bool KeepWindowPadding = false; // Keep WindowPadding to help understand that DockSpace() is a widget inside the window.
10597 ImGuiDockNodeFlags DockSpaceFlags = ImGuiDockNodeFlags_None;
10598};
10599
10600// THIS IS A DEMO FOR ADVANCED USAGE OF DockSpace().
10601// MOST REGULAR APPLICATIONS WANTING TO ALLOW DOCKING WINDOWS ON THE EDGE OF YOUR SCREEN CAN SIMPLY USE:
10602// ImGui::NewFrame(); + ImGui::DockSpaceOverViewport(); // Create a dockspace in main viewport
10603// OR:
10604// ImGui::NewFrame(); + ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Create a dockspace in main viewport, where central node is transparent.
10605// Demonstrate using DockSpace() to create an explicit docking node within an existing window, with various options.
10606// Read https://github.com/ocornut/imgui/wiki/Docking for details.
10607// The reasons we do not use DockSpaceOverViewport() in this demo is because:
10608// - (1) we allow the host window to be floating/moveable instead of filling the viewport (when args->IsFullscreen == false)
10609// which is mostly to showcase the idea that DockSpace() may be submitted anywhere.
10610// Also see 'Demo->Examples->Documents' for a less abstract version of this.
10611// - (2) we allow the host window to have padding (when args->UsePadding == true)
10612// - (3) we expose variety of other flags.
10613static void ShowExampleAppDockSpaceAdvanced(ImGuiDemoDockspaceArgs* args, bool* p_open)
10614{
10615 ImGuiDockNodeFlags dockspace_flags = args->DockSpaceFlags;
10616
10617 // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
10618 // because it would be confusing to have two docking targets within each others.
10619 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;
10620 if (args->IsFullscreen)
10621 {
10622 // Fullscreen dockspace: practically the same as calling DockSpaceOverViewport();
10623 const ImGuiViewport* viewport = ImGui::GetMainViewport();
10624 ImGui::SetNextWindowPos(viewport->WorkPos);
10625 ImGui::SetNextWindowSize(viewport->WorkSize);
10626 ImGui::SetNextWindowViewport(viewport->ID);
10627 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
10628 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
10629 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
10630 window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
10631 window_flags |= ImGuiWindowFlags_NoBackground;
10632 }
10633 else
10634 {
10635 // Floating dockspace
10636 dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
10637 }
10638
10639 // Important: note that we proceed even if Begin() returns false (aka window is collapsed).
10640 // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
10641 // all active windows docked into it will lose their parent and become undocked.
10642 // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
10643 // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
10644 if (!args->KeepWindowPadding)
10645 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
10646 ImGui::Begin("Window with a DockSpace", p_open, window_flags);
10647 if (!args->KeepWindowPadding)
10648 ImGui::PopStyleVar();
10649
10650 if (args->IsFullscreen)
10651 ImGui::PopStyleVar(2);
10652
10653 // Submit the DockSpace widget inside our window
10654 // - Note that the id here is different from the one used by DockSpaceOverViewport(), so docking state won't get transfered between "Basic" and "Advanced" demos.
10655 // - If we made the ShowExampleAppDockSpaceBasic() calculate its own ID and pass it to DockSpaceOverViewport() the ID could easily match.
10656 ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
10657 ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
10658
10659 ImGui::End();
10660}
10661
10662static void ShowExampleAppDockSpaceBasic(ImGuiDockNodeFlags flags)
10663{
10664 // Basic version which you can use in many apps:
10665 // e.g:
10666 // ImGui::DockSpaceOverViewport();
10667 // or:
10668 // ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Central node will be transparent
10669 // or:
10670 // ImGuiViewport* viewport = ImGui::GetMainViewport();
10671 // ImGui::DockSpaceOverViewport(0, viewport, ImGuiDockNodeFlags_None);
10672
10673 ImGui::DockSpaceOverViewport(0, nullptr, flags);
10674}
10675
10676void ShowExampleAppDockSpace(bool* p_open)
10677{
10678 static int opt_demo_mode = 0;
10679 static bool opt_demo_mode_changed = false;
10680 static ImGuiDemoDockspaceArgs args;
10681
10682 if (opt_demo_mode == 0)
10683 ShowExampleAppDockSpaceBasic(args.DockSpaceFlags);
10684 else
10685 ShowExampleAppDockSpaceAdvanced(&args, p_open);
10686
10687 // Refocus our window to minimize perceived loss of focus when changing mode (caused by the fact that each use a different window, which would not happen in a real app)
10688 if (opt_demo_mode_changed)
10689 ImGui::SetNextWindowFocus();
10690 ImGui::Begin("Examples: Dockspace", p_open, ImGuiWindowFlags_MenuBar);
10691 opt_demo_mode_changed = false;
10692 opt_demo_mode_changed |= ImGui::RadioButton("Basic demo mode", &opt_demo_mode, 0);
10693 opt_demo_mode_changed |= ImGui::RadioButton("Advanced demo mode", &opt_demo_mode, 1);
10694
10695 ImGui::SeparatorText("Options");
10696
10697 if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) == 0)
10698 {
10699 ShowDockingDisabledMessage();
10700 }
10701 else if (opt_demo_mode == 0)
10702 {
10703 args.DockSpaceFlags &= ImGuiDockNodeFlags_PassthruCentralNode; // Allowed flags
10704 ImGui::CheckboxFlags("Flag: PassthruCentralNode", &args.DockSpaceFlags, ImGuiDockNodeFlags_PassthruCentralNode);
10705 }
10706 else if (opt_demo_mode == 1)
10707 {
10708 ImGui::Checkbox("Fullscreen", &args.IsFullscreen);
10709 ImGui::Checkbox("Keep Window Padding", &args.KeepWindowPadding);
10710 ImGui::SameLine();
10711 HelpMarker("This is mostly exposed to facilitate understanding that a DockSpace() is _inside_ a window.");
10712 ImGui::BeginDisabled(args.IsFullscreen == false);
10713 ImGui::CheckboxFlags("Flag: PassthruCentralNode", &args.DockSpaceFlags, ImGuiDockNodeFlags_PassthruCentralNode);
10714 ImGui::EndDisabled();
10715 ImGui::CheckboxFlags("Flag: NoDockingOverCentralNode", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoDockingOverCentralNode);
10716 ImGui::CheckboxFlags("Flag: NoDockingSplit", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoDockingSplit);
10717 ImGui::CheckboxFlags("Flag: NoUndocking", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoUndocking);
10718 ImGui::CheckboxFlags("Flag: NoResize", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoResize);
10719 ImGui::CheckboxFlags("Flag: AutoHideTabBar", &args.DockSpaceFlags, ImGuiDockNodeFlags_AutoHideTabBar);
10720 }
10721
10722 // Show demo options and help
10723 if (ImGui::BeginMenuBar())
10724 {
10725 if (ImGui::BeginMenu("Help"))
10726 {
10727 ImGui::TextUnformatted(
10728 "This demonstrates the use of ImGui::DockSpace() which allows you to manually\ncreate a docking node _within_ another window." "\n"
10729 "The \"Basic\" version uses the ImGui::DockSpaceOverViewport() helper. Most applications can probably use this.");
10730 ImGui::Separator();
10731 ImGui::TextUnformatted("When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n"
10732 "- Drag from window title bar or their tab to dock/undock." "\n"
10733 "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n"
10734 "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n"
10735 "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)");
10736 ImGui::Separator();
10737 ImGui::TextUnformatted("More details:"); ImGui::Bullet(); ImGui::SameLine(); ImGui::TextLinkOpenURL("Docking Wiki page", "https://github.com/ocornut/imgui/wiki/Docking");
10738 ImGui::BulletText("Read comments in ShowExampleAppDockSpace()");
10739 ImGui::EndMenu();
10740 }
10741 ImGui::EndMenuBar();
10742 }
10743
10744 ImGui::End();
10745}
10746
10747//-----------------------------------------------------------------------------
10748// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
10749//-----------------------------------------------------------------------------
10750
10751// Simplified structure to mimic a Document model
10752struct MyDocument
10753{
10754 char Name[32]; // Document title
10755 int UID; // Unique ID (necessary as we can change title)
10756 bool Open; // Set when open (we keep an array of all available documents to simplify demo code!)
10757 bool OpenPrev; // Copy of Open from last update.
10758 bool Dirty; // Set when the document has been modified
10759 ImVec4 Color; // An arbitrary variable associated to the document
10760
10761 MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
10762 {
10763 UID = uid;
10764 snprintf(Name, sizeof(Name), "%s", name);
10765 Open = OpenPrev = open;
10766 Dirty = false;
10767 Color = color;
10768 }
10769 void DoOpen() { Open = true; }
10770 void DoForceClose() { Open = false; Dirty = false; }
10771 void DoSave() { Dirty = false; }
10772};
10773
10774struct ExampleAppDocuments
10775{
10776 ImVector<MyDocument> Documents;
10777 ImVector<MyDocument*> CloseQueue;
10778 MyDocument* RenamingDoc = NULL;
10779 bool RenamingStarted = false;
10780
10781 ExampleAppDocuments()
10782 {
10783 Documents.push_back(MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
10784 Documents.push_back(MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
10785 Documents.push_back(MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
10786 Documents.push_back(MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));
10787 Documents.push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f)));
10788 Documents.push_back(MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f)));
10789 }
10790
10791 // As we allow to change document name, we append a never-changing document ID so tabs are stable
10792 void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size)
10793 {
10794 snprintf(out_buf, out_buf_size, "%s###doc%d", doc->Name, doc->UID);
10795 }
10796
10797 // Display placeholder contents for the Document
10798 void DisplayDocContents(MyDocument* doc)
10799 {
10800 ImGui::PushID(doc);
10801 ImGui::Text("Document \"%s\"", doc->Name);
10802 ImGui::PushStyleColor(ImGuiCol_Text, doc->Color);
10803 ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
10804 ImGui::PopStyleColor();
10805
10806 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip);
10807 if (ImGui::Button("Rename.."))
10808 {
10809 RenamingDoc = doc;
10810 RenamingStarted = true;
10811 }
10812 ImGui::SameLine();
10813
10814 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip);
10815 if (ImGui::Button("Modify"))
10816 doc->Dirty = true;
10817
10818 ImGui::SameLine();
10819 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip);
10820 if (ImGui::Button("Save"))
10821 doc->DoSave();
10822
10823 ImGui::SameLine();
10824 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip);
10825 if (ImGui::Button("Close"))
10826 CloseQueue.push_back(doc);
10827 ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior.
10828 ImGui::PopID();
10829 }
10830
10831 // Display context menu for the Document
10832 void DisplayDocContextMenu(MyDocument* doc)
10833 {
10834 if (!ImGui::BeginPopupContextItem())
10835 return;
10836
10837 char buf[256];
10838 sprintf(buf, "Save %s", doc->Name);
10839 if (ImGui::MenuItem(buf, "Ctrl+S", false, doc->Open))
10840 doc->DoSave();
10841 if (ImGui::MenuItem("Rename...", "Ctrl+R", false, doc->Open))
10842 RenamingDoc = doc;
10843 if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open))
10844 CloseQueue.push_back(doc);
10845 ImGui::EndPopup();
10846 }
10847
10848 // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
10849 // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
10850 // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
10851 // 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
10852 // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
10853 // give the impression of a flicker for one frame.
10854 // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
10855 // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
10856 void NotifyOfDocumentsClosedElsewhere()
10857 {
10858 for (MyDocument& doc : Documents)
10859 {
10860 if (!doc.Open && doc.OpenPrev)
10861 ImGui::SetTabItemClosed(doc.Name);
10862 doc.OpenPrev = doc.Open;
10863 }
10864 }
10865};
10866
10867void ShowExampleAppDocuments(bool* p_open)
10868{
10869 static ExampleAppDocuments app;
10870
10871 // Options
10872 enum Target
10873 {
10874 Target_None,
10875 Target_Tab, // Create documents as local tab into a local tab bar
10876 Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace
10877 };
10878 static Target opt_target = Target_Tab;
10879 static bool opt_reorderable = true;
10880 static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;
10881
10882 // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant")
10883 // that we emit gets docked into the same spot as the parent window ("Example: Documents").
10884 // This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab
10885 // not visible, which in turn would stop submitting the "Eggplant" window.
10886 // We avoid this problem by submitting our documents window even if our parent window is not currently visible.
10887 // Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking.
10888
10889 bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar);
10890 if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow)
10891 {
10892 ImGui::End();
10893 return;
10894 }
10895 IMGUI_DEMO_MARKER("Examples/Documents");
10896
10897 // Menu
10898 if (ImGui::BeginMenuBar())
10899 {
10900 if (ImGui::BeginMenu("File"))
10901 {
10902 int open_count = 0;
10903 for (MyDocument& doc : app.Documents)
10904 open_count += doc.Open ? 1 : 0;
10905
10906 if (ImGui::BeginMenu("Open", open_count < app.Documents.Size))
10907 {
10908 for (MyDocument& doc : app.Documents)
10909 if (!doc.Open && ImGui::MenuItem(doc.Name))
10910 doc.DoOpen();
10911 ImGui::EndMenu();
10912 }
10913 if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0))
10914 for (MyDocument& doc : app.Documents)
10915 app.CloseQueue.push_back(&doc);
10916 if (ImGui::MenuItem("Exit") && p_open)
10917 *p_open = false;
10918 ImGui::EndMenu();
10919 }
10920 ImGui::EndMenuBar();
10921 }
10922
10923 // [Debug] List documents with one checkbox for each
10924 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
10925 {
10926 MyDocument& doc = app.Documents[doc_n];
10927 if (doc_n > 0)
10928 ImGui::SameLine();
10929 ImGui::PushID(&doc);
10930 if (ImGui::Checkbox(doc.Name, &doc.Open))
10931 if (!doc.Open)
10932 doc.DoForceClose();
10933 ImGui::PopID();
10934 }
10935 ImGui::PushItemWidth(ImGui::GetFontSize() * 12);
10936 ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0DockSpace+Window\0");
10937 ImGui::PopItemWidth();
10938 bool redock_all = false;
10939 if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); }
10940 if (opt_target == Target_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); }
10941
10942 ImGui::Separator();
10943
10944 // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.
10945 // They have multiple effects:
10946 // - Display a dot next to the title.
10947 // - Tab is selected when clicking the X close button.
10948 // - Closure is not assumed (will wait for user to stop submitting the tab).
10949 // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
10950 // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty
10951 // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.
10952 // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
10953
10954 // Tabs
10955 if (opt_target == Target_Tab)
10956 {
10957 ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);
10958 tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;
10959 if (ImGui::BeginTabBar("##tabs", tab_bar_flags))
10960 {
10961 if (opt_reorderable)
10962 app.NotifyOfDocumentsClosedElsewhere();
10963
10964 // [DEBUG] Stress tests
10965 //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.
10966 //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..
10967
10968 // Submit Tabs
10969 for (MyDocument& doc : app.Documents)
10970 {
10971 if (!doc.Open)
10972 continue;
10973
10974 // As we allow to change document name, we append a never-changing document id so tabs are stable
10975 char doc_name_buf[64];
10976 app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf));
10977 ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);
10978 bool visible = ImGui::BeginTabItem(doc_name_buf, &doc.Open, tab_flags);
10979
10980 // Cancel attempt to close when unsaved add to save queue so we can display a popup.
10981 if (!doc.Open && doc.Dirty)
10982 {
10983 doc.Open = true;
10984 app.CloseQueue.push_back(&doc);
10985 }
10986
10987 app.DisplayDocContextMenu(&doc);
10988 if (visible)
10989 {
10990 app.DisplayDocContents(&doc);
10991 ImGui::EndTabItem();
10992 }
10993 }
10994
10995 ImGui::EndTabBar();
10996 }
10997 }
10998 else if (opt_target == Target_DockSpaceAndWindow)
10999 {
11000 if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)
11001 {
11002 app.NotifyOfDocumentsClosedElsewhere();
11003
11004 // Create a DockSpace node where any window can be docked
11005 ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
11006 ImGui::DockSpace(dockspace_id);
11007
11008 // Create Windows
11009 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
11010 {
11011 MyDocument* doc = &app.Documents[doc_n];
11012 if (!doc->Open)
11013 continue;
11014
11015 ImGui::SetNextWindowDockID(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver);
11016 ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0);
11017 bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags);
11018
11019 // Cancel attempt to close when unsaved add to save queue so we can display a popup.
11020 if (!doc->Open && doc->Dirty)
11021 {
11022 doc->Open = true;
11023 app.CloseQueue.push_back(doc);
11024 }
11025
11026 app.DisplayDocContextMenu(doc);
11027 if (visible)
11028 app.DisplayDocContents(doc);
11029
11030 ImGui::End();
11031 }
11032 }
11033 else
11034 {
11035 ShowDockingDisabledMessage();
11036 }
11037 }
11038
11039 // Early out other contents
11040 if (!window_contents_visible)
11041 {
11042 ImGui::End();
11043 return;
11044 }
11045
11046 // Display renaming UI
11047 if (app.RenamingDoc != NULL)
11048 {
11049 if (app.RenamingStarted)
11050 ImGui::OpenPopup("Rename");
11051 if (ImGui::BeginPopup("Rename"))
11052 {
11053 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30);
11054 if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_COUNTOF(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue))
11055 {
11056 ImGui::CloseCurrentPopup();
11057 app.RenamingDoc = NULL;
11058 }
11059 if (app.RenamingStarted)
11060 ImGui::SetKeyboardFocusHere(-1);
11061 ImGui::EndPopup();
11062 }
11063 else
11064 {
11065 app.RenamingDoc = NULL;
11066 }
11067 app.RenamingStarted = false;
11068 }
11069
11070 // Display closing confirmation UI
11071 if (!app.CloseQueue.empty())
11072 {
11073 int close_queue_unsaved_documents = 0;
11074 for (int n = 0; n < app.CloseQueue.Size; n++)
11075 if (app.CloseQueue[n]->Dirty)
11076 close_queue_unsaved_documents++;
11077
11078 if (close_queue_unsaved_documents == 0)
11079 {
11080 // Close documents when all are unsaved
11081 for (int n = 0; n < app.CloseQueue.Size; n++)
11082 app.CloseQueue[n]->DoForceClose();
11083 app.CloseQueue.clear();
11084 }
11085 else
11086 {
11087 if (!ImGui::IsPopupOpen("Save?"))
11088 ImGui::OpenPopup("Save?");
11089 if (ImGui::BeginPopupModal("Save?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
11090 {
11091 ImGui::Text("Save change to the following items?");
11092 float item_height = ImGui::GetTextLineHeightWithSpacing();
11093 if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle))
11094 for (MyDocument* doc : app.CloseQueue)
11095 if (doc->Dirty)
11096 ImGui::Text("%s", doc->Name);
11097 ImGui::EndChild();
11098
11099 ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
11100 if (ImGui::Button("Yes", button_size))
11101 {
11102 for (MyDocument* doc : app.CloseQueue)
11103 {
11104 if (doc->Dirty)
11105 doc->DoSave();
11106 doc->DoForceClose();
11107 }
11108 app.CloseQueue.clear();
11109 ImGui::CloseCurrentPopup();
11110 }
11111 ImGui::SameLine();
11112 if (ImGui::Button("No", button_size))
11113 {
11114 for (MyDocument* doc : app.CloseQueue)
11115 doc->DoForceClose();
11116 app.CloseQueue.clear();
11117 ImGui::CloseCurrentPopup();
11118 }
11119 ImGui::SameLine();
11120 if (ImGui::Button("Cancel", button_size))
11121 {
11122 app.CloseQueue.clear();
11123 ImGui::CloseCurrentPopup();
11124 }
11125 ImGui::EndPopup();
11126 }
11127 }
11128 }
11129
11130 ImGui::End();
11131}
11132
11133//-----------------------------------------------------------------------------
11134// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
11135//-----------------------------------------------------------------------------
11136
11137//#include "imgui_internal.h" // NavMoveRequestTryWrapping()
11138
11139struct ExampleAsset
11140{
11141 ImGuiID ID;
11142 int Type;
11143
11144 ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; }
11145
11146 static const ImGuiTableSortSpecs* s_current_sort_specs;
11147
11148 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count)
11149 {
11150 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
11151 if (items_count > 1)
11152 qsort(items, (size_t)items_count, sizeof(items[0]), ExampleAsset::CompareWithSortSpecs);
11153 s_current_sort_specs = NULL;
11154 }
11155
11156 // Compare function to be used by qsort()
11157 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
11158 {
11159 const ExampleAsset* a = (const ExampleAsset*)lhs;
11160 const ExampleAsset* b = (const ExampleAsset*)rhs;
11161 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
11162 {
11163 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
11164 int delta = 0;
11165 if (sort_spec->ColumnIndex == 0)
11166 delta = ((int)a->ID - (int)b->ID);
11167 else if (sort_spec->ColumnIndex == 1)
11168 delta = (a->Type - b->Type);
11169 if (delta > 0)
11170 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
11171 if (delta < 0)
11172 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
11173 }
11174 return (int)a->ID - (int)b->ID;
11175 }
11176};
11177const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL;
11178
11179struct ExampleAssetsBrowser
11180{
11181 // Options
11182 bool ShowTypeOverlay = true;
11183 bool AllowSorting = true;
11184 bool AllowBoxSelect = true; // Will set ImGuiMultiSelectFlags_BoxSelect2d
11185 bool AllowBoxSelectInsideSelection = false; // Will set ImGuiMultiSelectFlags_SelectOnClickAlways
11186 bool AllowDragUnselected = false; // Will set ImGuiMultiSelectFlags_SelectOnClickRelease
11187 float IconSize = 0;
11188 int IconSpacing = 10;
11189 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.
11190 bool StretchSpacing = true;
11191 bool UseScrollX = false; // Debug: submit twice the number of items per line (overflow horizontally to exercise ScrollX + box-select)
11192
11193 // State
11194 ImVector<ExampleAsset> Items; // Our items
11195 ExampleSelectionWithDeletion Selection; // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion)
11196 ImGuiID NextItemId = 0; // Unique identifier when creating new items
11197 bool RequestDelete = false; // Deferred deletion request
11198 bool RequestSort = false; // Deferred sort request
11199 float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better
11200
11201 // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way.
11202 ImVec2 LayoutItemSize;
11203 ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing
11204 float LayoutItemSpacing = 0.0f;
11205 float LayoutSelectableSpacing = 0.0f;
11206 float LayoutOuterPadding = 0.0f;
11207 int LayoutColumnCount = 0;
11208 int LayoutLineCount = 0;
11209
11210 // Functions
11211 ExampleAssetsBrowser()
11212 {
11213 AddItems(10000);
11214 }
11215 void AddItems(int count)
11216 {
11217 if (Items.Size == 0)
11218 NextItemId = 0;
11219 Items.reserve(Items.Size + count);
11220 for (int n = 0; n < count; n++, NextItemId++)
11221 Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2));
11222 RequestSort = true;
11223 }
11224 void ClearItems()
11225 {
11226 Items.clear();
11227 Selection.Clear();
11228 }
11229
11230 // Logic would be written in the main code BeginChild() and outputting to local variables.
11231 // We extracted it into a function so we can call it easily from multiple places.
11232 void UpdateLayoutSizes(float avail_width)
11233 {
11234 // Layout: when not stretching: allow extending into right-most spacing.
11235 LayoutItemSpacing = (float)IconSpacing;
11236 if (StretchSpacing == false)
11237 avail_width += floorf(LayoutItemSpacing * 0.5f);
11238
11239 // Layout: calculate number of icon per line and number of lines
11240 LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize));
11241 LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);
11242
11243 // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.
11244 if (StretchSpacing && LayoutColumnCount > 1)
11245 LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount;
11246
11247 if (UseScrollX)
11248 LayoutColumnCount *= 2;
11249 LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;
11250
11251 LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing);
11252 LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);
11253 LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f);
11254 }
11255
11256 void Draw(const char* title, bool* p_open)
11257 {
11258 if (IconSize <= 0.0f)
11259 IconSize = ImGui::CalcTextSize("99999").x;
11260
11261 ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver);
11262 if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar))
11263 {
11264 ImGui::End();
11265 return;
11266 }
11267 IMGUI_DEMO_MARKER("Examples/Assets Browser");
11268
11269 // Menu bar
11270 if (ImGui::BeginMenuBar())
11271 {
11272 if (ImGui::BeginMenu("File"))
11273 {
11274 if (ImGui::MenuItem("Add 10000 items"))
11275 AddItems(10000);
11276 if (ImGui::MenuItem("Clear items"))
11277 ClearItems();
11278 ImGui::Separator();
11279 if (ImGui::MenuItem("Close", NULL, false, p_open != NULL))
11280 *p_open = false;
11281 ImGui::EndMenu();
11282 }
11283 if (ImGui::BeginMenu("Edit"))
11284 {
11285 if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0))
11286 RequestDelete = true;
11287 ImGui::EndMenu();
11288 }
11289 if (ImGui::BeginMenu("Options"))
11290 {
11291 ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
11292
11293 ImGui::SeparatorText("Contents");
11294 ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay);
11295 ImGui::Checkbox("Allow Sorting", &AllowSorting);
11296
11297 ImGui::SeparatorText("Selection Behavior");
11298 ImGui::Checkbox("Allow box-selection", &AllowBoxSelect);
11299 if (ImGui::Checkbox("Allow box-selection from selected items", &AllowBoxSelectInsideSelection) && AllowBoxSelectInsideSelection)
11300 AllowDragUnselected = false;
11301 if (ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected) && AllowDragUnselected)
11302 AllowBoxSelectInsideSelection = false;
11303
11304 ImGui::SeparatorText("Layout");
11305 ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f");
11306 ImGui::SameLine(); HelpMarker("Use Ctrl+Wheel to zoom");
11307 ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32);
11308 ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32);
11309 ImGui::Checkbox("Stretch Spacing", &StretchSpacing);
11310 ImGui::Checkbox("Use ScrollX", &UseScrollX);
11311 ImGui::PopItemWidth();
11312 ImGui::EndMenu();
11313 }
11314 ImGui::EndMenuBar();
11315 }
11316
11317 // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI
11318 if (AllowSorting)
11319 {
11320 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
11321 ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders;
11322 if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight())))
11323 {
11324 ImGui::TableSetupColumn("Index");
11325 ImGui::TableSetupColumn("Type");
11326 ImGui::TableHeadersRow();
11327 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
11328 if (sort_specs->SpecsDirty || RequestSort)
11329 {
11330 ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size);
11331 sort_specs->SpecsDirty = RequestSort = false;
11332 }
11333 ImGui::EndTable();
11334 }
11335 ImGui::PopStyleVar();
11336 }
11337
11338 ImGuiIO& io = ImGui::GetIO();
11339 ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));
11340 if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_HorizontalScrollbar))
11341 {
11342 ImDrawList* draw_list = ImGui::GetWindowDrawList();
11343
11344 const float avail_width = ImGui::GetContentRegionAvail().x;
11345 UpdateLayoutSizes(avail_width);
11346
11347 // Calculate and store start position.
11348 ImVec2 start_pos = ImGui::GetCursorScreenPos();
11349 start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding);
11350 ImGui::SetCursorScreenPos(start_pos);
11351
11352 // Multi-select
11353 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid;
11354
11355 // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items)
11356 if (AllowBoxSelect)
11357 ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d;
11358
11359 // - Selection mode
11360 if (AllowDragUnselected)
11361 ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // Rarely used: Allows dragging an unselected item without selecting it(rarely used)
11362 else if (AllowBoxSelectInsideSelection)
11363 ms_flags |= ImGuiMultiSelectFlags_SelectOnClickAlways; // Rarely used: Prevents Drag and Drop from being used on multiple-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection.
11364
11365 // - Enable keyboard wrapping on X axis
11366 // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing:
11367 // ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
11368 // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system)
11369 ms_flags |= ImGuiMultiSelectFlags_NavWrapX;
11370
11371 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size);
11372
11373 // Use custom selection adapter: store ID in selection (recommended)
11374 Selection.UserData = this;
11375 Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; };
11376 Selection.ApplyRequests(ms_io);
11377
11378 const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete;
11379 const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1;
11380 RequestDelete = false;
11381
11382 // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items)
11383 // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...
11384 // But it is necessary for two reasons:
11385 // - Selectables uses it by default to visually fill the space between two items.
11386 // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do).
11387 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing));
11388
11389 // Rendering parameters
11390 const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) };
11391 const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220));
11392 const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f);
11393 const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x);
11394
11395 const int column_count = LayoutColumnCount;
11396 ImGuiListClipper clipper;
11397 clipper.Begin(LayoutLineCount, LayoutItemStep.y);
11398 if (item_curr_idx_to_focus != -1)
11399 clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped.
11400 if (ms_io->RangeSrcItem != -1)
11401 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped.
11402 while (clipper.Step())
11403 {
11404 for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++)
11405 {
11406 const int item_min_idx_for_current_line = line_idx * column_count;
11407 const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size);
11408 for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx)
11409 {
11410 ExampleAsset* item_data = &Items[item_idx];
11411 ImGui::PushID((int)item_data->ID);
11412
11413 // Position item
11414 ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y);
11415 ImGui::SetCursorScreenPos(pos);
11416
11417 ImGui::SetNextItemSelectionUserData(item_idx);
11418 bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID);
11419 bool item_is_visible = ImGui::IsRectVisible(LayoutItemSize);
11420 ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize);
11421
11422 // Update our selection state immediately (without waiting for EndMultiSelect() requests)
11423 // because we use this to alter the color of our text/icon.
11424 if (ImGui::IsItemToggledSelection())
11425 item_is_selected = !item_is_selected;
11426
11427 // Focus (for after deletion)
11428 if (item_curr_idx_to_focus == item_idx)
11429 ImGui::SetKeyboardFocusHere(-1);
11430
11431 // Drag and drop
11432 if (ImGui::BeginDragDropSource())
11433 {
11434 // Create payload with full selection OR single unselected item.
11435 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
11436 if (ImGui::GetDragDropPayload() == NULL)
11437 {
11438 ImVector<ImGuiID> payload_items;
11439 void* it = NULL;
11440 ImGuiID id = 0;
11441 if (!item_is_selected)
11442 payload_items.push_back(item_data->ID);
11443 else
11444 while (Selection.GetNextSelectedItem(&it, &id))
11445 payload_items.push_back(id);
11446 ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
11447 }
11448
11449 // Display payload content in tooltip, by extracting it from the payload data
11450 // (we could read from selection, but it is more correct and reusable to read from payload)
11451 const ImGuiPayload* payload = ImGui::GetDragDropPayload();
11452 const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID);
11453 ImGui::Text("%d assets", payload_count);
11454
11455 ImGui::EndDragDropSource();
11456 }
11457
11458 // Render icon (a real app would likely display an image/thumbnail here)
11459 // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well.
11460 if (item_is_visible)
11461 {
11462 ImVec2 box_min(pos.x - 1, pos.y - 1);
11463 ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious
11464 draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color
11465 if (ShowTypeOverlay && item_data->Type != 0)
11466 {
11467 ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_COUNTOF(icon_type_overlay_colors)];
11468 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);
11469 }
11470 if (display_label)
11471 {
11472 ImU32 label_col = ImGui::GetColorU32(item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled);
11473 char label[32];
11474 sprintf(label, "%d", item_data->ID);
11475 draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label);
11476 }
11477 }
11478
11479 ImGui::PopID();
11480 }
11481 }
11482 }
11483 clipper.End();
11484 if (Items.Size == 0)
11485 ImGui::Dummy(ImVec2(0, 0));
11486 ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
11487
11488 // Context menu
11489 if (ImGui::BeginPopupContextWindow())
11490 {
11491 ImGui::Text("Selection: %d items", Selection.Size);
11492 ImGui::Separator();
11493 if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0))
11494 RequestDelete = true;
11495 ImGui::EndPopup();
11496 }
11497
11498 ms_io = ImGui::EndMultiSelect();
11499 Selection.ApplyRequests(ms_io);
11500 if (want_delete)
11501 Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus);
11502
11503 // Zooming with Ctrl+Wheel
11504 if (ImGui::IsWindowAppearing())
11505 ZoomWheelAccum = 0.0f;
11506 if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false)
11507 {
11508 ZoomWheelAccum += io.MouseWheel;
11509 if (fabsf(ZoomWheelAccum) >= 1.0f)
11510 {
11511 // Calculate hovered item index from mouse location
11512 // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it.
11513 const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x;
11514 const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y;
11515 const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx;
11516 //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
11517
11518 // Zoom
11519 IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum);
11520 IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f);
11521 ZoomWheelAccum -= (int)ZoomWheelAccum;
11522 UpdateLayoutSizes(avail_width);
11523
11524 // Manipulate scroll to that we will land at the same Y location of currently hovered item.
11525 // - Calculate next frame position of item under mouse
11526 // - Set new scroll position to be used in next ImGui::BeginChild() call.
11527 float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(hovered_item_ny, 1.0f)) * LayoutItemStep.y;
11528 hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y;
11529 float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y;
11530 ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y);
11531 }
11532 }
11533 }
11534 ImGui::EndChild();
11535
11536 ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size);
11537 ImGui::End();
11538 }
11539};
11540
11541void ShowExampleAppAssetsBrowser(bool* p_open)
11542{
11543 IMGUI_DEMO_MARKER("Examples/Assets Browser");
11544 static ExampleAssetsBrowser assets_browser;
11545 assets_browser.Draw("Example: Assets Browser", p_open);
11546}
11547
11548// End of Demo code
11549#else
11550
11551void ImGui::ShowAboutWindow(bool*) {}
11552void ImGui::ShowDemoWindow(bool*) {}
11553void ImGui::ShowUserGuide() {}
11554void ImGui::ShowStyleEditor(ImGuiStyle*) {}
11555bool ImGui::ShowStyleSelector(const char*) { return false; }
11556
11557#endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS
11558
11559#endif // #ifndef IMGUI_DISABLE