1// dear imgui, v1.91.9b
2// (demo code)
3
4// Help:
5// - Read FAQ at http://dearimgui.com/faq
6// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
7// - Need help integrating Dear ImGui in your codebase?
8// - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started
9// - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
10// Read top of imgui.cpp and imgui.h for many details, documentation, comments, links.
11// Get the latest version at https://github.com/ocornut/imgui
12
13// How to easily locate code?
14// - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools
15// - Browse an online version the demo with code linked to hovered widgets: https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html
16// - Find a visible string and search for it in the code!
17
18//---------------------------------------------------
19// PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT!
20//---------------------------------------------------
21// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:
22// Think again! It is the most useful reference code that you and other coders will want to refer to and call.
23// Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of your game/app!
24// Also include Metrics! ItemPicker! DebugLog! and other debug features.
25// Removing this file from your project is hindering access to documentation for everyone in your team,
26// likely leading you to poorer usage of the library.
27// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().
28// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be
29// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
30// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.
31// Thank you,
32// -Your beloved friend, imgui_demo.cpp (which you won't delete)
33
34//--------------------------------------------
35// ABOUT THE MEANING OF THE 'static' KEYWORD:
36//--------------------------------------------
37// In this demo code, we frequently use 'static' variables inside functions.
38// A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function.
39// Think of "static int n = 0;" as "global int n = 0;" !
40// We do this IN THE DEMO because we want:
41// - to gather code and data in the same place.
42// - to make the demo source code faster to read, faster to change, smaller in size.
43// - it is also a convenient way of storing simple UI related information as long as your function
44// doesn't need to be reentrant or used in multiple threads.
45// This might be a pattern you will want to use in your code, but most of the data you would be working
46// with in a complex codebase is likely going to be stored outside your functions.
47
48//-----------------------------------------
49// ABOUT THE CODING STYLE OF OUR DEMO CODE
50//-----------------------------------------
51// The Demo code in this file is designed to be easy to copy-and-paste into your application!
52// Because of this:
53// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.
54// - We try to declare static variables in the local scope, as close as possible to the code using them.
55// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.
56// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided
57// by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional
58// and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
59// Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
60
61// Navigating this file:
62// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
63// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
64// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
65// - You can search/grep for all sections listed in the index to find the section.
66
67/*
68
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] DemoWindowWidgetsBasic()
77// [SECTION] DemoWindowWidgetsBullets()
78// [SECTION] DemoWindowWidgetsCollapsingHeaders()
79// [SECTION] DemoWindowWidgetsComboBoxes()
80// [SECTION] DemoWindowWidgetsColorAndPickers()
81// [SECTION] DemoWindowWidgetsDataTypes()
82// [SECTION] DemoWindowWidgetsDisableBlocks()
83// [SECTION] DemoWindowWidgetsDragAndDrop()
84// [SECTION] DemoWindowWidgetsDragsAndSliders()
85// [SECTION] DemoWindowWidgetsImages()
86// [SECTION] DemoWindowWidgetsListBoxes()
87// [SECTION] DemoWindowWidgetsMultiComponents()
88// [SECTION] DemoWindowWidgetsPlotting()
89// [SECTION] DemoWindowWidgetsProgressBars()
90// [SECTION] DemoWindowWidgetsQueryingStatuses()
91// [SECTION] DemoWindowWidgetsSelectables()
92// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
93// [SECTION] DemoWindowWidgetsTabs()
94// [SECTION] DemoWindowWidgetsText()
95// [SECTION] DemoWindowWidgetsTextFilter()
96// [SECTION] DemoWindowWidgetsTextInput()
97// [SECTION] DemoWindowWidgetsTooltips()
98// [SECTION] DemoWindowWidgetsTreeNodes()
99// [SECTION] DemoWindowWidgetsVerticalSliders()
100// [SECTION] DemoWindowWidgets()
101// [SECTION] DemoWindowLayout()
102// [SECTION] DemoWindowPopups()
103// [SECTION] DemoWindowTables()
104// [SECTION] DemoWindowInputs()
105// [SECTION] About Window / ShowAboutWindow()
106// [SECTION] Style Editor / ShowStyleEditor()
107// [SECTION] User Guide / ShowUserGuide()
108// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
109// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
110// [SECTION] Example App: Debug Log / ShowExampleAppLog()
111// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
112// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
113// [SECTION] Example App: Long Text / ShowExampleAppLongText()
114// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
115// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
116// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
117// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
118// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()
119// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
120// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
121// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
122// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
123
124*/
125
126#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
127#define _CRT_SECURE_NO_WARNINGS
128#endif
129
130#include "imgui.h"
131#ifndef IMGUI_DISABLE
132
133// System includes
134#include <ctype.h> // toupper
135#include <limits.h> // INT_MIN, INT_MAX
136#include <math.h> // sqrtf, powf, cosf, sinf, floorf, ceilf
137#include <stdio.h> // vsnprintf, sscanf, printf
138#include <stdlib.h> // NULL, malloc, free, atoi
139#include <stdint.h> // intptr_t
140#if !defined(_MSC_VER) || _MSC_VER >= 1800
141#include <inttypes.h> // PRId64/PRIu64, not avail in some MinGW headers.
142#endif
143#ifdef __EMSCRIPTEN__
144#include <emscripten/version.h> // __EMSCRIPTEN_major__ etc.
145#endif
146
147// Visual Studio warnings
148#ifdef _MSC_VER
149#pragma warning (disable: 4127) // condition expression is constant
150#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
151#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
152#endif
153
154// Clang/GCC warnings with -Weverything
155#if defined(__clang__)
156#if __has_warning("-Wunknown-warning-option")
157#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
158#endif
159#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
160#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
161#pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning: 'xx' is deprecated: The POSIX name for this.. // for strdup used in demo code (so user can copy & paste the code)
162#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type
163#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
164#pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal
165#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning: declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
166#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used.
167#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
168#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
169#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
170#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
171#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
172#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
173#elif defined(__GNUC__)
174#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
175#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
176#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
177#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
178#pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure)
179#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
180#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
181#pragma GCC diagnostic ignored "-Wmisleading-indentation" // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub.
182#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
183#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
184#endif
185
186// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
187#ifdef _WIN32
188#define IM_NEWLINE "\r\n"
189#else
190#define IM_NEWLINE "\n"
191#endif
192
193// Helpers
194#if defined(_MSC_VER) && !defined(snprintf)
195#define snprintf _snprintf
196#endif
197#if defined(_MSC_VER) && !defined(vsnprintf)
198#define vsnprintf _vsnprintf
199#endif
200
201// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013)
202#if !defined(PRId64) && defined(_MSC_VER)
203#define PRId64 "I64d"
204#define PRIu64 "I64u"
205#elif !defined(PRId64)
206#define PRId64 "lld"
207#define PRIu64 "llu"
208#endif
209
210// Helpers macros
211// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
212// but making an exception here as those are largely simplifying code...
213// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
214#define IM_MIN(A, B) (((A) < (B)) ? (A) : (B))
215#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
216#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
217
218// Enforce cdecl calling convention for functions called by the standard library,
219// in case compilation settings changed the default to e.g. __vectorcall
220#ifndef IMGUI_CDECL
221#ifdef _MSC_VER
222#define IMGUI_CDECL __cdecl
223#else
224#define IMGUI_CDECL
225#endif
226#endif
227
228//-----------------------------------------------------------------------------
229// [SECTION] Forward Declarations
230//-----------------------------------------------------------------------------
231
232#if !defined(IMGUI_DISABLE_DEMO_WINDOWS)
233
234// Forward Declarations
235struct ImGuiDemoWindowData;
236static void ShowExampleAppMainMenuBar();
237static void ShowExampleAppAssetsBrowser(bool* p_open);
238static void ShowExampleAppConsole(bool* p_open);
239static void ShowExampleAppCustomRendering(bool* p_open);
240static void ShowExampleAppDockSpace(bool* p_open);
241static void ShowExampleAppDocuments(bool* p_open);
242static void ShowExampleAppLog(bool* p_open);
243static void ShowExampleAppLayout(bool* p_open);
244static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);
245static void ShowExampleAppSimpleOverlay(bool* p_open);
246static void ShowExampleAppAutoResize(bool* p_open);
247static void ShowExampleAppConstrainedResize(bool* p_open);
248static void ShowExampleAppFullscreen(bool* p_open);
249static void ShowExampleAppLongText(bool* p_open);
250static void ShowExampleAppWindowTitles(bool* p_open);
251static void ShowExampleMenuFile();
252
253// We split the contents of the big ShowDemoWindow() function into smaller functions
254// (because the link time of very large functions tends to grow non-linearly)
255static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data);
256static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data);
257static void DemoWindowLayout();
258static void DemoWindowPopups();
259static void DemoWindowTables();
260static void DemoWindowColumns();
261static void DemoWindowInputs();
262
263// Helper tree functions used by Property Editor & Multi-Select demos
264struct ExampleTreeNode;
265static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent);
266static void ExampleTree_DestroyNode(ExampleTreeNode* node);
267
268//-----------------------------------------------------------------------------
269// [SECTION] Helpers
270//-----------------------------------------------------------------------------
271
272// Helper to display a little (?) mark which shows a tooltip when hovered.
273// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
274static void HelpMarker(const char* desc)
275{
276 ImGui::TextDisabled(fmt: "(?)");
277 if (ImGui::BeginItemTooltip())
278 {
279 ImGui::PushTextWrapPos(wrap_local_pos_x: ImGui::GetFontSize() * 35.0f);
280 ImGui::TextUnformatted(text: desc);
281 ImGui::PopTextWrapPos();
282 ImGui::EndTooltip();
283 }
284}
285
286static void ShowDockingDisabledMessage()
287{
288 ImGuiIO& io = ImGui::GetIO();
289 ImGui::Text(fmt: "ERROR: Docking is not enabled! See Demo > Configuration.");
290 ImGui::Text(fmt: "Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or ");
291 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: 0.0f);
292 if (ImGui::SmallButton(label: "click here"))
293 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
294}
295
296// Helper to wire demo markers located in code to an interactive browser
297typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data);
298extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback;
299extern void* GImGuiDemoMarkerCallbackUserData;
300ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL;
301void* GImGuiDemoMarkerCallbackUserData = NULL;
302#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
303
304//-----------------------------------------------------------------------------
305// [SECTION] Demo Window / ShowDemoWindow()
306//-----------------------------------------------------------------------------
307
308// Data to be shared across different functions of the demo.
309struct ImGuiDemoWindowData
310{
311 // Examples Apps (accessible from the "Examples" menu)
312 bool ShowMainMenuBar = false;
313 bool ShowAppAssetsBrowser = false;
314 bool ShowAppConsole = false;
315 bool ShowAppCustomRendering = false;
316 bool ShowAppDocuments = false;
317 bool ShowAppDockSpace = false;
318 bool ShowAppLog = false;
319 bool ShowAppLayout = false;
320 bool ShowAppPropertyEditor = false;
321 bool ShowAppSimpleOverlay = false;
322 bool ShowAppAutoResize = false;
323 bool ShowAppConstrainedResize = false;
324 bool ShowAppFullscreen = false;
325 bool ShowAppLongText = false;
326 bool ShowAppWindowTitles = false;
327
328 // Dear ImGui Tools (accessible from the "Tools" menu)
329 bool ShowMetrics = false;
330 bool ShowDebugLog = false;
331 bool ShowIDStackTool = false;
332 bool ShowStyleEditor = false;
333 bool ShowAbout = false;
334
335 // Other data
336 bool DisableSections = false;
337 ExampleTreeNode* DemoTree = NULL;
338
339 ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(node: DemoTree); }
340};
341
342// Demonstrate most Dear ImGui features (this is big function!)
343// You may execute this function to experiment with the UI and understand what it does.
344// You may then search for keywords in the code when you are interested by a specific feature.
345void ImGui::ShowDemoWindow(bool* p_open)
346{
347 // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
348 // Most functions would normally just assert/crash if the context is missing.
349 IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!");
350
351 // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
352 IMGUI_CHECKVERSION();
353
354 // Stored data
355 static ImGuiDemoWindowData demo_data;
356
357 // Examples Apps (accessible from the "Examples" menu)
358 if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); }
359 if (demo_data.ShowAppDockSpace) { ShowExampleAppDockSpace(p_open: &demo_data.ShowAppDockSpace); } // Important: Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function)
360 if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(p_open: &demo_data.ShowAppDocuments); } // ...process the Document app next, as it may also use a DockSpace()
361 if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(p_open: &demo_data.ShowAppAssetsBrowser); }
362 if (demo_data.ShowAppConsole) { ShowExampleAppConsole(p_open: &demo_data.ShowAppConsole); }
363 if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(p_open: &demo_data.ShowAppCustomRendering); }
364 if (demo_data.ShowAppLog) { ShowExampleAppLog(p_open: &demo_data.ShowAppLog); }
365 if (demo_data.ShowAppLayout) { ShowExampleAppLayout(p_open: &demo_data.ShowAppLayout); }
366 if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(p_open: &demo_data.ShowAppPropertyEditor, demo_data: &demo_data); }
367 if (demo_data.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(p_open: &demo_data.ShowAppSimpleOverlay); }
368 if (demo_data.ShowAppAutoResize) { ShowExampleAppAutoResize(p_open: &demo_data.ShowAppAutoResize); }
369 if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(p_open: &demo_data.ShowAppConstrainedResize); }
370 if (demo_data.ShowAppFullscreen) { ShowExampleAppFullscreen(p_open: &demo_data.ShowAppFullscreen); }
371 if (demo_data.ShowAppLongText) { ShowExampleAppLongText(p_open: &demo_data.ShowAppLongText); }
372 if (demo_data.ShowAppWindowTitles) { ShowExampleAppWindowTitles(p_open: &demo_data.ShowAppWindowTitles); }
373
374 // Dear ImGui Tools (accessible from the "Tools" menu)
375 if (demo_data.ShowMetrics) { ImGui::ShowMetricsWindow(p_open: &demo_data.ShowMetrics); }
376 if (demo_data.ShowDebugLog) { ImGui::ShowDebugLogWindow(p_open: &demo_data.ShowDebugLog); }
377 if (demo_data.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(p_open: &demo_data.ShowIDStackTool); }
378 if (demo_data.ShowAbout) { ImGui::ShowAboutWindow(p_open: &demo_data.ShowAbout); }
379 if (demo_data.ShowStyleEditor)
380 {
381 ImGui::Begin(name: "Dear ImGui Style Editor", p_open: &demo_data.ShowStyleEditor);
382 ImGui::ShowStyleEditor();
383 ImGui::End();
384 }
385
386 // Demonstrate the various window flags. Typically you would just use the default!
387 static bool no_titlebar = false;
388 static bool no_scrollbar = false;
389 static bool no_menu = false;
390 static bool no_move = false;
391 static bool no_resize = false;
392 static bool no_collapse = false;
393 static bool no_close = false;
394 static bool no_nav = false;
395 static bool no_background = false;
396 static bool no_bring_to_front = false;
397 static bool no_docking = false;
398 static bool unsaved_document = false;
399
400 ImGuiWindowFlags window_flags = 0;
401 if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar;
402 if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar;
403 if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar;
404 if (no_move) window_flags |= ImGuiWindowFlags_NoMove;
405 if (no_resize) window_flags |= ImGuiWindowFlags_NoResize;
406 if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse;
407 if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
408 if (no_background) window_flags |= ImGuiWindowFlags_NoBackground;
409 if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
410 if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking;
411 if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument;
412 if (no_close) p_open = NULL; // Don't pass our bool* to Begin
413
414 // We specify a default position/size in case there's no data in the .ini file.
415 // We only do it to make the demo applications a little more welcoming, but typically this isn't required.
416 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
417 ImGui::SetNextWindowPos(pos: ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), cond: ImGuiCond_FirstUseEver);
418 ImGui::SetNextWindowSize(size: ImVec2(550, 680), cond: ImGuiCond_FirstUseEver);
419
420 // Main body of the Demo window starts here.
421 if (!ImGui::Begin(name: "Dear ImGui Demo", p_open, flags: window_flags))
422 {
423 // Early out if the window is collapsed, as an optimization.
424 ImGui::End();
425 return;
426 }
427
428 // Most "big" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details.
429 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * -12); // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets.
430 //ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align)
431
432 // Menu Bar
433 DemoWindowMenuBar(demo_data: &demo_data);
434
435 ImGui::Text(fmt: "dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
436 ImGui::Spacing();
437
438 IMGUI_DEMO_MARKER("Help");
439 if (ImGui::CollapsingHeader(label: "Help"))
440 {
441 ImGui::SeparatorText(label: "ABOUT THIS DEMO:");
442 ImGui::BulletText(fmt: "Sections below are demonstrating many aspects of the library.");
443 ImGui::BulletText(fmt: "The \"Examples\" menu above leads to more demo contents.");
444 ImGui::BulletText(fmt: "The \"Tools\" menu above gives access to: About Box, Style Editor,\n"
445 "and Metrics/Debugger (general purpose Dear ImGui debugging tool).");
446
447 ImGui::SeparatorText(label: "PROGRAMMER GUIDE:");
448 ImGui::BulletText(fmt: "See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!");
449 ImGui::BulletText(fmt: "See comments in imgui.cpp.");
450 ImGui::BulletText(fmt: "See example applications in the examples/ folder.");
451 ImGui::BulletText(fmt: "Read the FAQ at ");
452 ImGui::SameLine(offset_from_start_x: 0, spacing: 0);
453 ImGui::TextLinkOpenURL(label: "https://www.dearimgui.com/faq/");
454 ImGui::BulletText(fmt: "Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.");
455 ImGui::BulletText(fmt: "Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.");
456
457 ImGui::SeparatorText(label: "USER GUIDE:");
458 ImGui::ShowUserGuide();
459 }
460
461 IMGUI_DEMO_MARKER("Configuration");
462 if (ImGui::CollapsingHeader(label: "Configuration"))
463 {
464 ImGuiIO& io = ImGui::GetIO();
465
466 if (ImGui::TreeNode(label: "Configuration##2"))
467 {
468 ImGui::SeparatorText(label: "General");
469 ImGui::CheckboxFlags(label: "io.ConfigFlags: NavEnableKeyboard", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NavEnableKeyboard);
470 ImGui::SameLine(); HelpMarker(desc: "Enable keyboard controls.");
471 ImGui::CheckboxFlags(label: "io.ConfigFlags: NavEnableGamepad", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NavEnableGamepad);
472 ImGui::SameLine(); HelpMarker(desc: "Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
473 ImGui::CheckboxFlags(label: "io.ConfigFlags: NoMouse", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NoMouse);
474 ImGui::SameLine(); HelpMarker(desc: "Instruct dear imgui to disable mouse inputs and interactions.");
475
476 // The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:
477 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
478 {
479 if (fmodf(x: (float)ImGui::GetTime(), y: 0.40f) < 0.20f)
480 {
481 ImGui::SameLine();
482 ImGui::Text(fmt: "<<PRESS SPACE TO DISABLE>>");
483 }
484 // Prevent both being checked
485 if (ImGui::IsKeyPressed(key: ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard))
486 io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
487 }
488
489 ImGui::CheckboxFlags(label: "io.ConfigFlags: NoMouseCursorChange", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NoMouseCursorChange);
490 ImGui::SameLine(); HelpMarker(desc: "Instruct backend to not alter mouse cursor shape and visibility.");
491 ImGui::CheckboxFlags(label: "io.ConfigFlags: NoKeyboard", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NoKeyboard);
492 ImGui::SameLine(); HelpMarker(desc: "Instruct dear imgui to disable keyboard inputs and interactions.");
493
494 ImGui::Checkbox(label: "io.ConfigInputTrickleEventQueue", v: &io.ConfigInputTrickleEventQueue);
495 ImGui::SameLine(); HelpMarker(desc: "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.");
496 ImGui::Checkbox(label: "io.MouseDrawCursor", v: &io.MouseDrawCursor);
497 ImGui::SameLine(); HelpMarker(desc: "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).");
498
499 ImGui::SeparatorText(label: "Keyboard/Gamepad Navigation");
500 ImGui::Checkbox(label: "io.ConfigNavSwapGamepadButtons", v: &io.ConfigNavSwapGamepadButtons);
501 ImGui::Checkbox(label: "io.ConfigNavMoveSetMousePos", v: &io.ConfigNavMoveSetMousePos);
502 ImGui::SameLine(); HelpMarker(desc: "Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
503 ImGui::Checkbox(label: "io.ConfigNavCaptureKeyboard", v: &io.ConfigNavCaptureKeyboard);
504 ImGui::Checkbox(label: "io.ConfigNavEscapeClearFocusItem", v: &io.ConfigNavEscapeClearFocusItem);
505 ImGui::SameLine(); HelpMarker(desc: "Pressing Escape clears focused item.");
506 ImGui::Checkbox(label: "io.ConfigNavEscapeClearFocusWindow", v: &io.ConfigNavEscapeClearFocusWindow);
507 ImGui::SameLine(); HelpMarker(desc: "Pressing Escape clears focused window.");
508 ImGui::Checkbox(label: "io.ConfigNavCursorVisibleAuto", v: &io.ConfigNavCursorVisibleAuto);
509 ImGui::SameLine(); HelpMarker(desc: "Using directional navigation key makes the cursor visible. Mouse click hides the cursor.");
510 ImGui::Checkbox(label: "io.ConfigNavCursorVisibleAlways", v: &io.ConfigNavCursorVisibleAlways);
511 ImGui::SameLine(); HelpMarker(desc: "Navigation cursor is always visible.");
512
513 ImGui::SeparatorText(label: "Docking");
514 ImGui::CheckboxFlags(label: "io.ConfigFlags: DockingEnable", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_DockingEnable);
515 ImGui::SameLine();
516 if (io.ConfigDockingWithShift)
517 HelpMarker(desc: "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).");
518 else
519 HelpMarker(desc: "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).");
520 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
521 {
522 ImGui::Indent();
523 ImGui::Checkbox(label: "io.ConfigDockingNoSplit", v: &io.ConfigDockingNoSplit);
524 ImGui::SameLine(); HelpMarker(desc: "Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.");
525 ImGui::Checkbox(label: "io.ConfigDockingWithShift", v: &io.ConfigDockingWithShift);
526 ImGui::SameLine(); HelpMarker(desc: "Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)");
527 ImGui::Checkbox(label: "io.ConfigDockingAlwaysTabBar", v: &io.ConfigDockingAlwaysTabBar);
528 ImGui::SameLine(); HelpMarker(desc: "Create a docking node and tab-bar on single floating windows.");
529 ImGui::Checkbox(label: "io.ConfigDockingTransparentPayload", v: &io.ConfigDockingTransparentPayload);
530 ImGui::SameLine(); HelpMarker(desc: "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.");
531 ImGui::Unindent();
532 }
533
534 ImGui::SeparatorText(label: "Multi-viewports");
535 ImGui::CheckboxFlags(label: "io.ConfigFlags: ViewportsEnable", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_ViewportsEnable);
536 ImGui::SameLine(); HelpMarker(desc: "[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details.");
537 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
538 {
539 ImGui::Indent();
540 ImGui::Checkbox(label: "io.ConfigViewportsNoAutoMerge", v: &io.ConfigViewportsNoAutoMerge);
541 ImGui::SameLine(); HelpMarker(desc: "Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it.");
542 ImGui::Checkbox(label: "io.ConfigViewportsNoTaskBarIcon", v: &io.ConfigViewportsNoTaskBarIcon);
543 ImGui::SameLine(); HelpMarker(desc: "Toggling this at runtime is normally unsupported (most platform backends won't refresh the task bar icon state right away).");
544 ImGui::Checkbox(label: "io.ConfigViewportsNoDecoration", v: &io.ConfigViewportsNoDecoration);
545 ImGui::SameLine(); HelpMarker(desc: "Toggling this at runtime is normally unsupported (most platform backends won't refresh the decoration right away).");
546 ImGui::Checkbox(label: "io.ConfigViewportsNoDefaultParent", v: &io.ConfigViewportsNoDefaultParent);
547 ImGui::SameLine(); HelpMarker(desc: "Toggling this at runtime is normally unsupported (most platform backends won't refresh the parenting right away).");
548 ImGui::Unindent();
549 }
550
551 ImGui::SeparatorText(label: "Windows");
552 ImGui::Checkbox(label: "io.ConfigWindowsResizeFromEdges", v: &io.ConfigWindowsResizeFromEdges);
553 ImGui::SameLine(); HelpMarker(desc: "Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.");
554 ImGui::Checkbox(label: "io.ConfigWindowsMoveFromTitleBarOnly", v: &io.ConfigWindowsMoveFromTitleBarOnly);
555 ImGui::Checkbox(label: "io.ConfigWindowsCopyContentsWithCtrlC", v: &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]
556 ImGui::SameLine(); HelpMarker(desc: "*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.");
557 ImGui::Checkbox(label: "io.ConfigScrollbarScrollByPage", v: &io.ConfigScrollbarScrollByPage);
558 ImGui::SameLine(); HelpMarker(desc: "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.");
559
560 ImGui::SeparatorText(label: "Widgets");
561 ImGui::Checkbox(label: "io.ConfigInputTextCursorBlink", v: &io.ConfigInputTextCursorBlink);
562 ImGui::SameLine(); HelpMarker(desc: "Enable blinking cursor (optional as some users consider it to be distracting).");
563 ImGui::Checkbox(label: "io.ConfigInputTextEnterKeepActive", v: &io.ConfigInputTextEnterKeepActive);
564 ImGui::SameLine(); HelpMarker(desc: "Pressing Enter will keep item active and select contents (single-line only).");
565 ImGui::Checkbox(label: "io.ConfigDragClickToInputText", v: &io.ConfigDragClickToInputText);
566 ImGui::SameLine(); HelpMarker(desc: "Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
567 ImGui::Checkbox(label: "io.ConfigMacOSXBehaviors", v: &io.ConfigMacOSXBehaviors);
568 ImGui::SameLine(); HelpMarker(desc: "Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
569 ImGui::Text(fmt: "Also see Style->Rendering for rendering options.");
570
571 // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
572 ImGui::SeparatorText(label: "Error Handling");
573
574 ImGui::Checkbox(label: "io.ConfigErrorRecovery", v: &io.ConfigErrorRecovery);
575 ImGui::SameLine(); HelpMarker(
576 desc: "Options to configure how we handle recoverable errors.\n"
577 "- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
578 "- You not are not supposed to rely on it in the course of a normal application run.\n"
579 "- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
580 "- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! "
581 "Otherwise it would severely hinder your ability to catch and correct mistakes!");
582 ImGui::Checkbox(label: "io.ConfigErrorRecoveryEnableAssert", v: &io.ConfigErrorRecoveryEnableAssert);
583 ImGui::Checkbox(label: "io.ConfigErrorRecoveryEnableDebugLog", v: &io.ConfigErrorRecoveryEnableDebugLog);
584 ImGui::Checkbox(label: "io.ConfigErrorRecoveryEnableTooltip", v: &io.ConfigErrorRecoveryEnableTooltip);
585 if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
586 io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
587
588 // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
589 ImGui::SeparatorText(label: "Debug");
590 ImGui::Checkbox(label: "io.ConfigDebugIsDebuggerPresent", v: &io.ConfigDebugIsDebuggerPresent);
591 ImGui::SameLine(); HelpMarker(desc: "Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
592 ImGui::Checkbox(label: "io.ConfigDebugHighlightIdConflicts", v: &io.ConfigDebugHighlightIdConflicts);
593 ImGui::SameLine(); HelpMarker(desc: "Highlight and show an error message when multiple items have conflicting identifiers.");
594 ImGui::BeginDisabled();
595 ImGui::Checkbox(label: "io.ConfigDebugBeginReturnValueOnce", v: &io.ConfigDebugBeginReturnValueOnce);
596 ImGui::EndDisabled();
597 ImGui::SameLine(); HelpMarker(desc: "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.");
598 ImGui::Checkbox(label: "io.ConfigDebugBeginReturnValueLoop", v: &io.ConfigDebugBeginReturnValueLoop);
599 ImGui::SameLine(); HelpMarker(desc: "Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
600 ImGui::Checkbox(label: "io.ConfigDebugIgnoreFocusLoss", v: &io.ConfigDebugIgnoreFocusLoss);
601 ImGui::SameLine(); HelpMarker(desc: "Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.");
602 ImGui::Checkbox(label: "io.ConfigDebugIniSettings", v: &io.ConfigDebugIniSettings);
603 ImGui::SameLine(); HelpMarker(desc: "Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).");
604
605 ImGui::TreePop();
606 ImGui::Spacing();
607 }
608
609 IMGUI_DEMO_MARKER("Configuration/Backend Flags");
610 if (ImGui::TreeNode(label: "Backend Flags"))
611 {
612 HelpMarker(
613 desc: "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n"
614 "Here we expose them as read-only fields to avoid breaking interactions with your backend.");
615
616 // Make a local copy to avoid modifying actual backend flags.
617 // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?
618 ImGui::BeginDisabled();
619 ImGui::CheckboxFlags(label: "io.BackendFlags: HasGamepad", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasGamepad);
620 ImGui::CheckboxFlags(label: "io.BackendFlags: HasMouseCursors", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasMouseCursors);
621 ImGui::CheckboxFlags(label: "io.BackendFlags: HasSetMousePos", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasSetMousePos);
622 ImGui::CheckboxFlags(label: "io.BackendFlags: PlatformHasViewports", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_PlatformHasViewports);
623 ImGui::CheckboxFlags(label: "io.BackendFlags: HasMouseHoveredViewport",flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasMouseHoveredViewport);
624 ImGui::CheckboxFlags(label: "io.BackendFlags: RendererHasVtxOffset", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_RendererHasVtxOffset);
625 ImGui::CheckboxFlags(label: "io.BackendFlags: RendererHasViewports", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_RendererHasViewports);
626 ImGui::EndDisabled();
627
628 ImGui::TreePop();
629 ImGui::Spacing();
630 }
631
632 IMGUI_DEMO_MARKER("Configuration/Style");
633 if (ImGui::TreeNode(label: "Style"))
634 {
635 ImGui::Checkbox(label: "Style Editor", v: &demo_data.ShowStyleEditor);
636 ImGui::SameLine();
637 HelpMarker(desc: "The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.");
638 ImGui::TreePop();
639 ImGui::Spacing();
640 }
641
642 IMGUI_DEMO_MARKER("Configuration/Capture, Logging");
643 if (ImGui::TreeNode(label: "Capture/Logging"))
644 {
645 HelpMarker(
646 desc: "The logging API redirects all text output so you can easily capture the content of "
647 "a window or a block. Tree nodes can be automatically expanded.\n"
648 "Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
649 ImGui::LogButtons();
650
651 HelpMarker(desc: "You can also call ImGui::LogText() to output directly to the log without a visual output.");
652 if (ImGui::Button(label: "Copy \"Hello, world!\" to clipboard"))
653 {
654 ImGui::LogToClipboard();
655 ImGui::LogText(fmt: "Hello, world!");
656 ImGui::LogFinish();
657 }
658 ImGui::TreePop();
659 }
660 }
661
662 IMGUI_DEMO_MARKER("Window options");
663 if (ImGui::CollapsingHeader(label: "Window options"))
664 {
665 if (ImGui::BeginTable(str_id: "split", columns: 3))
666 {
667 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No titlebar", v: &no_titlebar);
668 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No scrollbar", v: &no_scrollbar);
669 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No menu", v: &no_menu);
670 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No move", v: &no_move);
671 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No resize", v: &no_resize);
672 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No collapse", v: &no_collapse);
673 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No close", v: &no_close);
674 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No nav", v: &no_nav);
675 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No background", v: &no_background);
676 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No bring to front", v: &no_bring_to_front);
677 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No docking", v: &no_docking);
678 ImGui::TableNextColumn(); ImGui::Checkbox(label: "Unsaved document", v: &unsaved_document);
679 ImGui::EndTable();
680 }
681 }
682
683 // All demo contents
684 DemoWindowWidgets(demo_data: &demo_data);
685 DemoWindowLayout();
686 DemoWindowPopups();
687 DemoWindowTables();
688 DemoWindowInputs();
689
690 // End of ShowDemoWindow()
691 ImGui::PopItemWidth();
692 ImGui::End();
693}
694
695//-----------------------------------------------------------------------------
696// [SECTION] DemoWindowMenuBar()
697//-----------------------------------------------------------------------------
698
699static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
700{
701 IMGUI_DEMO_MARKER("Menu");
702 if (ImGui::BeginMenuBar())
703 {
704 if (ImGui::BeginMenu(label: "Menu"))
705 {
706 IMGUI_DEMO_MARKER("Menu/File");
707 ShowExampleMenuFile();
708 ImGui::EndMenu();
709 }
710 if (ImGui::BeginMenu(label: "Examples"))
711 {
712 IMGUI_DEMO_MARKER("Menu/Examples");
713 ImGui::MenuItem(label: "Main menu bar", NULL, p_selected: &demo_data->ShowMainMenuBar);
714
715 ImGui::SeparatorText(label: "Mini apps");
716 ImGui::MenuItem(label: "Assets Browser", NULL, p_selected: &demo_data->ShowAppAssetsBrowser);
717 ImGui::MenuItem(label: "Console", NULL, p_selected: &demo_data->ShowAppConsole);
718 ImGui::MenuItem(label: "Custom rendering", NULL, p_selected: &demo_data->ShowAppCustomRendering);
719 ImGui::MenuItem(label: "Documents", NULL, p_selected: &demo_data->ShowAppDocuments);
720 ImGui::MenuItem(label: "Dockspace", NULL, p_selected: &demo_data->ShowAppDockSpace);
721 ImGui::MenuItem(label: "Log", NULL, p_selected: &demo_data->ShowAppLog);
722 ImGui::MenuItem(label: "Property editor", NULL, p_selected: &demo_data->ShowAppPropertyEditor);
723 ImGui::MenuItem(label: "Simple layout", NULL, p_selected: &demo_data->ShowAppLayout);
724 ImGui::MenuItem(label: "Simple overlay", NULL, p_selected: &demo_data->ShowAppSimpleOverlay);
725
726 ImGui::SeparatorText(label: "Concepts");
727 ImGui::MenuItem(label: "Auto-resizing window", NULL, p_selected: &demo_data->ShowAppAutoResize);
728 ImGui::MenuItem(label: "Constrained-resizing window", NULL, p_selected: &demo_data->ShowAppConstrainedResize);
729 ImGui::MenuItem(label: "Fullscreen window", NULL, p_selected: &demo_data->ShowAppFullscreen);
730 ImGui::MenuItem(label: "Long text display", NULL, p_selected: &demo_data->ShowAppLongText);
731 ImGui::MenuItem(label: "Manipulating window titles", NULL, p_selected: &demo_data->ShowAppWindowTitles);
732
733 ImGui::EndMenu();
734 }
735 //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar!
736 if (ImGui::BeginMenu(label: "Tools"))
737 {
738 IMGUI_DEMO_MARKER("Menu/Tools");
739 ImGuiIO& io = ImGui::GetIO();
740#ifndef IMGUI_DISABLE_DEBUG_TOOLS
741 const bool has_debug_tools = true;
742#else
743 const bool has_debug_tools = false;
744#endif
745 ImGui::MenuItem(label: "Metrics/Debugger", NULL, p_selected: &demo_data->ShowMetrics, enabled: has_debug_tools);
746 if (ImGui::BeginMenu(label: "Debug Options"))
747 {
748 ImGui::BeginDisabled(disabled: !has_debug_tools);
749 ImGui::Checkbox(label: "Highlight ID Conflicts", v: &io.ConfigDebugHighlightIdConflicts);
750 ImGui::EndDisabled();
751 ImGui::Checkbox(label: "Assert on error recovery", v: &io.ConfigErrorRecoveryEnableAssert);
752 ImGui::TextDisabled(fmt: "(see Demo->Configuration for details & more)");
753 ImGui::EndMenu();
754 }
755 ImGui::MenuItem(label: "Debug Log", NULL, p_selected: &demo_data->ShowDebugLog, enabled: has_debug_tools);
756 ImGui::MenuItem(label: "ID Stack Tool", NULL, p_selected: &demo_data->ShowIDStackTool, enabled: has_debug_tools);
757 bool is_debugger_present = io.ConfigDebugIsDebuggerPresent;
758 if (ImGui::MenuItem(label: "Item Picker", NULL, selected: false, enabled: has_debug_tools))// && is_debugger_present))
759 ImGui::DebugStartItemPicker();
760 if (!is_debugger_present)
761 ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable some extra features to avoid casual users crashing the application.");
762 ImGui::MenuItem(label: "Style Editor", NULL, p_selected: &demo_data->ShowStyleEditor);
763 ImGui::MenuItem(label: "About Dear ImGui", NULL, p_selected: &demo_data->ShowAbout);
764
765 ImGui::EndMenu();
766 }
767 ImGui::EndMenuBar();
768 }
769}
770
771//-----------------------------------------------------------------------------
772// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
773//-----------------------------------------------------------------------------
774
775// Simple representation for a tree
776// (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.)
777struct ExampleTreeNode
778{
779 // Tree structure
780 char Name[28] = "";
781 int UID = 0;
782 ExampleTreeNode* Parent = NULL;
783 ImVector<ExampleTreeNode*> Childs;
784 unsigned short IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily
785
786 // Leaf Data
787 bool HasData = false; // All leaves have data
788 bool DataMyBool = true;
789 int DataMyInt = 128;
790 ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f);
791};
792
793// Simple representation of struct metadata/serialization data.
794// (this is a minimal version of what a typical advanced application may provide)
795struct ExampleMemberInfo
796{
797 const char* Name; // Member name
798 ImGuiDataType DataType; // Member type
799 int DataCount; // Member count (1 when scalar)
800 int Offset; // Offset inside parent structure
801};
802
803// Metadata description of ExampleTreeNode struct.
804static const ExampleMemberInfo ExampleTreeNodeMemberInfos[]
805{
806 { .Name: "MyName", .DataType: ImGuiDataType_String, .DataCount: 1, offsetof(ExampleTreeNode, Name) },
807 { .Name: "MyBool", .DataType: ImGuiDataType_Bool, .DataCount: 1, offsetof(ExampleTreeNode, DataMyBool) },
808 { .Name: "MyInt", .DataType: ImGuiDataType_S32, .DataCount: 1, offsetof(ExampleTreeNode, DataMyInt) },
809 { .Name: "MyVec2", .DataType: ImGuiDataType_Float, .DataCount: 2, offsetof(ExampleTreeNode, DataMyVec2) },
810};
811
812static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent)
813{
814 ExampleTreeNode* node = IM_NEW(ExampleTreeNode);
815 snprintf(s: node->Name, IM_ARRAYSIZE(node->Name), format: "%s", name);
816 node->UID = uid;
817 node->Parent = parent;
818 node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0;
819 if (parent)
820 parent->Childs.push_back(v: node);
821 return node;
822}
823
824static void ExampleTree_DestroyNode(ExampleTreeNode* node)
825{
826 for (ExampleTreeNode* child_node : node->Childs)
827 ExampleTree_DestroyNode(node: child_node);
828 IM_DELETE(p: node);
829}
830
831// Create example tree data
832// (this allocates _many_ more times than most other code in either Dear ImGui or others demo)
833static ExampleTreeNode* ExampleTree_CreateDemoTree()
834{
835 static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" };
836 const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name);
837 char name_buf[NAME_MAX_LEN];
838 int uid = 0;
839 ExampleTreeNode* node_L0 = ExampleTree_CreateNode(name: "<ROOT>", uid: ++uid, NULL);
840 const int root_items_multiplier = 2;
841 for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++)
842 {
843 snprintf(s: name_buf, IM_ARRAYSIZE(name_buf), format: "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier);
844 ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name: name_buf, uid: ++uid, parent: node_L0);
845 const int number_of_childs = (int)strlen(s: node_L1->Name);
846 for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
847 {
848 snprintf(s: name_buf, IM_ARRAYSIZE(name_buf), format: "Child %d", idx_L1);
849 ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name: name_buf, uid: ++uid, parent: node_L1);
850 node_L2->HasData = true;
851 if (idx_L1 == 0)
852 {
853 snprintf(s: name_buf, IM_ARRAYSIZE(name_buf), format: "Sub-child %d", 0);
854 ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name: name_buf, uid: ++uid, parent: node_L2);
855 node_L3->HasData = true;
856 }
857 }
858 }
859 return node_L0;
860}
861
862//-----------------------------------------------------------------------------
863// [SECTION] DemoWindowWidgetsBasic()
864//-----------------------------------------------------------------------------
865
866static void DemoWindowWidgetsBasic()
867{
868 IMGUI_DEMO_MARKER("Widgets/Basic");
869 if (ImGui::TreeNode(label: "Basic"))
870 {
871 ImGui::SeparatorText(label: "General");
872
873 IMGUI_DEMO_MARKER("Widgets/Basic/Button");
874 static int clicked = 0;
875 if (ImGui::Button(label: "Button"))
876 clicked++;
877 if (clicked & 1)
878 {
879 ImGui::SameLine();
880 ImGui::Text(fmt: "Thanks for clicking me!");
881 }
882
883 IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox");
884 static bool check = true;
885 ImGui::Checkbox(label: "checkbox", v: &check);
886
887 IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton");
888 static int e = 0;
889 ImGui::RadioButton(label: "radio a", v: &e, v_button: 0); ImGui::SameLine();
890 ImGui::RadioButton(label: "radio b", v: &e, v_button: 1); ImGui::SameLine();
891 ImGui::RadioButton(label: "radio c", v: &e, v_button: 2);
892
893 // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
894 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)");
895 for (int i = 0; i < 7; i++)
896 {
897 if (i > 0)
898 ImGui::SameLine();
899 ImGui::PushID(int_id: i);
900 ImGui::PushStyleColor(idx: ImGuiCol_Button, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.6f, v: 0.6f));
901 ImGui::PushStyleColor(idx: ImGuiCol_ButtonHovered, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.7f, v: 0.7f));
902 ImGui::PushStyleColor(idx: ImGuiCol_ButtonActive, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.8f, v: 0.8f));
903 ImGui::Button(label: "Click");
904 ImGui::PopStyleColor(count: 3);
905 ImGui::PopID();
906 }
907
908 // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
909 // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
910 // See 'Demo->Layout->Text Baseline Alignment' for details.
911 ImGui::AlignTextToFramePadding();
912 ImGui::Text(fmt: "Hold to repeat:");
913 ImGui::SameLine();
914
915 // Arrow buttons with Repeater
916 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)");
917 static int counter = 0;
918 float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
919 ImGui::PushItemFlag(option: ImGuiItemFlags_ButtonRepeat, enabled: true);
920 if (ImGui::ArrowButton(str_id: "##left", dir: ImGuiDir_Left)) { counter--; }
921 ImGui::SameLine(offset_from_start_x: 0.0f, spacing);
922 if (ImGui::ArrowButton(str_id: "##right", dir: ImGuiDir_Right)) { counter++; }
923 ImGui::PopItemFlag();
924 ImGui::SameLine();
925 ImGui::Text(fmt: "%d", counter);
926
927 ImGui::Button(label: "Tooltip");
928 ImGui::SetItemTooltip("I am a tooltip");
929
930 ImGui::LabelText(label: "label", fmt: "Value");
931
932 ImGui::SeparatorText(label: "Inputs");
933
934 {
935 // To wire InputText() with std::string or any other custom string type,
936 // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
937 IMGUI_DEMO_MARKER("Widgets/Basic/InputText");
938 static char str0[128] = "Hello, world!";
939 ImGui::InputText(label: "input text", buf: str0, IM_ARRAYSIZE(str0));
940 ImGui::SameLine(); HelpMarker(
941 desc: "USER:\n"
942 "Hold SHIFT or use mouse to select text.\n"
943 "CTRL+Left/Right to word jump.\n"
944 "CTRL+A or Double-Click to select all.\n"
945 "CTRL+X,CTRL+C,CTRL+V for clipboard.\n"
946 "CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.\n"
947 "ESCAPE to revert.\n\n"
948 "PROGRAMMER:\n"
949 "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
950 "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
951 "in imgui_demo.cpp).");
952
953 static char str1[128] = "";
954 ImGui::InputTextWithHint(label: "input text (w/ hint)", hint: "enter text here", buf: str1, IM_ARRAYSIZE(str1));
955
956 IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat");
957 static int i0 = 123;
958 ImGui::InputInt(label: "input int", v: &i0);
959
960 static float f0 = 0.001f;
961 ImGui::InputFloat(label: "input float", v: &f0, step: 0.01f, step_fast: 1.0f, format: "%.3f");
962
963 static double d0 = 999999.00000001;
964 ImGui::InputDouble(label: "input double", v: &d0, step: 0.01f, step_fast: 1.0f, format: "%.8f");
965
966 static float f1 = 1.e10f;
967 ImGui::InputFloat(label: "input scientific", v: &f1, step: 0.0f, step_fast: 0.0f, format: "%e");
968 ImGui::SameLine(); HelpMarker(
969 desc: "You can input value using the scientific notation,\n"
970 " e.g. \"1e+8\" becomes \"100000000\".");
971
972 static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
973 ImGui::InputFloat3(label: "input float3", v: vec4a);
974 }
975
976 ImGui::SeparatorText(label: "Drags");
977
978 {
979 IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat");
980 static int i1 = 50, i2 = 42, i3 = 128;
981 ImGui::DragInt(label: "drag int", v: &i1, v_speed: 1);
982 ImGui::SameLine(); HelpMarker(
983 desc: "Click and drag to edit value.\n"
984 "Hold SHIFT/ALT for faster/slower edit.\n"
985 "Double-click or CTRL+click to input value.");
986 ImGui::DragInt(label: "drag int 0..100", v: &i2, v_speed: 1, v_min: 0, v_max: 100, format: "%d%%", flags: ImGuiSliderFlags_AlwaysClamp);
987 ImGui::DragInt(label: "drag int wrap 100..200", v: &i3, v_speed: 1, v_min: 100, v_max: 200, format: "%d", flags: ImGuiSliderFlags_WrapAround);
988
989 static float f1 = 1.00f, f2 = 0.0067f;
990 ImGui::DragFloat(label: "drag float", v: &f1, v_speed: 0.005f);
991 ImGui::DragFloat(label: "drag small float", v: &f2, v_speed: 0.0001f, v_min: 0.0f, v_max: 0.0f, format: "%.06f ns");
992 //ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround);
993 }
994
995 ImGui::SeparatorText(label: "Sliders");
996
997 {
998 IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat");
999 static int i1 = 0;
1000 ImGui::SliderInt(label: "slider int", v: &i1, v_min: -1, v_max: 3);
1001 ImGui::SameLine(); HelpMarker(desc: "CTRL+click to input value.");
1002
1003 static float f1 = 0.123f, f2 = 0.0f;
1004 ImGui::SliderFloat(label: "slider float", v: &f1, v_min: 0.0f, v_max: 1.0f, format: "ratio = %.3f");
1005 ImGui::SliderFloat(label: "slider float (log)", v: &f2, v_min: -10.0f, v_max: 10.0f, format: "%.4f", flags: ImGuiSliderFlags_Logarithmic);
1006
1007 IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle");
1008 static float angle = 0.0f;
1009 ImGui::SliderAngle(label: "slider angle", v_rad: &angle);
1010
1011 // Using the format string to display a name instead of an integer.
1012 // Here we completely omit '%d' from the format string, so it'll only display a name.
1013 // This technique can also be used with DragInt().
1014 IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)");
1015 enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };
1016 static int elem = Element_Fire;
1017 const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
1018 const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
1019 ImGui::SliderInt(label: "slider enum", v: &elem, v_min: 0, v_max: Element_COUNT - 1, format: elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here.
1020 ImGui::SameLine(); HelpMarker(desc: "Using the format string parameter to display a name instead of the underlying integer.");
1021 }
1022
1023 ImGui::SeparatorText(label: "Selectors/Pickers");
1024
1025 {
1026 IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4");
1027 static float col1[3] = { 1.0f, 0.0f, 0.2f };
1028 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1029 ImGui::ColorEdit3(label: "color 1", col: col1);
1030 ImGui::SameLine(); HelpMarker(
1031 desc: "Click on the color square to open a color picker.\n"
1032 "Click and hold to use drag and drop.\n"
1033 "Right-click on the color square to show options.\n"
1034 "CTRL+click on individual component to input value.\n");
1035
1036 ImGui::ColorEdit4(label: "color 2", col: col2);
1037 }
1038
1039 {
1040 // Using the _simplified_ one-liner Combo() api here
1041 // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.
1042 IMGUI_DEMO_MARKER("Widgets/Basic/Combo");
1043 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" };
1044 static int item_current = 0;
1045 ImGui::Combo(label: "combo", current_item: &item_current, items, IM_ARRAYSIZE(items));
1046 ImGui::SameLine(); HelpMarker(
1047 desc: "Using the simplified one-liner Combo API here.\n"
1048 "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.");
1049 }
1050
1051 {
1052 // Using the _simplified_ one-liner ListBox() api here
1053 // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.
1054 IMGUI_DEMO_MARKER("Widgets/Basic/ListBox");
1055 const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
1056 static int item_current = 1;
1057 ImGui::ListBox(label: "listbox", current_item: &item_current, items, IM_ARRAYSIZE(items), height_in_items: 4);
1058 ImGui::SameLine(); HelpMarker(
1059 desc: "Using the simplified one-liner ListBox API here.\n"
1060 "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.");
1061 }
1062
1063 // Testing ImGuiOnceUponAFrame helper.
1064 //static ImGuiOnceUponAFrame once;
1065 //for (int i = 0; i < 5; i++)
1066 // if (once)
1067 // ImGui::Text("This will be displayed only once.");
1068
1069 ImGui::TreePop();
1070 }
1071}
1072
1073//-----------------------------------------------------------------------------
1074// [SECTION] DemoWindowWidgetsBullets()
1075//-----------------------------------------------------------------------------
1076
1077static void DemoWindowWidgetsBullets()
1078{
1079 IMGUI_DEMO_MARKER("Widgets/Bullets");
1080 if (ImGui::TreeNode(label: "Bullets"))
1081 {
1082 ImGui::BulletText(fmt: "Bullet point 1");
1083 ImGui::BulletText(fmt: "Bullet point 2\nOn multiple lines");
1084 if (ImGui::TreeNode(label: "Tree node"))
1085 {
1086 ImGui::BulletText(fmt: "Another bullet point");
1087 ImGui::TreePop();
1088 }
1089 ImGui::Bullet(); ImGui::Text(fmt: "Bullet point 3 (two calls)");
1090 ImGui::Bullet(); ImGui::SmallButton(label: "Button");
1091 ImGui::TreePop();
1092 }
1093}
1094
1095//-----------------------------------------------------------------------------
1096// [SECTION] DemoWindowWidgetsCollapsingHeaders()
1097//-----------------------------------------------------------------------------
1098
1099static void DemoWindowWidgetsCollapsingHeaders()
1100{
1101 IMGUI_DEMO_MARKER("Widgets/Collapsing Headers");
1102 if (ImGui::TreeNode(label: "Collapsing Headers"))
1103 {
1104 static bool closable_group = true;
1105 ImGui::Checkbox(label: "Show 2nd header", v: &closable_group);
1106 if (ImGui::CollapsingHeader(label: "Header", flags: ImGuiTreeNodeFlags_None))
1107 {
1108 ImGui::Text(fmt: "IsItemHovered: %d", ImGui::IsItemHovered());
1109 for (int i = 0; i < 5; i++)
1110 ImGui::Text(fmt: "Some content %d", i);
1111 }
1112 if (ImGui::CollapsingHeader(label: "Header with a close button", p_visible: &closable_group))
1113 {
1114 ImGui::Text(fmt: "IsItemHovered: %d", ImGui::IsItemHovered());
1115 for (int i = 0; i < 5; i++)
1116 ImGui::Text(fmt: "More content %d", i);
1117 }
1118 /*
1119 if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet))
1120 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1121 */
1122 ImGui::TreePop();
1123 }
1124}
1125
1126//-----------------------------------------------------------------------------
1127// [SECTION] DemoWindowWidgetsColorAndPickers()
1128//-----------------------------------------------------------------------------
1129
1130static void DemoWindowWidgetsColorAndPickers()
1131{
1132 IMGUI_DEMO_MARKER("Widgets/Color");
1133 if (ImGui::TreeNode(label: "Color/Picker Widgets"))
1134 {
1135 static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);
1136 static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None;
1137
1138 ImGui::SeparatorText(label: "Options");
1139 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoAlpha", flags: &base_flags, flags_value: ImGuiColorEditFlags_NoAlpha);
1140 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaOpaque", flags: &base_flags, flags_value: ImGuiColorEditFlags_AlphaOpaque);
1141 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaNoBg", flags: &base_flags, flags_value: ImGuiColorEditFlags_AlphaNoBg);
1142 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaPreviewHalf", flags: &base_flags, flags_value: ImGuiColorEditFlags_AlphaPreviewHalf);
1143 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoDragDrop", flags: &base_flags, flags_value: ImGuiColorEditFlags_NoDragDrop);
1144 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoOptions", flags: &base_flags, flags_value: ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker(desc: "Right-click on the individual color widget to show options.");
1145 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_HDR", flags: &base_flags, flags_value: ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker(desc: "Currently all this does is to lift the 0..1 limits on dragging widgets.");
1146
1147 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit");
1148 ImGui::SeparatorText(label: "Inline color editor");
1149 ImGui::Text(fmt: "Color widget:");
1150 ImGui::SameLine(); HelpMarker(
1151 desc: "Click on the color square to open a color picker.\n"
1152 "CTRL+click on individual component to input value.\n");
1153 ImGui::ColorEdit3(label: "MyColor##1", col: (float*)&color, flags: base_flags);
1154
1155 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)");
1156 ImGui::Text(fmt: "Color widget HSV with Alpha:");
1157 ImGui::ColorEdit4(label: "MyColor##2", col: (float*)&color, flags: ImGuiColorEditFlags_DisplayHSV | base_flags);
1158
1159 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)");
1160 ImGui::Text(fmt: "Color widget with Float Display:");
1161 ImGui::ColorEdit4(label: "MyColor##2f", col: (float*)&color, flags: ImGuiColorEditFlags_Float | base_flags);
1162
1163 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)");
1164 ImGui::Text(fmt: "Color button with Picker:");
1165 ImGui::SameLine(); HelpMarker(
1166 desc: "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n"
1167 "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only "
1168 "be used for the tooltip and picker popup.");
1169 ImGui::ColorEdit4(label: "MyColor##3", col: (float*)&color, flags: ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags);
1170
1171 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)");
1172 ImGui::Text(fmt: "Color button with Custom Picker Popup:");
1173
1174 // Generate a default palette. The palette will persist and can be edited.
1175 static bool saved_palette_init = true;
1176 static ImVec4 saved_palette[32] = {};
1177 if (saved_palette_init)
1178 {
1179 for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1180 {
1181 ImGui::ColorConvertHSVtoRGB(h: n / 31.0f, s: 0.8f, v: 0.8f,
1182 out_r&: saved_palette[n].x, out_g&: saved_palette[n].y, out_b&: saved_palette[n].z);
1183 saved_palette[n].w = 1.0f; // Alpha
1184 }
1185 saved_palette_init = false;
1186 }
1187
1188 static ImVec4 backup_color;
1189 bool open_popup = ImGui::ColorButton(desc_id: "MyColor##3b", col: color, flags: base_flags);
1190 ImGui::SameLine(offset_from_start_x: 0, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
1191 open_popup |= ImGui::Button(label: "Palette");
1192 if (open_popup)
1193 {
1194 ImGui::OpenPopup(str_id: "mypicker");
1195 backup_color = color;
1196 }
1197 if (ImGui::BeginPopup(str_id: "mypicker"))
1198 {
1199 ImGui::Text(fmt: "MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!");
1200 ImGui::Separator();
1201 ImGui::ColorPicker4(label: "##picker", col: (float*)&color, flags: base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);
1202 ImGui::SameLine();
1203
1204 ImGui::BeginGroup(); // Lock X position
1205 ImGui::Text(fmt: "Current");
1206 ImGui::ColorButton(desc_id: "##current", col: color, flags: ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, size: ImVec2(60, 40));
1207 ImGui::Text(fmt: "Previous");
1208 if (ImGui::ColorButton(desc_id: "##previous", col: backup_color, flags: ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, size: ImVec2(60, 40)))
1209 color = backup_color;
1210 ImGui::Separator();
1211 ImGui::Text(fmt: "Palette");
1212 for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1213 {
1214 ImGui::PushID(int_id: n);
1215 if ((n % 8) != 0)
1216 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemSpacing.y);
1217
1218 ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
1219 if (ImGui::ColorButton(desc_id: "##palette", col: saved_palette[n], flags: palette_button_flags, size: ImVec2(20, 20)))
1220 color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
1221
1222 // Allow user to drop colors into each palette entry. Note that ColorButton() is already a
1223 // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
1224 if (ImGui::BeginDragDropTarget())
1225 {
1226 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
1227 memcpy(dest: (float*)&saved_palette[n], src: payload->Data, n: sizeof(float) * 3);
1228 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
1229 memcpy(dest: (float*)&saved_palette[n], src: payload->Data, n: sizeof(float) * 4);
1230 ImGui::EndDragDropTarget();
1231 }
1232
1233 ImGui::PopID();
1234 }
1235 ImGui::EndGroup();
1236 ImGui::EndPopup();
1237 }
1238
1239 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)");
1240 ImGui::Text(fmt: "Color button only:");
1241 static bool no_border = false;
1242 ImGui::Checkbox(label: "ImGuiColorEditFlags_NoBorder", v: &no_border);
1243 ImGui::ColorButton(desc_id: "MyColor##3c", col: *(ImVec4*)&color, flags: base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), size: ImVec2(80, 80));
1244
1245 IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker");
1246 ImGui::SeparatorText(label: "Color picker");
1247
1248 static bool ref_color = false;
1249 static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);
1250 static int picker_mode = 0;
1251 static int display_mode = 0;
1252 static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar;
1253
1254 ImGui::PushID(str_id: "Color picker");
1255 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoAlpha", flags: &color_picker_flags, flags_value: ImGuiColorEditFlags_NoAlpha);
1256 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaBar", flags: &color_picker_flags, flags_value: ImGuiColorEditFlags_AlphaBar);
1257 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoSidePreview", flags: &color_picker_flags, flags_value: ImGuiColorEditFlags_NoSidePreview);
1258 if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview)
1259 {
1260 ImGui::SameLine();
1261 ImGui::Checkbox(label: "With Ref Color", v: &ref_color);
1262 if (ref_color)
1263 {
1264 ImGui::SameLine();
1265 ImGui::ColorEdit4(label: "##RefColor", col: &ref_color_v.x, flags: ImGuiColorEditFlags_NoInputs | base_flags);
1266 }
1267 }
1268
1269 ImGui::Combo(label: "Picker Mode", current_item: &picker_mode, items_separated_by_zeros: "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0");
1270 ImGui::SameLine(); HelpMarker(desc: "When not specified explicitly, user can right-click the picker to change mode.");
1271
1272 ImGui::Combo(label: "Display Mode", current_item: &display_mode, items_separated_by_zeros: "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0");
1273 ImGui::SameLine(); HelpMarker(
1274 desc: "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
1275 "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
1276 "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
1277
1278 ImGuiColorEditFlags flags = base_flags | color_picker_flags;
1279 if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar;
1280 if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel;
1281 if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays
1282 if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode
1283 if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV;
1284 if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;
1285 ImGui::ColorPicker4(label: "MyColor##4", col: (float*)&color, flags, ref_col: ref_color ? &ref_color_v.x : NULL);
1286
1287 ImGui::Text(fmt: "Set defaults in code:");
1288 ImGui::SameLine(); HelpMarker(
1289 desc: "SetColorEditOptions() is designed to allow you to set boot-time default.\n"
1290 "We don't have Push/Pop functions because you can force options on a per-widget basis if needed, "
1291 "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid "
1292 "encouraging you to persistently save values that aren't forward-compatible.");
1293 if (ImGui::Button(label: "Default: Uint8 + HSV + Hue Bar"))
1294 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);
1295 if (ImGui::Button(label: "Default: Float + HDR + Hue Wheel"))
1296 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
1297
1298 // Always display a small version of both types of pickers
1299 // (that's in order to make it more visible in the demo to people who are skimming quickly through it)
1300 ImGui::Text(fmt: "Both types:");
1301 float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;
1302 ImGui::SetNextItemWidth(w);
1303 ImGui::ColorPicker3(label: "##MyColor##5", col: (float*)&color, flags: ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1304 ImGui::SameLine();
1305 ImGui::SetNextItemWidth(w);
1306 ImGui::ColorPicker3(label: "##MyColor##6", col: (float*)&color, flags: ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1307 ImGui::PopID();
1308
1309 // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
1310 static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
1311 ImGui::Spacing();
1312 ImGui::Text(fmt: "HSV encoded colors");
1313 ImGui::SameLine(); HelpMarker(
1314 desc: "By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV "
1315 "allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the "
1316 "added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
1317 ImGui::Text(fmt: "Color widget with InputHSV:");
1318 ImGui::ColorEdit4(label: "HSV shown as RGB##1", col: (float*)&color_hsv, flags: ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1319 ImGui::ColorEdit4(label: "HSV shown as HSV##1", col: (float*)&color_hsv, flags: ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1320 ImGui::DragFloat4(label: "Raw HSV values", v: (float*)&color_hsv, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
1321
1322 ImGui::TreePop();
1323 }
1324}
1325
1326//-----------------------------------------------------------------------------
1327// [SECTION] DemoWindowWidgetsComboBoxes()
1328//-----------------------------------------------------------------------------
1329
1330static void DemoWindowWidgetsComboBoxes()
1331{
1332 IMGUI_DEMO_MARKER("Widgets/Combo");
1333 if (ImGui::TreeNode(label: "Combo"))
1334 {
1335 // Combo Boxes are also called "Dropdown" in other systems
1336 // Expose flags as checkbox for the demo
1337 static ImGuiComboFlags flags = 0;
1338 ImGui::CheckboxFlags(label: "ImGuiComboFlags_PopupAlignLeft", flags: &flags, flags_value: ImGuiComboFlags_PopupAlignLeft);
1339 ImGui::SameLine(); HelpMarker(desc: "Only makes a difference if the popup is larger than the combo");
1340 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_NoArrowButton", flags: &flags, flags_value: ImGuiComboFlags_NoArrowButton))
1341 flags &= ~ImGuiComboFlags_NoPreview; // Clear incompatible flags
1342 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_NoPreview", flags: &flags, flags_value: ImGuiComboFlags_NoPreview))
1343 flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags
1344 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_WidthFitPreview", flags: &flags, flags_value: ImGuiComboFlags_WidthFitPreview))
1345 flags &= ~ImGuiComboFlags_NoPreview;
1346
1347 // Override default popup height
1348 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_HeightSmall", flags: &flags, flags_value: ImGuiComboFlags_HeightSmall))
1349 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall);
1350 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_HeightRegular", flags: &flags, flags_value: ImGuiComboFlags_HeightRegular))
1351 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular);
1352 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_HeightLargest", flags: &flags, flags_value: ImGuiComboFlags_HeightLargest))
1353 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest);
1354
1355 // Using the generic BeginCombo() API, you have full control over how to display the combo contents.
1356 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1357 // stored in the object itself, etc.)
1358 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1359 static int item_selected_idx = 0; // Here we store our selection data as an index.
1360
1361 // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[])
1362 const char* combo_preview_value = items[item_selected_idx];
1363 if (ImGui::BeginCombo(label: "combo 1", preview_value: combo_preview_value, flags))
1364 {
1365 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1366 {
1367 const bool is_selected = (item_selected_idx == n);
1368 if (ImGui::Selectable(label: items[n], selected: is_selected))
1369 item_selected_idx = n;
1370
1371 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1372 if (is_selected)
1373 ImGui::SetItemDefaultFocus();
1374 }
1375 ImGui::EndCombo();
1376 }
1377
1378 // Show case embedding a filter using a simple trick: displaying the filter inside combo contents.
1379 // See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives.
1380 if (ImGui::BeginCombo(label: "combo 2 (w/ filter)", preview_value: combo_preview_value, flags))
1381 {
1382 static ImGuiTextFilter filter;
1383 if (ImGui::IsWindowAppearing())
1384 {
1385 ImGui::SetKeyboardFocusHere();
1386 filter.Clear();
1387 }
1388 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_F);
1389 filter.Draw(label: "##Filter", width: -FLT_MIN);
1390
1391 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1392 {
1393 const bool is_selected = (item_selected_idx == n);
1394 if (filter.PassFilter(text: items[n]))
1395 if (ImGui::Selectable(label: items[n], selected: is_selected))
1396 item_selected_idx = n;
1397 }
1398 ImGui::EndCombo();
1399 }
1400
1401 ImGui::Spacing();
1402 ImGui::SeparatorText(label: "One-liner variants");
1403 HelpMarker(desc: "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.");
1404
1405 // Simplified one-liner Combo() API, using values packed in a single constant string
1406 // This is a convenience for when the selection set is small and known at compile-time.
1407 static int item_current_2 = 0;
1408 ImGui::Combo(label: "combo 3 (one-liner)", current_item: &item_current_2, items_separated_by_zeros: "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
1409
1410 // Simplified one-liner Combo() using an array of const char*
1411 // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
1412 static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview
1413 ImGui::Combo(label: "combo 4 (array)", current_item: &item_current_3, items, IM_ARRAYSIZE(items));
1414
1415 // Simplified one-liner Combo() using an accessor function
1416 static int item_current_4 = 0;
1417 ImGui::Combo(label: "combo 5 (function)", current_item: &item_current_4, getter: [](void* data, int n) { return ((const char**)data)[n]; }, user_data: items, IM_ARRAYSIZE(items));
1418
1419 ImGui::TreePop();
1420 }
1421}
1422
1423//-----------------------------------------------------------------------------
1424// [SECTION] DemoWindowWidgetsDataTypes()
1425//-----------------------------------------------------------------------------
1426
1427static void DemoWindowWidgetsDataTypes()
1428{
1429 IMGUI_DEMO_MARKER("Widgets/Data Types");
1430 if (ImGui::TreeNode(label: "Data Types"))
1431 {
1432 // DragScalar/InputScalar/SliderScalar functions allow various data types
1433 // - signed/unsigned
1434 // - 8/16/32/64-bits
1435 // - integer/float/double
1436 // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
1437 // to pass the type, and passing all arguments by pointer.
1438 // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type.
1439 // In practice, if you frequently use a given type that is not covered by the normal API entry points,
1440 // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
1441 // and then pass their address to the generic function. For example:
1442 // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
1443 // {
1444 // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
1445 // }
1446
1447 // Setup limits (as helper variables so we can take their address, as explained above)
1448 // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
1449 #ifndef LLONG_MIN
1450 ImS64 LLONG_MIN = -9223372036854775807LL - 1;
1451 ImS64 LLONG_MAX = 9223372036854775807LL;
1452 ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);
1453 #endif
1454 const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127;
1455 const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255;
1456 const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767;
1457 const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535;
1458 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;
1459 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;
1460 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;
1461 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;
1462 const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;
1463 const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;
1464
1465 // State
1466 static char s8_v = 127;
1467 static ImU8 u8_v = 255;
1468 static short s16_v = 32767;
1469 static ImU16 u16_v = 65535;
1470 static ImS32 s32_v = -1;
1471 static ImU32 u32_v = (ImU32)-1;
1472 static ImS64 s64_v = -1;
1473 static ImU64 u64_v = (ImU64)-1;
1474 static float f32_v = 0.123f;
1475 static double f64_v = 90000.01234567890123456789;
1476
1477 const float drag_speed = 0.2f;
1478 static bool drag_clamp = false;
1479 IMGUI_DEMO_MARKER("Widgets/Data Types/Drags");
1480 ImGui::SeparatorText(label: "Drags");
1481 ImGui::Checkbox(label: "Clamp integers to 0..50", v: &drag_clamp);
1482 ImGui::SameLine(); HelpMarker(
1483 desc: "As with every widget in dear imgui, we never modify values unless there is a user interaction.\n"
1484 "You can override the clamping limits by using CTRL+Click to input a value.");
1485 ImGui::DragScalar(label: "drag s8", data_type: ImGuiDataType_S8, p_data: &s8_v, v_speed: drag_speed, p_min: drag_clamp ? &s8_zero : NULL, p_max: drag_clamp ? &s8_fifty : NULL);
1486 ImGui::DragScalar(label: "drag u8", data_type: ImGuiDataType_U8, p_data: &u8_v, v_speed: drag_speed, p_min: drag_clamp ? &u8_zero : NULL, p_max: drag_clamp ? &u8_fifty : NULL, format: "%u ms");
1487 ImGui::DragScalar(label: "drag s16", data_type: ImGuiDataType_S16, p_data: &s16_v, v_speed: drag_speed, p_min: drag_clamp ? &s16_zero : NULL, p_max: drag_clamp ? &s16_fifty : NULL);
1488 ImGui::DragScalar(label: "drag u16", data_type: ImGuiDataType_U16, p_data: &u16_v, v_speed: drag_speed, p_min: drag_clamp ? &u16_zero : NULL, p_max: drag_clamp ? &u16_fifty : NULL, format: "%u ms");
1489 ImGui::DragScalar(label: "drag s32", data_type: ImGuiDataType_S32, p_data: &s32_v, v_speed: drag_speed, p_min: drag_clamp ? &s32_zero : NULL, p_max: drag_clamp ? &s32_fifty : NULL);
1490 ImGui::DragScalar(label: "drag s32 hex", data_type: ImGuiDataType_S32, p_data: &s32_v, v_speed: drag_speed, p_min: drag_clamp ? &s32_zero : NULL, p_max: drag_clamp ? &s32_fifty : NULL, format: "0x%08X");
1491 ImGui::DragScalar(label: "drag u32", data_type: ImGuiDataType_U32, p_data: &u32_v, v_speed: drag_speed, p_min: drag_clamp ? &u32_zero : NULL, p_max: drag_clamp ? &u32_fifty : NULL, format: "%u ms");
1492 ImGui::DragScalar(label: "drag s64", data_type: ImGuiDataType_S64, p_data: &s64_v, v_speed: drag_speed, p_min: drag_clamp ? &s64_zero : NULL, p_max: drag_clamp ? &s64_fifty : NULL);
1493 ImGui::DragScalar(label: "drag u64", data_type: ImGuiDataType_U64, p_data: &u64_v, v_speed: drag_speed, p_min: drag_clamp ? &u64_zero : NULL, p_max: drag_clamp ? &u64_fifty : NULL);
1494 ImGui::DragScalar(label: "drag float", data_type: ImGuiDataType_Float, p_data: &f32_v, v_speed: 0.005f, p_min: &f32_zero, p_max: &f32_one, format: "%f");
1495 ImGui::DragScalar(label: "drag float log", data_type: ImGuiDataType_Float, p_data: &f32_v, v_speed: 0.005f, p_min: &f32_zero, p_max: &f32_one, format: "%f", flags: ImGuiSliderFlags_Logarithmic);
1496 ImGui::DragScalar(label: "drag double", data_type: ImGuiDataType_Double, p_data: &f64_v, v_speed: 0.0005f, p_min: &f64_zero, NULL, format: "%.10f grams");
1497 ImGui::DragScalar(label: "drag double log",data_type: ImGuiDataType_Double, p_data: &f64_v, v_speed: 0.0005f, p_min: &f64_zero, p_max: &f64_one, format: "0 < %.10f < 1", flags: ImGuiSliderFlags_Logarithmic);
1498
1499 IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders");
1500 ImGui::SeparatorText(label: "Sliders");
1501 ImGui::SliderScalar(label: "slider s8 full", data_type: ImGuiDataType_S8, p_data: &s8_v, p_min: &s8_min, p_max: &s8_max, format: "%d");
1502 ImGui::SliderScalar(label: "slider u8 full", data_type: ImGuiDataType_U8, p_data: &u8_v, p_min: &u8_min, p_max: &u8_max, format: "%u");
1503 ImGui::SliderScalar(label: "slider s16 full", data_type: ImGuiDataType_S16, p_data: &s16_v, p_min: &s16_min, p_max: &s16_max, format: "%d");
1504 ImGui::SliderScalar(label: "slider u16 full", data_type: ImGuiDataType_U16, p_data: &u16_v, p_min: &u16_min, p_max: &u16_max, format: "%u");
1505 ImGui::SliderScalar(label: "slider s32 low", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_zero, p_max: &s32_fifty,format: "%d");
1506 ImGui::SliderScalar(label: "slider s32 high", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_hi_a, p_max: &s32_hi_b, format: "%d");
1507 ImGui::SliderScalar(label: "slider s32 full", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_min, p_max: &s32_max, format: "%d");
1508 ImGui::SliderScalar(label: "slider s32 hex", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_zero, p_max: &s32_fifty, format: "0x%04X");
1509 ImGui::SliderScalar(label: "slider u32 low", data_type: ImGuiDataType_U32, p_data: &u32_v, p_min: &u32_zero, p_max: &u32_fifty,format: "%u");
1510 ImGui::SliderScalar(label: "slider u32 high", data_type: ImGuiDataType_U32, p_data: &u32_v, p_min: &u32_hi_a, p_max: &u32_hi_b, format: "%u");
1511 ImGui::SliderScalar(label: "slider u32 full", data_type: ImGuiDataType_U32, p_data: &u32_v, p_min: &u32_min, p_max: &u32_max, format: "%u");
1512 ImGui::SliderScalar(label: "slider s64 low", data_type: ImGuiDataType_S64, p_data: &s64_v, p_min: &s64_zero, p_max: &s64_fifty,format: "%" PRId64);
1513 ImGui::SliderScalar(label: "slider s64 high", data_type: ImGuiDataType_S64, p_data: &s64_v, p_min: &s64_hi_a, p_max: &s64_hi_b, format: "%" PRId64);
1514 ImGui::SliderScalar(label: "slider s64 full", data_type: ImGuiDataType_S64, p_data: &s64_v, p_min: &s64_min, p_max: &s64_max, format: "%" PRId64);
1515 ImGui::SliderScalar(label: "slider u64 low", data_type: ImGuiDataType_U64, p_data: &u64_v, p_min: &u64_zero, p_max: &u64_fifty,format: "%" PRIu64 " ms");
1516 ImGui::SliderScalar(label: "slider u64 high", data_type: ImGuiDataType_U64, p_data: &u64_v, p_min: &u64_hi_a, p_max: &u64_hi_b, format: "%" PRIu64 " ms");
1517 ImGui::SliderScalar(label: "slider u64 full", data_type: ImGuiDataType_U64, p_data: &u64_v, p_min: &u64_min, p_max: &u64_max, format: "%" PRIu64 " ms");
1518 ImGui::SliderScalar(label: "slider float low", data_type: ImGuiDataType_Float, p_data: &f32_v, p_min: &f32_zero, p_max: &f32_one);
1519 ImGui::SliderScalar(label: "slider float low log", data_type: ImGuiDataType_Float, p_data: &f32_v, p_min: &f32_zero, p_max: &f32_one, format: "%.10f", flags: ImGuiSliderFlags_Logarithmic);
1520 ImGui::SliderScalar(label: "slider float high", data_type: ImGuiDataType_Float, p_data: &f32_v, p_min: &f32_lo_a, p_max: &f32_hi_a, format: "%e");
1521 ImGui::SliderScalar(label: "slider double low", data_type: ImGuiDataType_Double, p_data: &f64_v, p_min: &f64_zero, p_max: &f64_one, format: "%.10f grams");
1522 ImGui::SliderScalar(label: "slider double low log",data_type: ImGuiDataType_Double, p_data: &f64_v, p_min: &f64_zero, p_max: &f64_one, format: "%.10f", flags: ImGuiSliderFlags_Logarithmic);
1523 ImGui::SliderScalar(label: "slider double high", data_type: ImGuiDataType_Double, p_data: &f64_v, p_min: &f64_lo_a, p_max: &f64_hi_a, format: "%e grams");
1524
1525 ImGui::SeparatorText(label: "Sliders (reverse)");
1526 ImGui::SliderScalar(label: "slider s8 reverse", data_type: ImGuiDataType_S8, p_data: &s8_v, p_min: &s8_max, p_max: &s8_min, format: "%d");
1527 ImGui::SliderScalar(label: "slider u8 reverse", data_type: ImGuiDataType_U8, p_data: &u8_v, p_min: &u8_max, p_max: &u8_min, format: "%u");
1528 ImGui::SliderScalar(label: "slider s32 reverse", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_fifty, p_max: &s32_zero, format: "%d");
1529 ImGui::SliderScalar(label: "slider u32 reverse", data_type: ImGuiDataType_U32, p_data: &u32_v, p_min: &u32_fifty, p_max: &u32_zero, format: "%u");
1530 ImGui::SliderScalar(label: "slider s64 reverse", data_type: ImGuiDataType_S64, p_data: &s64_v, p_min: &s64_fifty, p_max: &s64_zero, format: "%" PRId64);
1531 ImGui::SliderScalar(label: "slider u64 reverse", data_type: ImGuiDataType_U64, p_data: &u64_v, p_min: &u64_fifty, p_max: &u64_zero, format: "%" PRIu64 " ms");
1532
1533 IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs");
1534 static bool inputs_step = true;
1535 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
1536 ImGui::SeparatorText(label: "Inputs");
1537 ImGui::Checkbox(label: "Show step buttons", v: &inputs_step);
1538 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ReadOnly", flags: &flags, flags_value: ImGuiInputTextFlags_ReadOnly);
1539 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ParseEmptyRefVal", flags: &flags, flags_value: ImGuiInputTextFlags_ParseEmptyRefVal);
1540 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_DisplayEmptyRefVal", flags: &flags, flags_value: ImGuiInputTextFlags_DisplayEmptyRefVal);
1541 ImGui::InputScalar(label: "input s8", data_type: ImGuiDataType_S8, p_data: &s8_v, p_step: inputs_step ? &s8_one : NULL, NULL, format: "%d", flags);
1542 ImGui::InputScalar(label: "input u8", data_type: ImGuiDataType_U8, p_data: &u8_v, p_step: inputs_step ? &u8_one : NULL, NULL, format: "%u", flags);
1543 ImGui::InputScalar(label: "input s16", data_type: ImGuiDataType_S16, p_data: &s16_v, p_step: inputs_step ? &s16_one : NULL, NULL, format: "%d", flags);
1544 ImGui::InputScalar(label: "input u16", data_type: ImGuiDataType_U16, p_data: &u16_v, p_step: inputs_step ? &u16_one : NULL, NULL, format: "%u", flags);
1545 ImGui::InputScalar(label: "input s32", data_type: ImGuiDataType_S32, p_data: &s32_v, p_step: inputs_step ? &s32_one : NULL, NULL, format: "%d", flags);
1546 ImGui::InputScalar(label: "input s32 hex", data_type: ImGuiDataType_S32, p_data: &s32_v, p_step: inputs_step ? &s32_one : NULL, NULL, format: "%04X", flags);
1547 ImGui::InputScalar(label: "input u32", data_type: ImGuiDataType_U32, p_data: &u32_v, p_step: inputs_step ? &u32_one : NULL, NULL, format: "%u", flags);
1548 ImGui::InputScalar(label: "input u32 hex", data_type: ImGuiDataType_U32, p_data: &u32_v, p_step: inputs_step ? &u32_one : NULL, NULL, format: "%08X", flags);
1549 ImGui::InputScalar(label: "input s64", data_type: ImGuiDataType_S64, p_data: &s64_v, p_step: inputs_step ? &s64_one : NULL, NULL, NULL, flags);
1550 ImGui::InputScalar(label: "input u64", data_type: ImGuiDataType_U64, p_data: &u64_v, p_step: inputs_step ? &u64_one : NULL, NULL, NULL, flags);
1551 ImGui::InputScalar(label: "input float", data_type: ImGuiDataType_Float, p_data: &f32_v, p_step: inputs_step ? &f32_one : NULL, NULL, NULL, flags);
1552 ImGui::InputScalar(label: "input double", data_type: ImGuiDataType_Double, p_data: &f64_v, p_step: inputs_step ? &f64_one : NULL, NULL, NULL, flags);
1553
1554 ImGui::TreePop();
1555 }
1556}
1557
1558//-----------------------------------------------------------------------------
1559// [SECTION] DemoWindowWidgetsDisableBlocks()
1560//-----------------------------------------------------------------------------
1561
1562static void DemoWindowWidgetsDisableBlocks(ImGuiDemoWindowData* demo_data)
1563{
1564 IMGUI_DEMO_MARKER("Widgets/Disable Blocks");
1565 if (ImGui::TreeNode(label: "Disable Blocks"))
1566 {
1567 ImGui::Checkbox(label: "Disable entire section above", v: &demo_data->DisableSections);
1568 ImGui::SameLine(); HelpMarker(desc: "Demonstrate using BeginDisabled()/EndDisabled() across other sections.");
1569 ImGui::TreePop();
1570 }
1571}
1572
1573//-----------------------------------------------------------------------------
1574// [SECTION] DemoWindowWidgetsDragAndDrop()
1575//-----------------------------------------------------------------------------
1576
1577static void DemoWindowWidgetsDragAndDrop()
1578{
1579 IMGUI_DEMO_MARKER("Widgets/Drag and drop");
1580 if (ImGui::TreeNode(label: "Drag and Drop"))
1581 {
1582 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets");
1583 if (ImGui::TreeNode(label: "Drag and drop in standard widgets"))
1584 {
1585 // ColorEdit widgets automatically act as drag source and drag target.
1586 // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
1587 // to allow your own widgets to use colors in their drag and drop interaction.
1588 // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
1589 HelpMarker(desc: "You can drag from the color squares.");
1590 static float col1[3] = { 1.0f, 0.0f, 0.2f };
1591 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1592 ImGui::ColorEdit3(label: "color 1", col: col1);
1593 ImGui::ColorEdit4(label: "color 2", col: col2);
1594 ImGui::TreePop();
1595 }
1596
1597 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items");
1598 if (ImGui::TreeNode(label: "Drag and drop to copy/swap items"))
1599 {
1600 enum Mode
1601 {
1602 Mode_Copy,
1603 Mode_Move,
1604 Mode_Swap
1605 };
1606 static int mode = 0;
1607 if (ImGui::RadioButton(label: "Copy", active: mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();
1608 if (ImGui::RadioButton(label: "Move", active: mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();
1609 if (ImGui::RadioButton(label: "Swap", active: mode == Mode_Swap)) { mode = Mode_Swap; }
1610 static const char* names[9] =
1611 {
1612 "Bobby", "Beatrice", "Betty",
1613 "Brianna", "Barry", "Bernard",
1614 "Bibi", "Blaine", "Bryn"
1615 };
1616 for (int n = 0; n < IM_ARRAYSIZE(names); n++)
1617 {
1618 ImGui::PushID(int_id: n);
1619 if ((n % 3) != 0)
1620 ImGui::SameLine();
1621 ImGui::Button(label: names[n], size: ImVec2(60, 60));
1622
1623 // Our buttons are both drag sources and drag targets here!
1624 if (ImGui::BeginDragDropSource(flags: ImGuiDragDropFlags_None))
1625 {
1626 // Set payload to carry the index of our item (could be anything)
1627 ImGui::SetDragDropPayload(type: "DND_DEMO_CELL", data: &n, sz: sizeof(int));
1628
1629 // Display preview (could be anything, e.g. when dragging an image we could decide to display
1630 // the filename and a small preview of the image, etc.)
1631 if (mode == Mode_Copy) { ImGui::Text(fmt: "Copy %s", names[n]); }
1632 if (mode == Mode_Move) { ImGui::Text(fmt: "Move %s", names[n]); }
1633 if (mode == Mode_Swap) { ImGui::Text(fmt: "Swap %s", names[n]); }
1634 ImGui::EndDragDropSource();
1635 }
1636 if (ImGui::BeginDragDropTarget())
1637 {
1638 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(type: "DND_DEMO_CELL"))
1639 {
1640 IM_ASSERT(payload->DataSize == sizeof(int));
1641 int payload_n = *(const int*)payload->Data;
1642 if (mode == Mode_Copy)
1643 {
1644 names[n] = names[payload_n];
1645 }
1646 if (mode == Mode_Move)
1647 {
1648 names[n] = names[payload_n];
1649 names[payload_n] = "";
1650 }
1651 if (mode == Mode_Swap)
1652 {
1653 const char* tmp = names[n];
1654 names[n] = names[payload_n];
1655 names[payload_n] = tmp;
1656 }
1657 }
1658 ImGui::EndDragDropTarget();
1659 }
1660 ImGui::PopID();
1661 }
1662 ImGui::TreePop();
1663 }
1664
1665 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)");
1666 if (ImGui::TreeNode(label: "Drag to reorder items (simple)"))
1667 {
1668 // FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
1669 // This code was always slightly faulty but in a way which was not easily noticeable.
1670 // Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
1671 ImGui::PushItemFlag(option: ImGuiItemFlags_AllowDuplicateId, enabled: true);
1672
1673 // Simple reordering
1674 HelpMarker(
1675 desc: "We don't use the drag and drop api at all here! "
1676 "Instead we query when the item is held but not hovered, and order items accordingly.");
1677 static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
1678 for (int n = 0; n < IM_ARRAYSIZE(item_names); n++)
1679 {
1680 const char* item = item_names[n];
1681 ImGui::Selectable(label: item);
1682
1683 if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
1684 {
1685 int n_next = n + (ImGui::GetMouseDragDelta(button: 0).y < 0.f ? -1 : 1);
1686 if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names))
1687 {
1688 item_names[n] = item_names[n_next];
1689 item_names[n_next] = item;
1690 ImGui::ResetMouseDragDelta();
1691 }
1692 }
1693 }
1694
1695 ImGui::PopItemFlag();
1696 ImGui::TreePop();
1697 }
1698
1699 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location");
1700 if (ImGui::TreeNode(label: "Tooltip at target location"))
1701 {
1702 for (int n = 0; n < 2; n++)
1703 {
1704 // Drop targets
1705 ImGui::Button(label: n ? "drop here##1" : "drop here##0");
1706 if (ImGui::BeginDragDropTarget())
1707 {
1708 ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip;
1709 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, flags: drop_target_flags))
1710 {
1711 IM_UNUSED(payload);
1712 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
1713 ImGui::SetTooltip("Cannot drop here!");
1714 }
1715 ImGui::EndDragDropTarget();
1716 }
1717
1718 // Drop source
1719 static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f };
1720 if (n == 0)
1721 ImGui::ColorButton(desc_id: "drag me", col: col4);
1722
1723 }
1724 ImGui::TreePop();
1725 }
1726
1727 ImGui::TreePop();
1728 }
1729}
1730
1731//-----------------------------------------------------------------------------
1732// [SECTION] DemoWindowWidgetsDragsAndSliders()
1733//-----------------------------------------------------------------------------
1734
1735static void DemoWindowWidgetsDragsAndSliders()
1736{
1737 IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags");
1738 if (ImGui::TreeNode(label: "Drag/Slider Flags"))
1739 {
1740 // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
1741 static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
1742 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_AlwaysClamp", flags: &flags, flags_value: ImGuiSliderFlags_AlwaysClamp);
1743 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_ClampOnInput", flags: &flags, flags_value: ImGuiSliderFlags_ClampOnInput);
1744 ImGui::SameLine(); HelpMarker(desc: "Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.");
1745 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_ClampZeroRange", flags: &flags, flags_value: ImGuiSliderFlags_ClampZeroRange);
1746 ImGui::SameLine(); HelpMarker(desc: "Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
1747 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_Logarithmic", flags: &flags, flags_value: ImGuiSliderFlags_Logarithmic);
1748 ImGui::SameLine(); HelpMarker(desc: "Enable logarithmic editing (more precision for small values).");
1749 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_NoRoundToFormat", flags: &flags, flags_value: ImGuiSliderFlags_NoRoundToFormat);
1750 ImGui::SameLine(); HelpMarker(desc: "Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
1751 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_NoInput", flags: &flags, flags_value: ImGuiSliderFlags_NoInput);
1752 ImGui::SameLine(); HelpMarker(desc: "Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
1753 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_NoSpeedTweaks", flags: &flags, flags_value: ImGuiSliderFlags_NoSpeedTweaks);
1754 ImGui::SameLine(); HelpMarker(desc: "Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
1755 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_WrapAround", flags: &flags, flags_value: ImGuiSliderFlags_WrapAround);
1756 ImGui::SameLine(); HelpMarker(desc: "Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)");
1757
1758 // Drags
1759 static float drag_f = 0.5f;
1760 static int drag_i = 50;
1761 ImGui::Text(fmt: "Underlying float value: %f", drag_f);
1762 ImGui::DragFloat(label: "DragFloat (0 -> 1)", v: &drag_f, v_speed: 0.005f, v_min: 0.0f, v_max: 1.0f, format: "%.3f", flags);
1763 ImGui::DragFloat(label: "DragFloat (0 -> +inf)", v: &drag_f, v_speed: 0.005f, v_min: 0.0f, FLT_MAX, format: "%.3f", flags);
1764 ImGui::DragFloat(label: "DragFloat (-inf -> 1)", v: &drag_f, v_speed: 0.005f, v_min: -FLT_MAX, v_max: 1.0f, format: "%.3f", flags);
1765 ImGui::DragFloat(label: "DragFloat (-inf -> +inf)", v: &drag_f, v_speed: 0.005f, v_min: -FLT_MAX, v_max: +FLT_MAX, format: "%.3f", flags);
1766 //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange
1767 //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags);
1768 ImGui::DragInt(label: "DragInt (0 -> 100)", v: &drag_i, v_speed: 0.5f, v_min: 0, v_max: 100, format: "%d", flags);
1769
1770 // Sliders
1771 static float slider_f = 0.5f;
1772 static int slider_i = 50;
1773 const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround;
1774 ImGui::Text(fmt: "Underlying float value: %f", slider_f);
1775 ImGui::SliderFloat(label: "SliderFloat (0 -> 1)", v: &slider_f, v_min: 0.0f, v_max: 1.0f, format: "%.3f", flags: flags_for_sliders);
1776 ImGui::SliderInt(label: "SliderInt (0 -> 100)", v: &slider_i, v_min: 0, v_max: 100, format: "%d", flags: flags_for_sliders);
1777
1778 ImGui::TreePop();
1779 }
1780}
1781
1782//-----------------------------------------------------------------------------
1783// [SECTION] DemoWindowWidgetsImages()
1784//-----------------------------------------------------------------------------
1785
1786static void DemoWindowWidgetsImages()
1787{
1788 IMGUI_DEMO_MARKER("Widgets/Images");
1789 if (ImGui::TreeNode(label: "Images"))
1790 {
1791 ImGuiIO& io = ImGui::GetIO();
1792 ImGui::TextWrapped(
1793 fmt: "Below we are displaying the font texture (which is the only texture we have access to in this demo). "
1794 "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. "
1795 "Hover the texture for a zoomed view!");
1796
1797 // Below we are displaying the font texture because it is the only texture we have access to inside the demo!
1798 // Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that
1799 // will be passed to the rendering backend via the ImDrawCmd structure.
1800 // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
1801 // of their respective source file to specify what they expect to be stored in ImTextureID, for example:
1802 // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer
1803 // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
1804 // More:
1805 // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
1806 // to ImGui::Image(), and gather width/height through your own functions, etc.
1807 // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
1808 // it will help you debug issues if you are confused about it.
1809 // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
1810 // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
1811 // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1812 ImTextureID my_tex_id = io.Fonts->TexID;
1813 float my_tex_w = (float)io.Fonts->TexWidth;
1814 float my_tex_h = (float)io.Fonts->TexHeight;
1815 {
1816 ImGui::Text(fmt: "%.0fx%.0f", my_tex_w, my_tex_h);
1817 ImVec2 pos = ImGui::GetCursorScreenPos();
1818 ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
1819 ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
1820 ImGui::PushStyleVar(idx: ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
1821 ImGui::ImageWithBg(user_texture_id: my_tex_id, image_size: ImVec2(my_tex_w, my_tex_h), uv0: uv_min, uv1: uv_max, bg_col: ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
1822 if (ImGui::BeginItemTooltip())
1823 {
1824 float region_sz = 32.0f;
1825 float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
1826 float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
1827 float zoom = 4.0f;
1828 if (region_x < 0.0f) { region_x = 0.0f; }
1829 else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }
1830 if (region_y < 0.0f) { region_y = 0.0f; }
1831 else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }
1832 ImGui::Text(fmt: "Min: (%.2f, %.2f)", region_x, region_y);
1833 ImGui::Text(fmt: "Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
1834 ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
1835 ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
1836 ImGui::ImageWithBg(user_texture_id: my_tex_id, image_size: ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, bg_col: ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
1837 ImGui::EndTooltip();
1838 }
1839 ImGui::PopStyleVar();
1840 }
1841
1842 IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons");
1843 ImGui::TextWrapped(fmt: "And now some textured buttons..");
1844 static int pressed_count = 0;
1845 for (int i = 0; i < 8; i++)
1846 {
1847 // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.
1848 // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.
1849 // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1850 ImGui::PushID(int_id: i);
1851 if (i > 0)
1852 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(i - 1.0f, i - 1.0f));
1853 ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible
1854 ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left
1855 ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h); // UV coordinates for (32,32) in our texture
1856 ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background
1857 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
1858 if (ImGui::ImageButton(str_id: "", user_texture_id: my_tex_id, image_size: size, uv0, uv1, bg_col, tint_col))
1859 pressed_count += 1;
1860 if (i > 0)
1861 ImGui::PopStyleVar();
1862 ImGui::PopID();
1863 ImGui::SameLine();
1864 }
1865 ImGui::NewLine();
1866 ImGui::Text(fmt: "Pressed %d times.", pressed_count);
1867 ImGui::TreePop();
1868 }
1869}
1870
1871//-----------------------------------------------------------------------------
1872// [SECTION] DemoWindowWidgetsListBoxes()
1873//-----------------------------------------------------------------------------
1874
1875static void DemoWindowWidgetsListBoxes()
1876{
1877 IMGUI_DEMO_MARKER("Widgets/List Boxes");
1878 if (ImGui::TreeNode(label: "List Boxes"))
1879 {
1880 // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild()
1881 // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
1882 // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild()
1883 // to always be called (inconsistent with BeginListBox()/EndListBox()).
1884
1885 // Using the generic BeginListBox() API, you have full control over how to display the combo contents.
1886 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1887 // stored in the object itself, etc.)
1888 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1889 static int item_selected_idx = 0; // Here we store our selected data as an index.
1890
1891 static bool item_highlight = false;
1892 int item_highlighted_idx = -1; // Here we store our highlighted data as an index.
1893 ImGui::Checkbox(label: "Highlight hovered item in second listbox", v: &item_highlight);
1894
1895 if (ImGui::BeginListBox(label: "listbox 1"))
1896 {
1897 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1898 {
1899 const bool is_selected = (item_selected_idx == n);
1900 if (ImGui::Selectable(label: items[n], selected: is_selected))
1901 item_selected_idx = n;
1902
1903 if (item_highlight && ImGui::IsItemHovered())
1904 item_highlighted_idx = n;
1905
1906 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1907 if (is_selected)
1908 ImGui::SetItemDefaultFocus();
1909 }
1910 ImGui::EndListBox();
1911 }
1912 ImGui::SameLine(); HelpMarker(desc: "Here we are sharing selection state between both boxes.");
1913
1914 // Custom size: use all width, 5 items tall
1915 ImGui::Text(fmt: "Full-width:");
1916 if (ImGui::BeginListBox(label: "##listbox 2", size: ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
1917 {
1918 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1919 {
1920 bool is_selected = (item_selected_idx == n);
1921 ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0;
1922 if (ImGui::Selectable(label: items[n], selected: is_selected, flags))
1923 item_selected_idx = n;
1924
1925 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1926 if (is_selected)
1927 ImGui::SetItemDefaultFocus();
1928 }
1929 ImGui::EndListBox();
1930 }
1931
1932 ImGui::TreePop();
1933 }
1934}
1935
1936//-----------------------------------------------------------------------------
1937// [SECTION] DemoWindowWidgetsMultiComponents()
1938//-----------------------------------------------------------------------------
1939
1940static void DemoWindowWidgetsMultiComponents()
1941{
1942 IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets");
1943 if (ImGui::TreeNode(label: "Multi-component Widgets"))
1944 {
1945 static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
1946 static int vec4i[4] = { 1, 5, 100, 255 };
1947
1948 ImGui::SeparatorText(label: "2-wide");
1949 ImGui::InputFloat2(label: "input float2", v: vec4f);
1950 ImGui::DragFloat2(label: "drag float2", v: vec4f, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
1951 ImGui::SliderFloat2(label: "slider float2", v: vec4f, v_min: 0.0f, v_max: 1.0f);
1952 ImGui::InputInt2(label: "input int2", v: vec4i);
1953 ImGui::DragInt2(label: "drag int2", v: vec4i, v_speed: 1, v_min: 0, v_max: 255);
1954 ImGui::SliderInt2(label: "slider int2", v: vec4i, v_min: 0, v_max: 255);
1955
1956 ImGui::SeparatorText(label: "3-wide");
1957 ImGui::InputFloat3(label: "input float3", v: vec4f);
1958 ImGui::DragFloat3(label: "drag float3", v: vec4f, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
1959 ImGui::SliderFloat3(label: "slider float3", v: vec4f, v_min: 0.0f, v_max: 1.0f);
1960 ImGui::InputInt3(label: "input int3", v: vec4i);
1961 ImGui::DragInt3(label: "drag int3", v: vec4i, v_speed: 1, v_min: 0, v_max: 255);
1962 ImGui::SliderInt3(label: "slider int3", v: vec4i, v_min: 0, v_max: 255);
1963
1964 ImGui::SeparatorText(label: "4-wide");
1965 ImGui::InputFloat4(label: "input float4", v: vec4f);
1966 ImGui::DragFloat4(label: "drag float4", v: vec4f, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
1967 ImGui::SliderFloat4(label: "slider float4", v: vec4f, v_min: 0.0f, v_max: 1.0f);
1968 ImGui::InputInt4(label: "input int4", v: vec4i);
1969 ImGui::DragInt4(label: "drag int4", v: vec4i, v_speed: 1, v_min: 0, v_max: 255);
1970 ImGui::SliderInt4(label: "slider int4", v: vec4i, v_min: 0, v_max: 255);
1971
1972 ImGui::SeparatorText(label: "Ranges");
1973 static float begin = 10, end = 90;
1974 static int begin_i = 100, end_i = 1000;
1975 ImGui::DragFloatRange2(label: "range float", v_current_min: &begin, v_current_max: &end, v_speed: 0.25f, v_min: 0.0f, v_max: 100.0f, format: "Min: %.1f %%", format_max: "Max: %.1f %%", flags: ImGuiSliderFlags_AlwaysClamp);
1976 ImGui::DragIntRange2(label: "range int", v_current_min: &begin_i, v_current_max: &end_i, v_speed: 5, v_min: 0, v_max: 1000, format: "Min: %d units", format_max: "Max: %d units");
1977 ImGui::DragIntRange2(label: "range int (no bounds)", v_current_min: &begin_i, v_current_max: &end_i, v_speed: 5, v_min: 0, v_max: 0, format: "Min: %d units", format_max: "Max: %d units");
1978
1979 ImGui::TreePop();
1980 }
1981}
1982
1983//-----------------------------------------------------------------------------
1984// [SECTION] DemoWindowWidgetsPlotting()
1985//-----------------------------------------------------------------------------
1986
1987static void DemoWindowWidgetsPlotting()
1988{
1989 // Plot/Graph widgets are not very good.
1990// Consider using a third-party library such as ImPlot: https://github.com/epezent/implot
1991// (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)
1992 IMGUI_DEMO_MARKER("Widgets/Plotting");
1993 if (ImGui::TreeNode(label: "Plotting"))
1994 {
1995 ImGui::Text(fmt: "Need better plotting and graphing? Consider using ImPlot:");
1996 ImGui::TextLinkOpenURL(label: "https://github.com/epezent/implot");
1997 ImGui::Separator();
1998
1999 static bool animate = true;
2000 ImGui::Checkbox(label: "Animate", v: &animate);
2001
2002 // Plot as lines and plot as histogram
2003 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
2004 ImGui::PlotLines(label: "Frame Times", values: arr, IM_ARRAYSIZE(arr));
2005 ImGui::PlotHistogram(label: "Histogram", values: arr, IM_ARRAYSIZE(arr), values_offset: 0, NULL, scale_min: 0.0f, scale_max: 1.0f, graph_size: ImVec2(0, 80.0f));
2006 //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!");
2007
2008 // Fill an array of contiguous float values to plot
2009 // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
2010 // and the sizeof() of your structure in the "stride" parameter.
2011 static float values[90] = {};
2012 static int values_offset = 0;
2013 static double refresh_time = 0.0;
2014 if (!animate || refresh_time == 0.0)
2015 refresh_time = ImGui::GetTime();
2016 while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
2017 {
2018 static float phase = 0.0f;
2019 values[values_offset] = cosf(x: phase);
2020 values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
2021 phase += 0.10f * values_offset;
2022 refresh_time += 1.0f / 60.0f;
2023 }
2024
2025 // Plots can display overlay texts
2026 // (in this example, we will display an average value)
2027 {
2028 float average = 0.0f;
2029 for (int n = 0; n < IM_ARRAYSIZE(values); n++)
2030 average += values[n];
2031 average /= (float)IM_ARRAYSIZE(values);
2032 char overlay[32];
2033 sprintf(s: overlay, format: "avg %f", average);
2034 ImGui::PlotLines(label: "Lines", values, IM_ARRAYSIZE(values), values_offset, overlay_text: overlay, scale_min: -1.0f, scale_max: 1.0f, graph_size: ImVec2(0, 80.0f));
2035 }
2036
2037 // Use functions to generate output
2038 // FIXME: This is actually VERY awkward because current plot API only pass in indices.
2039 // We probably want an API passing floats and user provide sample rate/count.
2040 struct Funcs
2041 {
2042 static float Sin(void*, int i) { return sinf(x: i * 0.1f); }
2043 static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }
2044 };
2045 static int func_type = 0, display_count = 70;
2046 ImGui::SeparatorText(label: "Functions");
2047 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
2048 ImGui::Combo(label: "func", current_item: &func_type, items_separated_by_zeros: "Sin\0Saw\0");
2049 ImGui::SameLine();
2050 ImGui::SliderInt(label: "Sample count", v: &display_count, v_min: 1, v_max: 400);
2051 float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
2052 ImGui::PlotLines(label: "Lines##2", values_getter: func, NULL, values_count: display_count, values_offset: 0, NULL, scale_min: -1.0f, scale_max: 1.0f, graph_size: ImVec2(0, 80));
2053 ImGui::PlotHistogram(label: "Histogram##2", values_getter: func, NULL, values_count: display_count, values_offset: 0, NULL, scale_min: -1.0f, scale_max: 1.0f, graph_size: ImVec2(0, 80));
2054
2055 ImGui::TreePop();
2056 }
2057}
2058
2059//-----------------------------------------------------------------------------
2060// [SECTION] DemoWindowWidgetsProgressBars()
2061//-----------------------------------------------------------------------------
2062
2063static void DemoWindowWidgetsProgressBars()
2064{
2065 IMGUI_DEMO_MARKER("Widgets/Progress Bars");
2066 if (ImGui::TreeNode(label: "Progress Bars"))
2067 {
2068 // Animate a simple progress bar
2069 static float progress = 0.0f, progress_dir = 1.0f;
2070 progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
2071 if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; }
2072 if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; }
2073
2074 // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
2075 // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
2076 ImGui::ProgressBar(fraction: progress, size_arg: ImVec2(0.0f, 0.0f));
2077 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
2078 ImGui::Text(fmt: "Progress Bar");
2079
2080 float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f);
2081 char buf[32];
2082 sprintf(s: buf, format: "%d/%d", (int)(progress_saturated * 1753), 1753);
2083 ImGui::ProgressBar(fraction: progress, size_arg: ImVec2(0.f, 0.f), overlay: buf);
2084
2085 // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value.
2086 // Adjust the factor if you want to adjust the animation speed.
2087 ImGui::ProgressBar(fraction: -1.0f * (float)ImGui::GetTime(), size_arg: ImVec2(0.0f, 0.0f), overlay: "Searching..");
2088 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
2089 ImGui::Text(fmt: "Indeterminate");
2090
2091 ImGui::TreePop();
2092 }
2093}
2094
2095//-----------------------------------------------------------------------------
2096// [SECTION] DemoWindowWidgetsQueryingStatuses()
2097//-----------------------------------------------------------------------------
2098
2099static void DemoWindowWidgetsQueryingStatuses()
2100{
2101 IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)");
2102 if (ImGui::TreeNode(label: "Querying Item Status (Edited/Active/Hovered etc.)"))
2103 {
2104 // Select an item type
2105 const char* item_names[] =
2106 {
2107 "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat",
2108 "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox"
2109 };
2110 static int item_type = 4;
2111 static bool item_disabled = false;
2112 ImGui::Combo(label: "Item Type", current_item: &item_type, items: item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names));
2113 ImGui::SameLine();
2114 HelpMarker(desc: "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().");
2115 ImGui::Checkbox(label: "Item Disabled", v: &item_disabled);
2116
2117 // Submit selected items so we can query their status in the code following it.
2118 bool ret = false;
2119 static bool b = false;
2120 static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
2121 static char str[16] = {};
2122 if (item_disabled)
2123 ImGui::BeginDisabled(disabled: true);
2124 if (item_type == 0) { ImGui::Text(fmt: "ITEM: Text"); } // Testing text items with no identifier/interaction
2125 if (item_type == 1) { ret = ImGui::Button(label: "ITEM: Button"); } // Testing button
2126 if (item_type == 2) { ImGui::PushItemFlag(option: ImGuiItemFlags_ButtonRepeat, enabled: true); ret = ImGui::Button(label: "ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater)
2127 if (item_type == 3) { ret = ImGui::Checkbox(label: "ITEM: Checkbox", v: &b); } // Testing checkbox
2128 if (item_type == 4) { ret = ImGui::SliderFloat(label: "ITEM: SliderFloat", v: &col4f[0], v_min: 0.0f, v_max: 1.0f); } // Testing basic item
2129 if (item_type == 5) { ret = ImGui::InputText(label: "ITEM: InputText", buf: &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing)
2130 if (item_type == 6) { ret = ImGui::InputTextMultiline(label: "ITEM: InputTextMultiline", buf: &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window)
2131 if (item_type == 7) { ret = ImGui::InputFloat(label: "ITEM: InputFloat", v: col4f, step: 1.0f); } // Testing +/- buttons on scalar input
2132 if (item_type == 8) { ret = ImGui::InputFloat3(label: "ITEM: InputFloat3", v: col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2133 if (item_type == 9) { ret = ImGui::ColorEdit4(label: "ITEM: ColorEdit4", col: col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2134 if (item_type == 10) { ret = ImGui::Selectable(label: "ITEM: Selectable"); } // Testing selectable item
2135 if (item_type == 11) { ret = ImGui::MenuItem(label: "ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)
2136 if (item_type == 12) { ret = ImGui::TreeNode(label: "ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node
2137 if (item_type == 13) { ret = ImGui::TreeNodeEx(label: "ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", flags: ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.
2138 if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo(label: "ITEM: Combo", current_item: &current, items, IM_ARRAYSIZE(items)); }
2139 if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox(label: "ITEM: ListBox", current_item: &current, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }
2140
2141 bool hovered_delay_none = ImGui::IsItemHovered();
2142 bool hovered_delay_stationary = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_Stationary);
2143 bool hovered_delay_short = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayShort);
2144 bool hovered_delay_normal = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayNormal);
2145 bool hovered_delay_tooltip = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary
2146
2147 // Display the values of IsItemHovered() and other common item state functions.
2148 // Note that the ImGuiHoveredFlags_XXX flags can be combined.
2149 // Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
2150 // we query every state in a single call to avoid storing them and to simplify the code.
2151 ImGui::BulletText(
2152 fmt: "Return value = %d\n"
2153 "IsItemFocused() = %d\n"
2154 "IsItemHovered() = %d\n"
2155 "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
2156 "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2157 "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n"
2158 "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n"
2159 "IsItemHovered(_AllowWhenDisabled) = %d\n"
2160 "IsItemHovered(_RectOnly) = %d\n"
2161 "IsItemActive() = %d\n"
2162 "IsItemEdited() = %d\n"
2163 "IsItemActivated() = %d\n"
2164 "IsItemDeactivated() = %d\n"
2165 "IsItemDeactivatedAfterEdit() = %d\n"
2166 "IsItemVisible() = %d\n"
2167 "IsItemClicked() = %d\n"
2168 "IsItemToggledOpen() = %d\n"
2169 "GetItemRectMin() = (%.1f, %.1f)\n"
2170 "GetItemRectMax() = (%.1f, %.1f)\n"
2171 "GetItemRectSize() = (%.1f, %.1f)",
2172 ret,
2173 ImGui::IsItemFocused(),
2174 ImGui::IsItemHovered(),
2175 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2176 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2177 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenOverlappedByItem),
2178 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenOverlappedByWindow),
2179 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenDisabled),
2180 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_RectOnly),
2181 ImGui::IsItemActive(),
2182 ImGui::IsItemEdited(),
2183 ImGui::IsItemActivated(),
2184 ImGui::IsItemDeactivated(),
2185 ImGui::IsItemDeactivatedAfterEdit(),
2186 ImGui::IsItemVisible(),
2187 ImGui::IsItemClicked(),
2188 ImGui::IsItemToggledOpen(),
2189 ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y,
2190 ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y,
2191 ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y
2192 );
2193 ImGui::BulletText(
2194 fmt: "with Hovering Delay or Stationary test:\n"
2195 "IsItemHovered() = = %d\n"
2196 "IsItemHovered(_Stationary) = %d\n"
2197 "IsItemHovered(_DelayShort) = %d\n"
2198 "IsItemHovered(_DelayNormal) = %d\n"
2199 "IsItemHovered(_Tooltip) = %d",
2200 hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);
2201
2202 if (item_disabled)
2203 ImGui::EndDisabled();
2204
2205 char buf[1] = "";
2206 ImGui::InputText(label: "unused", buf, IM_ARRAYSIZE(buf), flags: ImGuiInputTextFlags_ReadOnly);
2207 ImGui::SameLine();
2208 HelpMarker(desc: "This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.");
2209
2210 ImGui::TreePop();
2211 }
2212
2213 IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)");
2214 if (ImGui::TreeNode(label: "Querying Window Status (Focused/Hovered etc.)"))
2215 {
2216 static bool embed_all_inside_a_child_window = false;
2217 ImGui::Checkbox(label: "Embed everything inside a child window for testing _RootWindow flag.", v: &embed_all_inside_a_child_window);
2218 if (embed_all_inside_a_child_window)
2219 ImGui::BeginChild(str_id: "outer_child", size: ImVec2(0, ImGui::GetFontSize() * 20.0f), child_flags: ImGuiChildFlags_Borders);
2220
2221 // Testing IsWindowFocused() function with its various flags.
2222 ImGui::BulletText(
2223 fmt: "IsWindowFocused() = %d\n"
2224 "IsWindowFocused(_ChildWindows) = %d\n"
2225 "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n"
2226 "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n"
2227 "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
2228 "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2229 "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
2230 "IsWindowFocused(_RootWindow) = %d\n"
2231 "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n"
2232 "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n"
2233 "IsWindowFocused(_AnyWindow) = %d\n",
2234 ImGui::IsWindowFocused(),
2235 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows),
2236 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),
2237 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy),
2238 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
2239 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2240 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
2241 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_RootWindow),
2242 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2243 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
2244 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_AnyWindow));
2245
2246 // Testing IsWindowHovered() function with its various flags.
2247 ImGui::BulletText(
2248 fmt: "IsWindowHovered() = %d\n"
2249 "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
2250 "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2251 "IsWindowHovered(_ChildWindows) = %d\n"
2252 "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n"
2253 "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n"
2254 "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
2255 "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2256 "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
2257 "IsWindowHovered(_RootWindow) = %d\n"
2258 "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
2259 "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n"
2260 "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
2261 "IsWindowHovered(_AnyWindow) = %d\n"
2262 "IsWindowHovered(_Stationary) = %d\n",
2263 ImGui::IsWindowHovered(),
2264 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2265 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2266 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows),
2267 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),
2268 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy),
2269 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
2270 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2271 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
2272 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_RootWindow),
2273 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2274 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
2275 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2276 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_AnyWindow),
2277 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_Stationary));
2278
2279 ImGui::BeginChild(str_id: "child", size: ImVec2(0, 50), child_flags: ImGuiChildFlags_Borders);
2280 ImGui::Text(fmt: "This is another child window for testing the _ChildWindows flag.");
2281 ImGui::EndChild();
2282 if (embed_all_inside_a_child_window)
2283 ImGui::EndChild();
2284
2285 // Calling IsItemHovered() after begin returns the hovered status of the title bar.
2286 // This is useful in particular if you want to create a context menu associated to the title bar of a window.
2287 // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).
2288 static bool test_window = false;
2289 ImGui::Checkbox(label: "Hovered/Active tests after Begin() for title bar testing", v: &test_window);
2290 if (test_window)
2291 {
2292 // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck.
2293 // Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as "don't skip items"?
2294 ImGui::Begin(name: "Title bar Hovered/Active tests", p_open: &test_window);
2295 if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()
2296 {
2297 if (ImGui::MenuItem(label: "Close")) { test_window = false; }
2298 ImGui::EndPopup();
2299 }
2300 ImGui::Text(
2301 fmt: "IsItemHovered() after begin = %d (== is title bar hovered)\n"
2302 "IsItemActive() after begin = %d (== is window being clicked/moved)\n",
2303 ImGui::IsItemHovered(), ImGui::IsItemActive());
2304 ImGui::End();
2305 }
2306
2307 ImGui::TreePop();
2308 }
2309}
2310
2311//-----------------------------------------------------------------------------
2312// [SECTION] DemoWindowWidgetsSelectables()
2313//-----------------------------------------------------------------------------
2314
2315static void DemoWindowWidgetsSelectables()
2316{
2317 IMGUI_DEMO_MARKER("Widgets/Selectables");
2318 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
2319 if (ImGui::TreeNode(label: "Selectables"))
2320 {
2321 // Selectable() has 2 overloads:
2322 // - The one taking "bool selected" as a read-only selection information.
2323 // When Selectable() has been clicked it returns true and you can alter selection state accordingly.
2324 // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
2325 // The earlier is more flexible, as in real application your selection may be stored in many different ways
2326 // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
2327 IMGUI_DEMO_MARKER("Widgets/Selectables/Basic");
2328 if (ImGui::TreeNode(label: "Basic"))
2329 {
2330 static bool selection[5] = { false, true, false, false };
2331 ImGui::Selectable(label: "1. I am selectable", p_selected: &selection[0]);
2332 ImGui::Selectable(label: "2. I am selectable", p_selected: &selection[1]);
2333 ImGui::Selectable(label: "3. I am selectable", p_selected: &selection[2]);
2334 if (ImGui::Selectable(label: "4. I am double clickable", selected: selection[3], flags: ImGuiSelectableFlags_AllowDoubleClick))
2335 if (ImGui::IsMouseDoubleClicked(button: 0))
2336 selection[3] = !selection[3];
2337 ImGui::TreePop();
2338 }
2339
2340 IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line");
2341 if (ImGui::TreeNode(label: "Rendering more items on the same line"))
2342 {
2343 // (1) Using SetNextItemAllowOverlap()
2344 // (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically.
2345 static bool selected[3] = { false, false, false };
2346 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(label: "main.c", p_selected: &selected[0]); ImGui::SameLine(); ImGui::SmallButton(label: "Link 1");
2347 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(label: "Hello.cpp", p_selected: &selected[1]); ImGui::SameLine(); ImGui::SmallButton(label: "Link 2");
2348 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(label: "Hello.h", p_selected: &selected[2]); ImGui::SameLine(); ImGui::SmallButton(label: "Link 3");
2349 ImGui::TreePop();
2350 }
2351
2352 IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables");
2353 if (ImGui::TreeNode(label: "In Tables"))
2354 {
2355 static bool selected[10] = {};
2356
2357 if (ImGui::BeginTable(str_id: "split1", columns: 3, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2358 {
2359 for (int i = 0; i < 10; i++)
2360 {
2361 char label[32];
2362 sprintf(s: label, format: "Item %d", i);
2363 ImGui::TableNextColumn();
2364 ImGui::Selectable(label, p_selected: &selected[i]); // FIXME-TABLE: Selection overlap
2365 }
2366 ImGui::EndTable();
2367 }
2368 ImGui::Spacing();
2369 if (ImGui::BeginTable(str_id: "split2", columns: 3, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2370 {
2371 for (int i = 0; i < 10; i++)
2372 {
2373 char label[32];
2374 sprintf(s: label, format: "Item %d", i);
2375 ImGui::TableNextRow();
2376 ImGui::TableNextColumn();
2377 ImGui::Selectable(label, p_selected: &selected[i], flags: ImGuiSelectableFlags_SpanAllColumns);
2378 ImGui::TableNextColumn();
2379 ImGui::Text(fmt: "Some other contents");
2380 ImGui::TableNextColumn();
2381 ImGui::Text(fmt: "123456");
2382 }
2383 ImGui::EndTable();
2384 }
2385 ImGui::TreePop();
2386 }
2387
2388 IMGUI_DEMO_MARKER("Widgets/Selectables/Grid");
2389 if (ImGui::TreeNode(label: "Grid"))
2390 {
2391 static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
2392
2393 // Add in a bit of silly fun...
2394 const float time = (float)ImGui::GetTime();
2395 const bool winning_state = memchr(s: selected, c: 0, n: sizeof(selected)) == NULL; // If all cells are selected...
2396 if (winning_state)
2397 ImGui::PushStyleVar(idx: ImGuiStyleVar_SelectableTextAlign, val: ImVec2(0.5f + 0.5f * cosf(x: time * 2.0f), 0.5f + 0.5f * sinf(x: time * 3.0f)));
2398
2399 for (int y = 0; y < 4; y++)
2400 for (int x = 0; x < 4; x++)
2401 {
2402 if (x > 0)
2403 ImGui::SameLine();
2404 ImGui::PushID(int_id: y * 4 + x);
2405 if (ImGui::Selectable(label: "Sailor", selected: selected[y][x] != 0, flags: 0, size: ImVec2(50, 50)))
2406 {
2407 // Toggle clicked cell + toggle neighbors
2408 selected[y][x] ^= 1;
2409 if (x > 0) { selected[y][x - 1] ^= 1; }
2410 if (x < 3) { selected[y][x + 1] ^= 1; }
2411 if (y > 0) { selected[y - 1][x] ^= 1; }
2412 if (y < 3) { selected[y + 1][x] ^= 1; }
2413 }
2414 ImGui::PopID();
2415 }
2416
2417 if (winning_state)
2418 ImGui::PopStyleVar();
2419 ImGui::TreePop();
2420 }
2421 IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment");
2422 if (ImGui::TreeNode(label: "Alignment"))
2423 {
2424 HelpMarker(
2425 desc: "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item "
2426 "basis using PushStyleVar(). You'll probably want to always keep your default situation to "
2427 "left-align otherwise it becomes difficult to layout multiple items on a same line");
2428 static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };
2429 for (int y = 0; y < 3; y++)
2430 {
2431 for (int x = 0; x < 3; x++)
2432 {
2433 ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);
2434 char name[32];
2435 sprintf(s: name, format: "(%.1f,%.1f)", alignment.x, alignment.y);
2436 if (x > 0) ImGui::SameLine();
2437 ImGui::PushStyleVar(idx: ImGuiStyleVar_SelectableTextAlign, val: alignment);
2438 ImGui::Selectable(label: name, p_selected: &selected[3 * y + x], flags: ImGuiSelectableFlags_None, size: ImVec2(80, 80));
2439 ImGui::PopStyleVar();
2440 }
2441 }
2442 ImGui::TreePop();
2443 }
2444 ImGui::TreePop();
2445 }
2446}
2447
2448//-----------------------------------------------------------------------------
2449// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
2450//-----------------------------------------------------------------------------
2451// Multi-selection demos
2452// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select
2453//-----------------------------------------------------------------------------
2454
2455static const char* ExampleNames[] =
2456{
2457 "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper",
2458 "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava",
2459 "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
2460};
2461
2462// Extra functions to add deletion support to ImGuiSelectionBasicStorage
2463struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage
2464{
2465 // Find which item should be Focused after deletion.
2466 // Call _before_ item submission. Retunr an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
2467 // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
2468 // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
2469 // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.
2470 // - 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.
2471 // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset.
2472 int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count)
2473 {
2474 if (Size == 0)
2475 return -1;
2476
2477 // If focused item is not selected...
2478 const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item
2479 if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx))
2480 {
2481 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.
2482 return focused_idx; // Request to focus same item after deletion.
2483 }
2484
2485 // If focused item is selected: land on first unselected item after focused item.
2486 for (int idx = focused_idx + 1; idx < items_count; idx++)
2487 if (!Contains(id: GetStorageIdFromIndex(idx)))
2488 return idx;
2489
2490 // If focused item is selected: otherwise return last unselected item before focused item.
2491 for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--)
2492 if (!Contains(id: GetStorageIdFromIndex(idx)))
2493 return idx;
2494
2495 return -1;
2496 }
2497
2498 // Rewrite item list (delete items) + update selection.
2499 // - Call after EndMultiSelect()
2500 // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data.
2501 template<typename ITEM_TYPE>
2502 void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& items, int item_curr_idx_to_select)
2503 {
2504 // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection).
2505 // If NavId was not part of selection, we will stay on same item.
2506 ImVector<ITEM_TYPE> new_items;
2507 new_items.reserve(items.Size - Size);
2508 int item_next_idx_to_select = -1;
2509 for (int idx = 0; idx < items.Size; idx++)
2510 {
2511 if (!Contains(id: GetStorageIdFromIndex(idx)))
2512 new_items.push_back(items[idx]);
2513 if (item_curr_idx_to_select == idx)
2514 item_next_idx_to_select = new_items.Size - 1;
2515 }
2516 items.swap(new_items);
2517
2518 // Update selection
2519 Clear();
2520 if (item_next_idx_to_select != -1 && ms_io->NavIdSelected)
2521 SetItemSelected(id: GetStorageIdFromIndex(idx: item_next_idx_to_select), selected: true);
2522 }
2523};
2524
2525// Example: Implement dual list box storage and interface
2526struct ExampleDualListBox
2527{
2528 ImVector<ImGuiID> Items[2]; // ID is index into ExampleName[]
2529 ImGuiSelectionBasicStorage Selections[2]; // Store ExampleItemId into selection
2530 bool OptKeepSorted = true;
2531
2532 void MoveAll(int src, int dst)
2533 {
2534 IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0));
2535 for (ImGuiID item_id : Items[src])
2536 Items[dst].push_back(v: item_id);
2537 Items[src].clear();
2538 SortItems(n: dst);
2539 Selections[src].Swap(r&: Selections[dst]);
2540 Selections[src].Clear();
2541 }
2542 void MoveSelected(int src, int dst)
2543 {
2544 for (int src_n = 0; src_n < Items[src].Size; src_n++)
2545 {
2546 ImGuiID item_id = Items[src][src_n];
2547 if (!Selections[src].Contains(id: item_id))
2548 continue;
2549 Items[src].erase(it: &Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap)
2550 Items[dst].push_back(v: item_id);
2551 src_n--;
2552 }
2553 if (OptKeepSorted)
2554 SortItems(n: dst);
2555 Selections[src].Swap(r&: Selections[dst]);
2556 Selections[src].Clear();
2557 }
2558 void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side)
2559 {
2560 // In this example we store item id in selection (instead of item index)
2561 Selections[side].UserData = Items[side].Data;
2562 Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; };
2563 Selections[side].ApplyRequests(ms_io);
2564 }
2565 static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)
2566 {
2567 const int* a = (const int*)lhs;
2568 const int* b = (const int*)rhs;
2569 return (*a - *b) > 0 ? +1 : -1;
2570 }
2571 void SortItems(int n)
2572 {
2573 qsort(base: Items[n].Data, nmemb: (size_t)Items[n].Size, size: sizeof(Items[n][0]), compar: CompareItemsByValue);
2574 }
2575 void Show()
2576 {
2577 //ImGui::Checkbox("Sorted", &OptKeepSorted);
2578 if (ImGui::BeginTable(str_id: "split", columns: 3, flags: ImGuiTableFlags_None))
2579 {
2580 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch); // Left side
2581 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed); // Buttons
2582 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch); // Right side
2583 ImGui::TableNextRow();
2584
2585 int request_move_selected = -1;
2586 int request_move_all = -1;
2587 float child_height_0 = 0.0f;
2588 for (int side = 0; side < 2; side++)
2589 {
2590 // FIXME-MULTISELECT: Dual List Box: Add context menus
2591 // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.
2592 ImVector<ImGuiID>& items = Items[side];
2593 ImGuiSelectionBasicStorage& selection = Selections[side];
2594
2595 ImGui::TableSetColumnIndex(column_n: (side == 0) ? 0 : 2);
2596 ImGui::Text(fmt: "%s (%d)", (side == 0) ? "Available" : "Basket", items.Size);
2597
2598 // Submit scrolling range to avoid glitches on moving/deletion
2599 const float items_height = ImGui::GetTextLineHeightWithSpacing();
2600 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2601
2602 bool child_visible;
2603 if (side == 0)
2604 {
2605 // Left child is resizable
2606 ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), size_max: ImVec2(FLT_MAX, FLT_MAX));
2607 child_visible = ImGui::BeginChild(str_id: "0", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY);
2608 child_height_0 = ImGui::GetWindowSize().y;
2609 }
2610 else
2611 {
2612 // Right child use same height as left one
2613 child_visible = ImGui::BeginChild(str_id: "1", size: ImVec2(-FLT_MIN, child_height_0), child_flags: ImGuiChildFlags_FrameStyle);
2614 }
2615 if (child_visible)
2616 {
2617 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None;
2618 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: items.Size);
2619 ApplySelectionRequests(ms_io, side);
2620
2621 for (int item_n = 0; item_n < items.Size; item_n++)
2622 {
2623 ImGuiID item_id = items[item_n];
2624 bool item_is_selected = selection.Contains(id: item_id);
2625 ImGui::SetNextItemSelectionUserData(item_n);
2626 ImGui::Selectable(label: ExampleNames[item_id], selected: item_is_selected, flags: ImGuiSelectableFlags_AllowDoubleClick);
2627 if (ImGui::IsItemFocused())
2628 {
2629 // FIXME-MULTISELECT: Dual List Box: Transfer focus
2630 if (ImGui::IsKeyPressed(key: ImGuiKey_Enter) || ImGui::IsKeyPressed(key: ImGuiKey_KeypadEnter))
2631 request_move_selected = side;
2632 if (ImGui::IsMouseDoubleClicked(button: 0)) // FIXME-MULTISELECT: Double-click on multi-selection?
2633 request_move_selected = side;
2634 }
2635 }
2636
2637 ms_io = ImGui::EndMultiSelect();
2638 ApplySelectionRequests(ms_io, side);
2639 }
2640 ImGui::EndChild();
2641 }
2642
2643 // Buttons columns
2644 ImGui::TableSetColumnIndex(column_n: 1);
2645 ImGui::NewLine();
2646 //ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f };
2647 ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() };
2648
2649 // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized)
2650 if (ImGui::Button(label: ">>", size: button_sz))
2651 request_move_all = 0;
2652 if (ImGui::Button(label: ">", size: button_sz))
2653 request_move_selected = 0;
2654 if (ImGui::Button(label: "<", size: button_sz))
2655 request_move_selected = 1;
2656 if (ImGui::Button(label: "<<", size: button_sz))
2657 request_move_all = 1;
2658
2659 // Process requests
2660 if (request_move_all != -1)
2661 MoveAll(src: request_move_all, dst: request_move_all ^ 1);
2662 if (request_move_selected != -1)
2663 MoveSelected(src: request_move_selected, dst: request_move_selected ^ 1);
2664
2665 // FIXME-MULTISELECT: Support action from outside
2666 /*
2667 if (OptKeepSorted == false)
2668 {
2669 ImGui::NewLine();
2670 if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {}
2671 if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {}
2672 }
2673 */
2674
2675 ImGui::EndTable();
2676 }
2677 }
2678};
2679
2680static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_data)
2681{
2682 IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select");
2683 if (ImGui::TreeNode(label: "Selection State & Multi-Select"))
2684 {
2685 HelpMarker(desc: "Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
2686
2687 // Without any fancy API: manage single-selection yourself.
2688 IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
2689 if (ImGui::TreeNode(label: "Single-Select"))
2690 {
2691 static int selected = -1;
2692 for (int n = 0; n < 5; n++)
2693 {
2694 char buf[32];
2695 sprintf(s: buf, format: "Object %d", n);
2696 if (ImGui::Selectable(label: buf, selected: selected == n))
2697 selected = n;
2698 }
2699 ImGui::TreePop();
2700 }
2701
2702 // Demonstrate implementation a most-basic form of multi-selection manually
2703 // This doesn't support the SHIFT modifier which requires BeginMultiSelect()!
2704 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)");
2705 if (ImGui::TreeNode(label: "Multi-Select (manual/simplified, without BeginMultiSelect)"))
2706 {
2707 HelpMarker(desc: "Hold CTRL and click to select multiple items.");
2708 static bool selection[5] = { false, false, false, false, false };
2709 for (int n = 0; n < 5; n++)
2710 {
2711 char buf[32];
2712 sprintf(s: buf, format: "Object %d", n);
2713 if (ImGui::Selectable(label: buf, selected: selection[n]))
2714 {
2715 if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held
2716 memset(s: selection, c: 0, n: sizeof(selection));
2717 selection[n] ^= 1; // Toggle current item
2718 }
2719 }
2720 ImGui::TreePop();
2721 }
2722
2723 // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.
2724 // SHIFT+Click w/ CTRL and other standard features are supported.
2725 // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.
2726 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select");
2727 if (ImGui::TreeNode(label: "Multi-Select"))
2728 {
2729 ImGui::Text(fmt: "Supported features:");
2730 ImGui::BulletText(fmt: "Keyboard navigation (arrows, page up/down, home/end, space).");
2731 ImGui::BulletText(fmt: "Ctrl modifier to preserve and toggle selection.");
2732 ImGui::BulletText(fmt: "Shift modifier for range selection.");
2733 ImGui::BulletText(fmt: "CTRL+A to select all.");
2734 ImGui::BulletText(fmt: "Escape to clear selection.");
2735 ImGui::BulletText(fmt: "Click and drag to box-select.");
2736 ImGui::Text(fmt: "Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.");
2737
2738 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2739 const int ITEMS_COUNT = 50;
2740 static ImGuiSelectionBasicStorage selection;
2741 ImGui::Text(fmt: "Selection: %d/%d", selection.Size, ITEMS_COUNT);
2742
2743 // The BeginChild() has no purpose for selection logic, other that offering a scrolling region.
2744 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2745 {
2746 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2747 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: ITEMS_COUNT);
2748 selection.ApplyRequests(ms_io);
2749
2750 for (int n = 0; n < ITEMS_COUNT; n++)
2751 {
2752 char label[64];
2753 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2754 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
2755 ImGui::SetNextItemSelectionUserData(n);
2756 ImGui::Selectable(label, selected: item_is_selected);
2757 }
2758
2759 ms_io = ImGui::EndMultiSelect();
2760 selection.ApplyRequests(ms_io);
2761 }
2762 ImGui::EndChild();
2763 ImGui::TreePop();
2764 }
2765
2766 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2767 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)");
2768 if (ImGui::TreeNode(label: "Multi-Select (with clipper)"))
2769 {
2770 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2771 static ImGuiSelectionBasicStorage selection;
2772
2773 ImGui::Text(fmt: "Added features:");
2774 ImGui::BulletText(fmt: "Using ImGuiListClipper.");
2775
2776 const int ITEMS_COUNT = 10000;
2777 ImGui::Text(fmt: "Selection: %d/%d", selection.Size, ITEMS_COUNT);
2778 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2779 {
2780 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2781 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: ITEMS_COUNT);
2782 selection.ApplyRequests(ms_io);
2783
2784 ImGuiListClipper clipper;
2785 clipper.Begin(items_count: ITEMS_COUNT);
2786 if (ms_io->RangeSrcItem != -1)
2787 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2788 while (clipper.Step())
2789 {
2790 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2791 {
2792 char label[64];
2793 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2794 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
2795 ImGui::SetNextItemSelectionUserData(n);
2796 ImGui::Selectable(label, selected: item_is_selected);
2797 }
2798 }
2799
2800 ms_io = ImGui::EndMultiSelect();
2801 selection.ApplyRequests(ms_io);
2802 }
2803 ImGui::EndChild();
2804 ImGui::TreePop();
2805 }
2806
2807 // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API.
2808 // In order to support Deletion without any glitches you need to:
2809 // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling.
2810 // - (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.
2811 // - (3) BeginXXXX process
2812 // - (4) Focus process
2813 // - (5) EndXXXX process
2814 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)");
2815 if (ImGui::TreeNode(label: "Multi-Select (with deletion)"))
2816 {
2817 // Storing items data separately from selection data.
2818 // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items)
2819 // Use a custom selection.Adapter: store item identifier in Selection (instead of index)
2820 static ImVector<ImGuiID> items;
2821 static ExampleSelectionWithDeletion selection;
2822 selection.UserData = (void*)&items;
2823 selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector<ImGuiID>* p_items = (ImVector<ImGuiID>*)self->UserData; return (*p_items)[idx]; }; // Index -> ID
2824
2825 ImGui::Text(fmt: "Added features:");
2826 ImGui::BulletText(fmt: "Dynamic list with Delete key support.");
2827 ImGui::Text(fmt: "Selection size: %d/%d", selection.Size, items.Size);
2828
2829 // Initialize default list with 50 items + button to add/remove items.
2830 static ImGuiID items_next_id = 0;
2831 if (items_next_id == 0)
2832 for (ImGuiID n = 0; n < 50; n++)
2833 items.push_back(v: items_next_id++);
2834 if (ImGui::SmallButton(label: "Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(v: items_next_id++); } }
2835 ImGui::SameLine();
2836 if (ImGui::SmallButton(label: "Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetItemSelected(id: items.back(), selected: false); items.pop_back(); } }
2837
2838 // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
2839 const float items_height = ImGui::GetTextLineHeightWithSpacing();
2840 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2841
2842 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2843 {
2844 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2845 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: items.Size);
2846 selection.ApplyRequests(ms_io);
2847
2848 const bool want_delete = ImGui::Shortcut(key_chord: ImGuiKey_Delete, flags: ImGuiInputFlags_Repeat) && (selection.Size > 0);
2849 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items_count: items.Size) : -1;
2850
2851 for (int n = 0; n < items.Size; n++)
2852 {
2853 const ImGuiID item_id = items[n];
2854 char label[64];
2855 sprintf(s: label, format: "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]);
2856
2857 bool item_is_selected = selection.Contains(id: item_id);
2858 ImGui::SetNextItemSelectionUserData(n);
2859 ImGui::Selectable(label, selected: item_is_selected);
2860 if (item_curr_idx_to_focus == n)
2861 ImGui::SetKeyboardFocusHere(-1);
2862 }
2863
2864 // Apply multi-select requests
2865 ms_io = ImGui::EndMultiSelect();
2866 selection.ApplyRequests(ms_io);
2867 if (want_delete)
2868 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_select: item_curr_idx_to_focus);
2869 }
2870 ImGui::EndChild();
2871 ImGui::TreePop();
2872 }
2873
2874 // Implement a Dual List Box (#6648)
2875 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)");
2876 if (ImGui::TreeNode(label: "Multi-Select (dual list box)"))
2877 {
2878 // Init default state
2879 static ExampleDualListBox dlb;
2880 if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0)
2881 for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++)
2882 dlb.Items[0].push_back(v: (ImGuiID)item_id);
2883
2884 // Show
2885 dlb.Show();
2886
2887 ImGui::TreePop();
2888 }
2889
2890 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2891 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)");
2892 if (ImGui::TreeNode(label: "Multi-Select (in a table)"))
2893 {
2894 static ImGuiSelectionBasicStorage selection;
2895
2896 const int ITEMS_COUNT = 10000;
2897 ImGui::Text(fmt: "Selection: %d/%d", selection.Size, ITEMS_COUNT);
2898 if (ImGui::BeginTable(str_id: "##Basket", columns: 2, flags: ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter))
2899 {
2900 ImGui::TableSetupColumn(label: "Object");
2901 ImGui::TableSetupColumn(label: "Action");
2902 ImGui::TableSetupScrollFreeze(cols: 0, rows: 1);
2903 ImGui::TableHeadersRow();
2904
2905 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2906 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: ITEMS_COUNT);
2907 selection.ApplyRequests(ms_io);
2908
2909 ImGuiListClipper clipper;
2910 clipper.Begin(items_count: ITEMS_COUNT);
2911 if (ms_io->RangeSrcItem != -1)
2912 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2913 while (clipper.Step())
2914 {
2915 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2916 {
2917 ImGui::TableNextRow();
2918 ImGui::TableNextColumn();
2919 char label[64];
2920 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2921 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
2922 ImGui::SetNextItemSelectionUserData(n);
2923 ImGui::Selectable(label, selected: item_is_selected, flags: ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
2924 ImGui::TableNextColumn();
2925 ImGui::SmallButton(label: "hello");
2926 }
2927 }
2928
2929 ms_io = ImGui::EndMultiSelect();
2930 selection.ApplyRequests(ms_io);
2931 ImGui::EndTable();
2932 }
2933 ImGui::TreePop();
2934 }
2935
2936 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)");
2937 if (ImGui::TreeNode(label: "Multi-Select (checkboxes)"))
2938 {
2939 ImGui::Text(fmt: "In a list of checkboxes (not selectable):");
2940 ImGui::BulletText(fmt: "Using _NoAutoSelect + _NoAutoClear flags.");
2941 ImGui::BulletText(fmt: "Shift+Click to check multiple boxes.");
2942 ImGui::BulletText(fmt: "Shift+Keyboard to copy current value to other boxes.");
2943
2944 // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper.
2945 static bool items[20] = {};
2946 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape;
2947 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoSelect);
2948 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoClear", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoClear);
2949 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect2d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.
2950
2951 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
2952 {
2953 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: -1, IM_ARRAYSIZE(items));
2954 ImGuiSelectionExternalStorage storage_wrapper;
2955 storage_wrapper.UserData = (void*)items;
2956 storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; };
2957 storage_wrapper.ApplyRequests(ms_io);
2958 for (int n = 0; n < 20; n++)
2959 {
2960 char label[32];
2961 sprintf(s: label, format: "Item %d", n);
2962 ImGui::SetNextItemSelectionUserData(n);
2963 ImGui::Checkbox(label, v: &items[n]);
2964 }
2965 ms_io = ImGui::EndMultiSelect();
2966 storage_wrapper.ApplyRequests(ms_io);
2967 }
2968 ImGui::EndChild();
2969
2970 ImGui::TreePop();
2971 }
2972
2973 // Demonstrate individual selection scopes in same window
2974 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)");
2975 if (ImGui::TreeNode(label: "Multi-Select (multiple scopes)"))
2976 {
2977 // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection
2978 const int SCOPES_COUNT = 3;
2979 const int ITEMS_COUNT = 8; // Per scope
2980 static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT];
2981
2982 // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.
2983 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid;
2984 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeWindow", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
2985 flags &= ~ImGuiMultiSelectFlags_ScopeRect;
2986 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeRect", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
2987 flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
2988 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ClearOnClickVoid", flags: &flags, flags_value: ImGuiMultiSelectFlags_ClearOnClickVoid);
2989 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect1d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect1d);
2990
2991 for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++)
2992 {
2993 ImGui::PushID(int_id: selection_scope_n);
2994 ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];
2995 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection->Size, items_count: ITEMS_COUNT);
2996 selection->ApplyRequests(ms_io);
2997
2998 ImGui::SeparatorText(label: "Selection scope");
2999 ImGui::Text(fmt: "Selection size: %d/%d", selection->Size, ITEMS_COUNT);
3000
3001 for (int n = 0; n < ITEMS_COUNT; n++)
3002 {
3003 char label[64];
3004 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
3005 bool item_is_selected = selection->Contains(id: (ImGuiID)n);
3006 ImGui::SetNextItemSelectionUserData(n);
3007 ImGui::Selectable(label, selected: item_is_selected);
3008 }
3009
3010 // Apply multi-select requests
3011 ms_io = ImGui::EndMultiSelect();
3012 selection->ApplyRequests(ms_io);
3013 ImGui::PopID();
3014 }
3015 ImGui::TreePop();
3016 }
3017
3018 // See ShowExampleAppAssetsBrowser()
3019 if (ImGui::TreeNode(label: "Multi-Select (tiled assets browser)"))
3020 {
3021 ImGui::Checkbox(label: "Assets Browser", v: &demo_data->ShowAppAssetsBrowser);
3022 ImGui::Text(fmt: "(also access from 'Examples->Assets Browser' in menu)");
3023 ImGui::TreePop();
3024 }
3025
3026 // Demonstrate supporting multiple-selection in a tree.
3027 // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly!
3028 // This showcase how SetNextItemSelectionUserData() never assume indices!
3029 // - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request.
3030 // We want this interpolation to match what the user sees: in visible order, skipping closed nodes.
3031 // This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper.
3032 // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you
3033 // are more likely to build an array mapping sequential indices to visible tree nodes, since your
3034 // filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier.
3035 // - Consider this a prototype: we are working toward simplifying some of it.
3036 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)");
3037 if (ImGui::TreeNode(label: "Multi-Select (trees)"))
3038 {
3039 HelpMarker(
3040 desc: "This is rather advanced and experimental. If you are getting started with multi-select, "
3041 "please don't start by looking at how to use it for a tree!\n\n"
3042 "Future versions will try to simplify and formalize some of this.");
3043
3044 struct ExampleTreeFuncs
3045 {
3046 static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection)
3047 {
3048 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3049 tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Enable pressing left to jump to parent
3050 if (node->Childs.Size == 0)
3051 tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf;
3052 if (selection->Contains(id: (ImGuiID)node->UID))
3053 tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3054
3055 // Using SetNextItemStorageID() to specify storage id, so we can easily peek into
3056 // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions.
3057 ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node);
3058 ImGui::SetNextItemStorageID((ImGuiID)node->UID);
3059 if (ImGui::TreeNodeEx(label: node->Name, flags: tree_node_flags))
3060 {
3061 for (ExampleTreeNode* child : node->Childs)
3062 DrawNode(node: child, selection);
3063 ImGui::TreePop();
3064 }
3065 else if (ImGui::IsItemToggledOpen())
3066 {
3067 TreeCloseAndUnselectChildNodes(node, selection);
3068 }
3069 }
3070
3071 static bool TreeNodeGetOpen(ExampleTreeNode* node)
3072 {
3073 return ImGui::GetStateStorage()->GetBool(key: (ImGuiID)node->UID);
3074 }
3075
3076 static void TreeNodeSetOpen(ExampleTreeNode* node, bool open)
3077 {
3078 ImGui::GetStateStorage()->SetBool(key: (ImGuiID)node->UID, val: open);
3079 }
3080
3081 // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected.
3082 // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node
3083 // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc.
3084 static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0)
3085 {
3086 // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!)
3087 int unselected_count = selection->Contains(id: (ImGuiID)node->UID) ? 1 : 0;
3088 if (depth == 0 || TreeNodeGetOpen(node))
3089 {
3090 for (ExampleTreeNode* child : node->Childs)
3091 unselected_count += TreeCloseAndUnselectChildNodes(node: child, selection, depth: depth + 1);
3092 TreeNodeSetOpen(node, open: false);
3093 }
3094
3095 // Select root node if any of its child was selected, otherwise unselect
3096 selection->SetItemSelected(id: (ImGuiID)node->UID, selected: (depth == 0 && unselected_count > 0));
3097 return unselected_count;
3098 }
3099
3100 // Apply multi-selection requests
3101 static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection)
3102 {
3103 for (ImGuiSelectionRequest& req : ms_io->Requests)
3104 {
3105 if (req.Type == ImGuiSelectionRequestType_SetAll)
3106 {
3107 if (req.Selected)
3108 TreeSetAllInOpenNodes(node: tree, selection, selected: req.Selected);
3109 else
3110 selection->Clear();
3111 }
3112 else if (req.Type == ImGuiSelectionRequestType_SetRange)
3113 {
3114 ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem;
3115 ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem;
3116 for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(curr_node: node, last_node))
3117 selection->SetItemSelected(id: (ImGuiID)node->UID, selected: req.Selected);
3118 }
3119 }
3120 }
3121
3122 static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected)
3123 {
3124 if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme
3125 selection->SetItemSelected(id: (ImGuiID)node->UID, selected);
3126 if (node->Parent == NULL || TreeNodeGetOpen(node))
3127 for (ExampleTreeNode* child : node->Childs)
3128 TreeSetAllInOpenNodes(node: child, selection, selected);
3129 }
3130
3131 // Interpolate in *user-visible order* AND only *over opened nodes*.
3132 // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler.
3133 // Here the tricks are that:
3134 // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion.
3135 // this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)'
3136 // which would only be called when crossing from child to a parent, aka not too much.
3137 // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack,
3138 // making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location.
3139 static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node)
3140 {
3141 // Reached last node
3142 if (curr_node == last_node)
3143 return NULL;
3144
3145 // Recurse into childs. Query storage to tell if the node is open.
3146 if (curr_node->Childs.Size > 0 && TreeNodeGetOpen(node: curr_node))
3147 return curr_node->Childs[0];
3148
3149 // Next sibling, then into our own parent
3150 while (curr_node->Parent != NULL)
3151 {
3152 if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size)
3153 return curr_node->Parent->Childs[curr_node->IndexInParent + 1];
3154 curr_node = curr_node->Parent;
3155 }
3156 return NULL;
3157 }
3158
3159 }; // ExampleTreeFuncs
3160
3161 static ImGuiSelectionBasicStorage selection;
3162 if (demo_data->DemoTree == NULL)
3163 demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once
3164 ImGui::Text(fmt: "Selection size: %d", selection.Size);
3165
3166 if (ImGui::BeginChild(str_id: "##Tree", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3167 {
3168 ExampleTreeNode* tree = demo_data->DemoTree;
3169 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d;
3170 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags: ms_flags, selection_size: selection.Size, items_count: -1);
3171 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, selection: &selection);
3172 for (ExampleTreeNode* node : tree->Childs)
3173 ExampleTreeFuncs::DrawNode(node, selection: &selection);
3174 ms_io = ImGui::EndMultiSelect();
3175 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, selection: &selection);
3176 }
3177 ImGui::EndChild();
3178
3179 ImGui::TreePop();
3180 }
3181
3182 // Advanced demonstration of BeginMultiSelect()
3183 // - Showcase clipping.
3184 // - Showcase deletion.
3185 // - Showcase basic drag and drop.
3186 // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
3187 // - Showcase using inside a table.
3188 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)");
3189 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
3190 if (ImGui::TreeNode(label: "Multi-Select (advanced)"))
3191 {
3192 // Options
3193 enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
3194 static bool use_clipper = true;
3195 static bool use_deletion = true;
3196 static bool use_drag_drop = true;
3197 static bool show_in_table = false;
3198 static bool show_color_button = true;
3199 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
3200 static WidgetType widget_type = WidgetType_Selectable;
3201
3202 if (ImGui::TreeNode(label: "Options"))
3203 {
3204 if (ImGui::RadioButton(label: "Selectables", active: widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; }
3205 ImGui::SameLine();
3206 if (ImGui::RadioButton(label: "Tree nodes", active: widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; }
3207 ImGui::SameLine();
3208 HelpMarker(desc: "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.");
3209 ImGui::Checkbox(label: "Enable clipper", v: &use_clipper);
3210 ImGui::Checkbox(label: "Enable deletion", v: &use_deletion);
3211 ImGui::Checkbox(label: "Enable drag & drop", v: &use_drag_drop);
3212 ImGui::Checkbox(label: "Show in a table", v: &show_in_table);
3213 ImGui::Checkbox(label: "Show color button", v: &show_color_button);
3214 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_SingleSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_SingleSelect);
3215 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoSelectAll", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoSelectAll);
3216 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoRangeSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoRangeSelect);
3217 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoSelect);
3218 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoClear", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoClear);
3219 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoClearOnReselect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoClearOnReselect);
3220 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect1d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect1d);
3221 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect2d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect2d);
3222 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelectNoScroll", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelectNoScroll);
3223 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ClearOnEscape", flags: &flags, flags_value: ImGuiMultiSelectFlags_ClearOnEscape);
3224 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ClearOnClickVoid", flags: &flags, flags_value: ImGuiMultiSelectFlags_ClearOnClickVoid);
3225 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeWindow", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
3226 flags &= ~ImGuiMultiSelectFlags_ScopeRect;
3227 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeRect", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
3228 flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
3229 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_SelectOnClick", flags: &flags, flags_value: ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick))
3230 flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease;
3231 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_SelectOnClickRelease", flags: &flags, flags_value: ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease))
3232 flags &= ~ImGuiMultiSelectFlags_SelectOnClick;
3233 ImGui::SameLine(); HelpMarker(desc: "Allow dragging an unselected item without altering selection.");
3234 ImGui::TreePop();
3235 }
3236
3237 // Initialize default list with 1000 items.
3238 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
3239 static ImVector<int> items;
3240 static int items_next_id = 0;
3241 if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(v: items_next_id++); } }
3242 static ExampleSelectionWithDeletion selection;
3243 static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu
3244
3245 ImGui::Text(fmt: "Selection size: %d/%d", selection.Size, items.Size);
3246
3247 const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing();
3248 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
3249 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3250 {
3251 ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());
3252 if (widget_type == WidgetType_TreeNode)
3253 ImGui::PushStyleVarY(idx: ImGuiStyleVar_ItemSpacing, val_y: 0.0f);
3254
3255 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: items.Size);
3256 selection.ApplyRequests(ms_io);
3257
3258 const bool want_delete = (ImGui::Shortcut(key_chord: ImGuiKey_Delete, flags: ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu;
3259 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items_count: items.Size) : -1;
3260 request_deletion_from_menu = false;
3261
3262 if (show_in_table)
3263 {
3264 if (widget_type == WidgetType_TreeNode)
3265 ImGui::PushStyleVar(idx: ImGuiStyleVar_CellPadding, val: ImVec2(0.0f, 0.0f));
3266 ImGui::BeginTable(str_id: "##Split", columns: 2, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
3267 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 0.70f);
3268 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 0.30f);
3269 //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f);
3270 }
3271
3272 ImGuiListClipper clipper;
3273 if (use_clipper)
3274 {
3275 clipper.Begin(items_count: items.Size);
3276 if (item_curr_idx_to_focus != -1)
3277 clipper.IncludeItemByIndex(item_index: item_curr_idx_to_focus); // Ensure focused item is not clipped.
3278 if (ms_io->RangeSrcItem != -1)
3279 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
3280 }
3281
3282 while (!use_clipper || clipper.Step())
3283 {
3284 const int item_begin = use_clipper ? clipper.DisplayStart : 0;
3285 const int item_end = use_clipper ? clipper.DisplayEnd : items.Size;
3286 for (int n = item_begin; n < item_end; n++)
3287 {
3288 if (show_in_table)
3289 ImGui::TableNextColumn();
3290
3291 const int item_id = items[n];
3292 const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)];
3293 char label[64];
3294 sprintf(s: label, format: "Object %05d: %s", item_id, item_category);
3295
3296 // IMPORTANT: for deletion refocus to work we need object ID to be stable,
3297 // aka not depend on their index in the list. Here we use our persistent item_id
3298 // instead of index to build a unique ID that will persist.
3299 // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).
3300 ImGui::PushID(int_id: item_id);
3301
3302 // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part
3303 // of the selection scope doesn't erroneously alter our selection.
3304 if (show_color_button)
3305 {
3306 ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK;
3307 ImGui::ColorButton(desc_id: "##", col: ImColor(dummy_col), flags: ImGuiColorEditFlags_NoTooltip, size: color_button_sz);
3308 ImGui::SameLine();
3309 }
3310
3311 // Submit item
3312 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
3313 bool item_is_open = false;
3314 ImGui::SetNextItemSelectionUserData(n);
3315 if (widget_type == WidgetType_Selectable)
3316 {
3317 ImGui::Selectable(label, selected: item_is_selected, flags: ImGuiSelectableFlags_None);
3318 }
3319 else if (widget_type == WidgetType_TreeNode)
3320 {
3321 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3322 if (item_is_selected)
3323 tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3324 item_is_open = ImGui::TreeNodeEx(label, flags: tree_node_flags);
3325 }
3326
3327 // Focus (for after deletion)
3328 if (item_curr_idx_to_focus == n)
3329 ImGui::SetKeyboardFocusHere(-1);
3330
3331 // Drag and Drop
3332 if (use_drag_drop && ImGui::BeginDragDropSource())
3333 {
3334 // Create payload with full selection OR single unselected item.
3335 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
3336 if (ImGui::GetDragDropPayload() == NULL)
3337 {
3338 ImVector<int> payload_items;
3339 void* it = NULL;
3340 ImGuiID id = 0;
3341 if (!item_is_selected)
3342 payload_items.push_back(v: item_id);
3343 else
3344 while (selection.GetNextSelectedItem(opaque_it: &it, out_id: &id))
3345 payload_items.push_back(v: (int)id);
3346 ImGui::SetDragDropPayload(type: "MULTISELECT_DEMO_ITEMS", data: payload_items.Data, sz: (size_t)payload_items.size_in_bytes());
3347 }
3348
3349 // Display payload content in tooltip
3350 const ImGuiPayload* payload = ImGui::GetDragDropPayload();
3351 const int* payload_items = (int*)payload->Data;
3352 const int payload_count = (int)payload->DataSize / (int)sizeof(int);
3353 if (payload_count == 1)
3354 ImGui::Text(fmt: "Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]);
3355 else
3356 ImGui::Text(fmt: "Dragging %d objects", payload_count);
3357
3358 ImGui::EndDragDropSource();
3359 }
3360
3361 if (widget_type == WidgetType_TreeNode && item_is_open)
3362 ImGui::TreePop();
3363
3364 // Right-click: context menu
3365 if (ImGui::BeginPopupContextItem())
3366 {
3367 ImGui::BeginDisabled(disabled: !use_deletion || selection.Size == 0);
3368 sprintf(s: label, format: "Delete %d item(s)###DeleteSelected", selection.Size);
3369 if (ImGui::Selectable(label))
3370 request_deletion_from_menu = true;
3371 ImGui::EndDisabled();
3372 ImGui::Selectable(label: "Close");
3373 ImGui::EndPopup();
3374 }
3375
3376 // Demo content within a table
3377 if (show_in_table)
3378 {
3379 ImGui::TableNextColumn();
3380 ImGui::SetNextItemWidth(-FLT_MIN);
3381 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(0, 0));
3382 ImGui::InputText(label: "###NoLabel", buf: (char*)(void*)item_category, buf_size: strlen(s: item_category), flags: ImGuiInputTextFlags_ReadOnly);
3383 ImGui::PopStyleVar();
3384 }
3385
3386 ImGui::PopID();
3387 }
3388 if (!use_clipper)
3389 break;
3390 }
3391
3392 if (show_in_table)
3393 {
3394 ImGui::EndTable();
3395 if (widget_type == WidgetType_TreeNode)
3396 ImGui::PopStyleVar();
3397 }
3398
3399 // Apply multi-select requests
3400 ms_io = ImGui::EndMultiSelect();
3401 selection.ApplyRequests(ms_io);
3402 if (want_delete)
3403 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_select: item_curr_idx_to_focus);
3404
3405 if (widget_type == WidgetType_TreeNode)
3406 ImGui::PopStyleVar();
3407 }
3408 ImGui::EndChild();
3409 ImGui::TreePop();
3410 }
3411 ImGui::TreePop();
3412 }
3413}
3414
3415//-----------------------------------------------------------------------------
3416// [SECTION] DemoWindowWidgetsTabs()
3417//-----------------------------------------------------------------------------
3418
3419static void DemoWindowWidgetsTabs()
3420{
3421 IMGUI_DEMO_MARKER("Widgets/Tabs");
3422 if (ImGui::TreeNode(label: "Tabs"))
3423 {
3424 IMGUI_DEMO_MARKER("Widgets/Tabs/Basic");
3425 if (ImGui::TreeNode(label: "Basic"))
3426 {
3427 ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
3428 if (ImGui::BeginTabBar(str_id: "MyTabBar", flags: tab_bar_flags))
3429 {
3430 if (ImGui::BeginTabItem(label: "Avocado"))
3431 {
3432 ImGui::Text(fmt: "This is the Avocado tab!\nblah blah blah blah blah");
3433 ImGui::EndTabItem();
3434 }
3435 if (ImGui::BeginTabItem(label: "Broccoli"))
3436 {
3437 ImGui::Text(fmt: "This is the Broccoli tab!\nblah blah blah blah blah");
3438 ImGui::EndTabItem();
3439 }
3440 if (ImGui::BeginTabItem(label: "Cucumber"))
3441 {
3442 ImGui::Text(fmt: "This is the Cucumber tab!\nblah blah blah blah blah");
3443 ImGui::EndTabItem();
3444 }
3445 ImGui::EndTabBar();
3446 }
3447 ImGui::Separator();
3448 ImGui::TreePop();
3449 }
3450
3451 IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button");
3452 if (ImGui::TreeNode(label: "Advanced & Close Button"))
3453 {
3454 // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).
3455 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;
3456 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_Reorderable", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_Reorderable);
3457 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_AutoSelectNewTabs", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_AutoSelectNewTabs);
3458 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_TabListPopupButton", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_TabListPopupButton);
3459 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);
3460 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_DrawSelectedOverline", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_DrawSelectedOverline);
3461 if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
3462 tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
3463 if (ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_FittingPolicyResizeDown", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_FittingPolicyResizeDown))
3464 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
3465 if (ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_FittingPolicyScroll", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_FittingPolicyScroll))
3466 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
3467
3468 // Tab Bar
3469 ImGui::AlignTextToFramePadding();
3470 ImGui::Text(fmt: "Opened:");
3471 const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" };
3472 static bool opened[4] = { true, true, true, true }; // Persistent user state
3473 for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3474 {
3475 ImGui::SameLine();
3476 ImGui::Checkbox(label: names[n], v: &opened[n]);
3477 }
3478
3479 // Passing a bool* to BeginTabItem() is similar to passing one to Begin():
3480 // the underlying bool will be set to false when the tab is closed.
3481 if (ImGui::BeginTabBar(str_id: "MyTabBar", flags: tab_bar_flags))
3482 {
3483 for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3484 if (opened[n] && ImGui::BeginTabItem(label: names[n], p_open: &opened[n], flags: ImGuiTabItemFlags_None))
3485 {
3486 ImGui::Text(fmt: "This is the %s tab!", names[n]);
3487 if (n & 1)
3488 ImGui::Text(fmt: "I am an odd tab.");
3489 ImGui::EndTabItem();
3490 }
3491 ImGui::EndTabBar();
3492 }
3493 ImGui::Separator();
3494 ImGui::TreePop();
3495 }
3496
3497 IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags");
3498 if (ImGui::TreeNode(label: "TabItemButton & Leading/Trailing flags"))
3499 {
3500 static ImVector<int> active_tabs;
3501 static int next_tab_id = 0;
3502 if (next_tab_id == 0) // Initialize with some default tabs
3503 for (int i = 0; i < 3; i++)
3504 active_tabs.push_back(v: next_tab_id++);
3505
3506 // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.
3507 // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...
3508 // but they tend to make more sense together)
3509 static bool show_leading_button = true;
3510 static bool show_trailing_button = true;
3511 ImGui::Checkbox(label: "Show Leading TabItemButton()", v: &show_leading_button);
3512 ImGui::Checkbox(label: "Show Trailing TabItemButton()", v: &show_trailing_button);
3513
3514 // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
3515 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown;
3516 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_TabListPopupButton", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_TabListPopupButton);
3517 if (ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_FittingPolicyResizeDown", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_FittingPolicyResizeDown))
3518 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
3519 if (ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_FittingPolicyScroll", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_FittingPolicyScroll))
3520 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
3521
3522 if (ImGui::BeginTabBar(str_id: "MyTabBar", flags: tab_bar_flags))
3523 {
3524 // Demo a Leading TabItemButton(): click the "?" button to open a menu
3525 if (show_leading_button)
3526 if (ImGui::TabItemButton(label: "?", flags: ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))
3527 ImGui::OpenPopup(str_id: "MyHelpMenu");
3528 if (ImGui::BeginPopup(str_id: "MyHelpMenu"))
3529 {
3530 ImGui::Selectable(label: "Hello!");
3531 ImGui::EndPopup();
3532 }
3533
3534 // Demo Trailing Tabs: click the "+" button to add a new tab.
3535 // (In your app you may want to use a font icon instead of the "+")
3536 // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end.
3537 if (show_trailing_button)
3538 if (ImGui::TabItemButton(label: "+", flags: ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))
3539 active_tabs.push_back(v: next_tab_id++); // Add new tab
3540
3541 // Submit our regular tabs
3542 for (int n = 0; n < active_tabs.Size; )
3543 {
3544 bool open = true;
3545 char name[16];
3546 snprintf(s: name, IM_ARRAYSIZE(name), format: "%04d", active_tabs[n]);
3547 if (ImGui::BeginTabItem(label: name, p_open: &open, flags: ImGuiTabItemFlags_None))
3548 {
3549 ImGui::Text(fmt: "This is the %s tab!", name);
3550 ImGui::EndTabItem();
3551 }
3552
3553 if (!open)
3554 active_tabs.erase(it: active_tabs.Data + n);
3555 else
3556 n++;
3557 }
3558
3559 ImGui::EndTabBar();
3560 }
3561 ImGui::Separator();
3562 ImGui::TreePop();
3563 }
3564 ImGui::TreePop();
3565 }
3566}
3567
3568//-----------------------------------------------------------------------------
3569// [SECTION] DemoWindowWidgetsText()
3570//-----------------------------------------------------------------------------
3571
3572static void DemoWindowWidgetsText()
3573{
3574 IMGUI_DEMO_MARKER("Widgets/Text");
3575 if (ImGui::TreeNode(label: "Text"))
3576 {
3577 IMGUI_DEMO_MARKER("Widgets/Text/Colored Text");
3578 if (ImGui::TreeNode(label: "Colorful Text"))
3579 {
3580 // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
3581 ImGui::TextColored(col: ImVec4(1.0f, 0.0f, 1.0f, 1.0f), fmt: "Pink");
3582 ImGui::TextColored(col: ImVec4(1.0f, 1.0f, 0.0f, 1.0f), fmt: "Yellow");
3583 ImGui::TextDisabled(fmt: "Disabled");
3584 ImGui::SameLine(); HelpMarker(desc: "The TextDisabled color is stored in ImGuiStyle.");
3585 ImGui::TreePop();
3586 }
3587
3588 IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping");
3589 if (ImGui::TreeNode(label: "Word Wrapping"))
3590 {
3591 // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
3592 ImGui::TextWrapped(
3593 fmt: "This text should automatically wrap on the edge of the window. The current implementation "
3594 "for text wrapping follows simple rules suitable for English and possibly other languages.");
3595 ImGui::Spacing();
3596
3597 static float wrap_width = 200.0f;
3598 ImGui::SliderFloat(label: "Wrap width", v: &wrap_width, v_min: -20, v_max: 600, format: "%.0f");
3599
3600 ImDrawList* draw_list = ImGui::GetWindowDrawList();
3601 for (int n = 0; n < 2; n++)
3602 {
3603 ImGui::Text(fmt: "Test paragraph %d:", n);
3604 ImVec2 pos = ImGui::GetCursorScreenPos();
3605 ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);
3606 ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());
3607 ImGui::PushTextWrapPos(wrap_local_pos_x: ImGui::GetCursorPos().x + wrap_width);
3608 if (n == 0)
3609 ImGui::Text(fmt: "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);
3610 else
3611 ImGui::Text(fmt: "aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh");
3612
3613 // Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
3614 draw_list->AddRect(p_min: ImGui::GetItemRectMin(), p_max: ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));
3615 draw_list->AddRectFilled(p_min: marker_min, p_max: marker_max, IM_COL32(255, 0, 255, 255));
3616 ImGui::PopTextWrapPos();
3617 }
3618
3619 ImGui::TreePop();
3620 }
3621
3622 IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text");
3623 if (ImGui::TreeNode(label: "UTF-8 Text"))
3624 {
3625 // UTF-8 test with Japanese characters
3626 // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
3627 // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
3628 // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
3629 // can save your source files as 'UTF-8 without signature').
3630 // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
3631 // CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
3632 // Don't do this in your application! Please use u8"text in any language" in your application!
3633 // Note that characters values are preserved even by InputText() if the font cannot be displayed,
3634 // so you can safely copy & paste garbled characters into another application.
3635 ImGui::TextWrapped(
3636 fmt: "CJK text will only appear if the font was loaded with the appropriate CJK character ranges. "
3637 "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. "
3638 "Read docs/FONTS.md for details.");
3639 ImGui::Text(fmt: "Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
3640 ImGui::Text(fmt: "Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
3641 static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
3642 //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis
3643 ImGui::InputText(label: "UTF-8 input", buf, IM_ARRAYSIZE(buf));
3644 ImGui::TreePop();
3645 }
3646 ImGui::TreePop();
3647 }
3648}
3649
3650//-----------------------------------------------------------------------------
3651// [SECTION] DemoWindowWidgetsTextFilter()
3652//-----------------------------------------------------------------------------
3653
3654static void DemoWindowWidgetsTextFilter()
3655{
3656 IMGUI_DEMO_MARKER("Widgets/Text Filter");
3657 if (ImGui::TreeNode(label: "Text Filter"))
3658 {
3659 // Helper class to easy setup a text filter.
3660 // You may want to implement a more feature-full filtering scheme in your own application.
3661 HelpMarker(desc: "Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.");
3662 static ImGuiTextFilter filter;
3663 ImGui::Text(fmt: "Filter usage:\n"
3664 " \"\" display all lines\n"
3665 " \"xxx\" display lines containing \"xxx\"\n"
3666 " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n"
3667 " \"-xxx\" hide lines containing \"xxx\"");
3668 filter.Draw();
3669 const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" };
3670 for (int i = 0; i < IM_ARRAYSIZE(lines); i++)
3671 if (filter.PassFilter(text: lines[i]))
3672 ImGui::BulletText(fmt: "%s", lines[i]);
3673 ImGui::TreePop();
3674 }
3675}
3676
3677//-----------------------------------------------------------------------------
3678// [SECTION] DemoWindowWidgetsTextInput()
3679//-----------------------------------------------------------------------------
3680
3681static void DemoWindowWidgetsTextInput()
3682{
3683 // To wire InputText() with std::string or any other custom string type,
3684 // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
3685 IMGUI_DEMO_MARKER("Widgets/Text Input");
3686 if (ImGui::TreeNode(label: "Text Input"))
3687 {
3688 IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input");
3689 if (ImGui::TreeNode(label: "Multi-line Text Input"))
3690 {
3691 // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize
3692 // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.
3693 static char text[1024 * 16] =
3694 "/*\n"
3695 " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"
3696 " the hexadecimal encoding of one offending instruction,\n"
3697 " more formally, the invalid operand with locked CMPXCHG8B\n"
3698 " instruction bug, is a design flaw in the majority of\n"
3699 " Intel Pentium, Pentium MMX, and Pentium OverDrive\n"
3700 " processors (all in the P5 microarchitecture).\n"
3701 "*/\n\n"
3702 "label:\n"
3703 "\tlock cmpxchg8b eax\n";
3704
3705 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
3706 HelpMarker(desc: "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)");
3707 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ReadOnly", flags: &flags, flags_value: ImGuiInputTextFlags_ReadOnly);
3708 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_AllowTabInput", flags: &flags, flags_value: ImGuiInputTextFlags_AllowTabInput);
3709 ImGui::SameLine(); HelpMarker(desc: "When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets.");
3710 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_CtrlEnterForNewLine", flags: &flags, flags_value: ImGuiInputTextFlags_CtrlEnterForNewLine);
3711 ImGui::InputTextMultiline(label: "##source", buf: text, IM_ARRAYSIZE(text), size: ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
3712 ImGui::TreePop();
3713 }
3714
3715 IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input");
3716 if (ImGui::TreeNode(label: "Filtered Text Input"))
3717 {
3718 struct TextFilters
3719 {
3720 // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)
3721 static int FilterCasingSwap(ImGuiInputTextCallbackData* data)
3722 {
3723 if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase
3724 else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase
3725 return 0;
3726 }
3727
3728 // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)
3729 static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)
3730 {
3731 if (data->EventChar < 256 && strchr(s: "imgui", c: (char)data->EventChar))
3732 return 0;
3733 return 1;
3734 }
3735 };
3736
3737 static char buf1[32] = ""; ImGui::InputText(label: "default", buf: buf1, buf_size: 32);
3738 static char buf2[32] = ""; ImGui::InputText(label: "decimal", buf: buf2, buf_size: 32, flags: ImGuiInputTextFlags_CharsDecimal);
3739 static char buf3[32] = ""; ImGui::InputText(label: "hexadecimal", buf: buf3, buf_size: 32, flags: ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
3740 static char buf4[32] = ""; ImGui::InputText(label: "uppercase", buf: buf4, buf_size: 32, flags: ImGuiInputTextFlags_CharsUppercase);
3741 static char buf5[32] = ""; ImGui::InputText(label: "no blank", buf: buf5, buf_size: 32, flags: ImGuiInputTextFlags_CharsNoBlank);
3742 static char buf6[32] = ""; ImGui::InputText(label: "casing swap", buf: buf6, buf_size: 32, flags: ImGuiInputTextFlags_CallbackCharFilter, callback: TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.
3743 static char buf7[32] = ""; ImGui::InputText(label: "\"imgui\"", buf: buf7, buf_size: 32, flags: ImGuiInputTextFlags_CallbackCharFilter, callback: TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.
3744 ImGui::TreePop();
3745 }
3746
3747 IMGUI_DEMO_MARKER("Widgets/Text Input/Password input");
3748 if (ImGui::TreeNode(label: "Password Input"))
3749 {
3750 static char password[64] = "password123";
3751 ImGui::InputText(label: "password", buf: password, IM_ARRAYSIZE(password), flags: ImGuiInputTextFlags_Password);
3752 ImGui::SameLine(); HelpMarker(desc: "Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
3753 ImGui::InputTextWithHint(label: "password (w/ hint)", hint: "<password>", buf: password, IM_ARRAYSIZE(password), flags: ImGuiInputTextFlags_Password);
3754 ImGui::InputText(label: "password (clear)", buf: password, IM_ARRAYSIZE(password));
3755 ImGui::TreePop();
3756 }
3757
3758 IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks");
3759 if (ImGui::TreeNode(label: "Completion, History, Edit Callbacks"))
3760 {
3761 struct Funcs
3762 {
3763 static int MyCallback(ImGuiInputTextCallbackData* data)
3764 {
3765 if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)
3766 {
3767 data->InsertChars(pos: data->CursorPos, text: "..");
3768 }
3769 else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)
3770 {
3771 if (data->EventKey == ImGuiKey_UpArrow)
3772 {
3773 data->DeleteChars(pos: 0, bytes_count: data->BufTextLen);
3774 data->InsertChars(pos: 0, text: "Pressed Up!");
3775 data->SelectAll();
3776 }
3777 else if (data->EventKey == ImGuiKey_DownArrow)
3778 {
3779 data->DeleteChars(pos: 0, bytes_count: data->BufTextLen);
3780 data->InsertChars(pos: 0, text: "Pressed Down!");
3781 data->SelectAll();
3782 }
3783 }
3784 else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)
3785 {
3786 // Toggle casing of first character
3787 char c = data->Buf[0];
3788 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32;
3789 data->BufDirty = true;
3790
3791 // Increment a counter
3792 int* p_int = (int*)data->UserData;
3793 *p_int = *p_int + 1;
3794 }
3795 return 0;
3796 }
3797 };
3798 static char buf1[64];
3799 ImGui::InputText(label: "Completion", buf: buf1, buf_size: 64, flags: ImGuiInputTextFlags_CallbackCompletion, callback: Funcs::MyCallback);
3800 ImGui::SameLine(); HelpMarker(
3801 desc: "Here we append \"..\" each time Tab is pressed. "
3802 "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3803
3804 static char buf2[64];
3805 ImGui::InputText(label: "History", buf: buf2, buf_size: 64, flags: ImGuiInputTextFlags_CallbackHistory, callback: Funcs::MyCallback);
3806 ImGui::SameLine(); HelpMarker(
3807 desc: "Here we replace and select text each time Up/Down are pressed. "
3808 "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3809
3810 static char buf3[64];
3811 static int edit_count = 0;
3812 ImGui::InputText(label: "Edit", buf: buf3, buf_size: 64, flags: ImGuiInputTextFlags_CallbackEdit, callback: Funcs::MyCallback, user_data: (void*)&edit_count);
3813 ImGui::SameLine(); HelpMarker(
3814 desc: "Here we toggle the casing of the first character on every edit + count edits.");
3815 ImGui::SameLine(); ImGui::Text(fmt: "(%d)", edit_count);
3816
3817 ImGui::TreePop();
3818 }
3819
3820 IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback");
3821 if (ImGui::TreeNode(label: "Resize Callback"))
3822 {
3823 // To wire InputText() with std::string or any other custom string type,
3824 // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
3825 // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
3826 HelpMarker(
3827 desc: "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n"
3828 "See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
3829 struct Funcs
3830 {
3831 static int MyResizeCallback(ImGuiInputTextCallbackData* data)
3832 {
3833 if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
3834 {
3835 ImVector<char>* my_str = (ImVector<char>*)data->UserData;
3836 IM_ASSERT(my_str->begin() == data->Buf);
3837 my_str->resize(new_size: data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
3838 data->Buf = my_str->begin();
3839 }
3840 return 0;
3841 }
3842
3843 // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
3844 // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
3845 static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
3846 {
3847 IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
3848 return ImGui::InputTextMultiline(label, buf: my_str->begin(), buf_size: (size_t)my_str->size(), size, flags: flags | ImGuiInputTextFlags_CallbackResize, callback: Funcs::MyResizeCallback, user_data: (void*)my_str);
3849 }
3850 };
3851
3852 // For this demo we are using ImVector as a string container.
3853 // Note that because we need to store a terminating zero character, our size/capacity are 1 more
3854 // than usually reported by a typical string class.
3855 static ImVector<char> my_str;
3856 if (my_str.empty())
3857 my_str.push_back(v: 0);
3858 Funcs::MyInputTextMultiline(label: "##MyStr", my_str: &my_str, size: ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));
3859 ImGui::Text(fmt: "Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
3860 ImGui::TreePop();
3861 }
3862
3863 IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment");
3864 if (ImGui::TreeNode(label: "Eliding, Alignment"))
3865 {
3866 static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp";
3867 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft;
3868 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ElideLeft", flags: &flags, flags_value: ImGuiInputTextFlags_ElideLeft);
3869 ImGui::InputText(label: "Path", buf: buf1, IM_ARRAYSIZE(buf1), flags);
3870 ImGui::TreePop();
3871 }
3872
3873 IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous");
3874 if (ImGui::TreeNode(label: "Miscellaneous"))
3875 {
3876 static char buf1[16];
3877 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll;
3878 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_EscapeClearsAll", flags: &flags, flags_value: ImGuiInputTextFlags_EscapeClearsAll);
3879 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ReadOnly", flags: &flags, flags_value: ImGuiInputTextFlags_ReadOnly);
3880 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_NoUndoRedo", flags: &flags, flags_value: ImGuiInputTextFlags_NoUndoRedo);
3881 ImGui::InputText(label: "Hello", buf: buf1, IM_ARRAYSIZE(buf1), flags);
3882 ImGui::TreePop();
3883 }
3884
3885 ImGui::TreePop();
3886 }
3887
3888}
3889
3890//-----------------------------------------------------------------------------
3891// [SECTION] DemoWindowWidgetsTooltips()
3892//-----------------------------------------------------------------------------
3893
3894static void DemoWindowWidgetsTooltips()
3895{
3896 IMGUI_DEMO_MARKER("Widgets/Tooltips");
3897 if (ImGui::TreeNode(label: "Tooltips"))
3898 {
3899 // Tooltips are windows following the mouse. They do not take focus away.
3900 ImGui::SeparatorText(label: "General");
3901
3902 // Typical use cases:
3903 // - Short-form (text only): SetItemTooltip("Hello");
3904 // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); }
3905
3906 // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); }
3907 // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); }
3908
3909 HelpMarker(
3910 desc: "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n"
3911 "We provide a helper SetItemTooltip() function to perform the two with standards flags.");
3912
3913 ImVec2 sz = ImVec2(-FLT_MIN, 0.0f);
3914
3915 ImGui::Button(label: "Basic", size: sz);
3916 ImGui::SetItemTooltip("I am a tooltip");
3917
3918 ImGui::Button(label: "Fancy", size: sz);
3919 if (ImGui::BeginItemTooltip())
3920 {
3921 ImGui::Text(fmt: "I am a fancy tooltip");
3922 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
3923 ImGui::PlotLines(label: "Curve", values: arr, IM_ARRAYSIZE(arr));
3924 ImGui::Text(fmt: "Sin(time) = %f", sinf(x: (float)ImGui::GetTime()));
3925 ImGui::EndTooltip();
3926 }
3927
3928 ImGui::SeparatorText(label: "Always On");
3929
3930 // Showcase NOT relying on a IsItemHovered() to emit a tooltip.
3931 // Here the tooltip is always emitted when 'always_on == true'.
3932 static int always_on = 0;
3933 ImGui::RadioButton(label: "Off", v: &always_on, v_button: 0);
3934 ImGui::SameLine();
3935 ImGui::RadioButton(label: "Always On (Simple)", v: &always_on, v_button: 1);
3936 ImGui::SameLine();
3937 ImGui::RadioButton(label: "Always On (Advanced)", v: &always_on, v_button: 2);
3938 if (always_on == 1)
3939 ImGui::SetTooltip("I am following you around.");
3940 else if (always_on == 2 && ImGui::BeginTooltip())
3941 {
3942 ImGui::ProgressBar(fraction: sinf(x: (float)ImGui::GetTime()) * 0.5f + 0.5f, size_arg: ImVec2(ImGui::GetFontSize() * 25, 0.0f));
3943 ImGui::EndTooltip();
3944 }
3945
3946 ImGui::SeparatorText(label: "Custom");
3947
3948 HelpMarker(
3949 desc: "Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize "
3950 "tooltip activation details across your application. You may however decide to use custom "
3951 "flags for a specific tooltip instance.");
3952
3953 // The following examples are passed for documentation purpose but may not be useful to most users.
3954 // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from
3955 // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.
3956 // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.
3957 ImGui::Button(label: "Manual", size: sz);
3958 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip))
3959 ImGui::SetTooltip("I am a manually emitted tooltip.");
3960
3961 ImGui::Button(label: "DelayNone", size: sz);
3962 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayNone))
3963 ImGui::SetTooltip("I am a tooltip with no delay.");
3964
3965 ImGui::Button(label: "DelayShort", size: sz);
3966 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))
3967 ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort);
3968
3969 ImGui::Button(label: "DelayLong", size: sz);
3970 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay))
3971 ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal);
3972
3973 ImGui::Button(label: "Stationary", size: sz);
3974 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_Stationary))
3975 ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating.");
3976
3977 // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',
3978 // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.
3979 ImGui::BeginDisabled();
3980 ImGui::Button(label: "Disabled item", size: sz);
3981 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip))
3982 ImGui::SetTooltip("I am a a tooltip for a disabled item.");
3983 ImGui::EndDisabled();
3984
3985 ImGui::TreePop();
3986 }
3987}
3988
3989//-----------------------------------------------------------------------------
3990// [SECTION] DemoWindowWidgetsTreeNodes()
3991//-----------------------------------------------------------------------------
3992
3993static void DemoWindowWidgetsTreeNodes()
3994{
3995 IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
3996 if (ImGui::TreeNode(label: "Tree Nodes"))
3997 {
3998 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees");
3999 if (ImGui::TreeNode(label: "Basic trees"))
4000 {
4001 for (int i = 0; i < 5; i++)
4002 {
4003 // Use SetNextItemOpen() so set the default state of a node to be open. We could
4004 // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
4005 if (i == 0)
4006 ImGui::SetNextItemOpen(is_open: true, cond: ImGuiCond_Once);
4007
4008 // Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict.
4009 // An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)',
4010 // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine.
4011 ImGui::PushID(int_id: i);
4012 if (ImGui::TreeNode(str_id: "", fmt: "Child %d", i))
4013 {
4014 ImGui::Text(fmt: "blah blah");
4015 ImGui::SameLine();
4016 if (ImGui::SmallButton(label: "button")) {}
4017 ImGui::TreePop();
4018 }
4019 ImGui::PopID();
4020 }
4021 ImGui::TreePop();
4022 }
4023
4024 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
4025 if (ImGui::TreeNode(label: "Advanced, with Selectable nodes"))
4026 {
4027 HelpMarker(
4028 desc: "This is a more typical looking tree with selectable nodes.\n"
4029 "Click to select, CTRL+Click to toggle, click on arrows or double-click to open.");
4030 static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
4031 static bool align_label_with_current_x_position = false;
4032 static bool test_drag_and_drop = false;
4033 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_OpenOnArrow", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_OpenOnArrow);
4034 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_OpenOnDoubleClick", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_OpenOnDoubleClick);
4035 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanAvailWidth", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker(desc: "Extend hit area to all available width instead of allowing more items to be laid out after the node.");
4036 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanFullWidth", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_SpanFullWidth);
4037 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanLabelWidth", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::SameLine(); HelpMarker(desc: "Reduce hit area to the text label and a bit of margin.");
4038 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanAllColumns", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker(desc: "For use in Tables only.");
4039 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_AllowOverlap", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_AllowOverlap);
4040 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_Framed", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker(desc: "Draw frame with background (e.g. for CollapsingHeader)");
4041 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_NavLeftJumpsBackHere", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_NavLeftJumpsBackHere);
4042 ImGui::Checkbox(label: "Align label with current X position", v: &align_label_with_current_x_position);
4043 ImGui::Checkbox(label: "Test tree node as drag source", v: &test_drag_and_drop);
4044 ImGui::Text(fmt: "Hello!");
4045 if (align_label_with_current_x_position)
4046 ImGui::Unindent(indent_w: ImGui::GetTreeNodeToLabelSpacing());
4047
4048 // 'selection_mask' is dumb representation of what may be user-side selection state.
4049 // You may retain selection state inside or outside your objects in whatever format you see fit.
4050 // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end
4051 /// of the loop. May be a pointer to your own node type, etc.
4052 static int selection_mask = (1 << 2);
4053 int node_clicked = -1;
4054 for (int i = 0; i < 6; i++)
4055 {
4056 // Disable the default "open on single-click behavior" + set Selected flag according to our selection.
4057 // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
4058 ImGuiTreeNodeFlags node_flags = base_flags;
4059 const bool is_selected = (selection_mask & (1 << i)) != 0;
4060 if (is_selected)
4061 node_flags |= ImGuiTreeNodeFlags_Selected;
4062 if (i < 3)
4063 {
4064 // Items 0..2 are Tree Node
4065 bool node_open = ImGui::TreeNodeEx(ptr_id: (void*)(intptr_t)i, flags: node_flags, fmt: "Selectable Node %d", i);
4066 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4067 node_clicked = i;
4068 if (test_drag_and_drop && ImGui::BeginDragDropSource())
4069 {
4070 ImGui::SetDragDropPayload(type: "_TREENODE", NULL, sz: 0);
4071 ImGui::Text(fmt: "This is a drag and drop source");
4072 ImGui::EndDragDropSource();
4073 }
4074 if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
4075 {
4076 // Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
4077 ImGui::SameLine();
4078 if (ImGui::SmallButton(label: "button")) {}
4079 }
4080 if (node_open)
4081 {
4082 ImGui::BulletText(fmt: "Blah blah\nBlah Blah");
4083 ImGui::SameLine();
4084 ImGui::SmallButton(label: "Button");
4085 ImGui::TreePop();
4086 }
4087 }
4088 else
4089 {
4090 // Items 3..5 are Tree Leaves
4091 // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
4092 // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
4093 node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
4094 ImGui::TreeNodeEx(ptr_id: (void*)(intptr_t)i, flags: node_flags, fmt: "Selectable Leaf %d", i);
4095 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4096 node_clicked = i;
4097 if (test_drag_and_drop && ImGui::BeginDragDropSource())
4098 {
4099 ImGui::SetDragDropPayload(type: "_TREENODE", NULL, sz: 0);
4100 ImGui::Text(fmt: "This is a drag and drop source");
4101 ImGui::EndDragDropSource();
4102 }
4103 }
4104 }
4105 if (node_clicked != -1)
4106 {
4107 // Update selection state
4108 // (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
4109 if (ImGui::GetIO().KeyCtrl)
4110 selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
4111 else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
4112 selection_mask = (1 << node_clicked); // Click to single-select
4113 }
4114 if (align_label_with_current_x_position)
4115 ImGui::Indent(indent_w: ImGui::GetTreeNodeToLabelSpacing());
4116 ImGui::TreePop();
4117 }
4118 ImGui::TreePop();
4119 }
4120}
4121
4122//-----------------------------------------------------------------------------
4123// [SECTION] DemoWindowWidgetsVerticalSliders()
4124//-----------------------------------------------------------------------------
4125
4126static void DemoWindowWidgetsVerticalSliders()
4127{
4128 IMGUI_DEMO_MARKER("Widgets/Vertical Sliders");
4129 if (ImGui::TreeNode(label: "Vertical Sliders"))
4130 {
4131 const float spacing = 4;
4132 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(spacing, spacing));
4133
4134 static int int_value = 0;
4135 ImGui::VSliderInt(label: "##int", size: ImVec2(18, 160), v: &int_value, v_min: 0, v_max: 5);
4136 ImGui::SameLine();
4137
4138 static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };
4139 ImGui::PushID(str_id: "set1");
4140 for (int i = 0; i < 7; i++)
4141 {
4142 if (i > 0) ImGui::SameLine();
4143 ImGui::PushID(int_id: i);
4144 ImGui::PushStyleColor(idx: ImGuiCol_FrameBg, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.5f, v: 0.5f));
4145 ImGui::PushStyleColor(idx: ImGuiCol_FrameBgHovered, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.6f, v: 0.5f));
4146 ImGui::PushStyleColor(idx: ImGuiCol_FrameBgActive, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.7f, v: 0.5f));
4147 ImGui::PushStyleColor(idx: ImGuiCol_SliderGrab, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.9f, v: 0.9f));
4148 ImGui::VSliderFloat(label: "##v", size: ImVec2(18, 160), v: &values[i], v_min: 0.0f, v_max: 1.0f, format: "");
4149 if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4150 ImGui::SetTooltip("%.3f", values[i]);
4151 ImGui::PopStyleColor(count: 4);
4152 ImGui::PopID();
4153 }
4154 ImGui::PopID();
4155
4156 ImGui::SameLine();
4157 ImGui::PushID(str_id: "set2");
4158 static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f };
4159 const int rows = 3;
4160 const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));
4161 for (int nx = 0; nx < 4; nx++)
4162 {
4163 if (nx > 0) ImGui::SameLine();
4164 ImGui::BeginGroup();
4165 for (int ny = 0; ny < rows; ny++)
4166 {
4167 ImGui::PushID(int_id: nx * rows + ny);
4168 ImGui::VSliderFloat(label: "##v", size: small_slider_size, v: &values2[nx], v_min: 0.0f, v_max: 1.0f, format: "");
4169 if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4170 ImGui::SetTooltip("%.3f", values2[nx]);
4171 ImGui::PopID();
4172 }
4173 ImGui::EndGroup();
4174 }
4175 ImGui::PopID();
4176
4177 ImGui::SameLine();
4178 ImGui::PushID(str_id: "set3");
4179 for (int i = 0; i < 4; i++)
4180 {
4181 if (i > 0) ImGui::SameLine();
4182 ImGui::PushID(int_id: i);
4183 ImGui::PushStyleVar(idx: ImGuiStyleVar_GrabMinSize, val: 40);
4184 ImGui::VSliderFloat(label: "##v", size: ImVec2(40, 160), v: &values[i], v_min: 0.0f, v_max: 1.0f, format: "%.2f\nsec");
4185 ImGui::PopStyleVar();
4186 ImGui::PopID();
4187 }
4188 ImGui::PopID();
4189 ImGui::PopStyleVar();
4190 ImGui::TreePop();
4191 }
4192}
4193
4194//-----------------------------------------------------------------------------
4195// [SECTION] DemoWindowWidgets()
4196//-----------------------------------------------------------------------------
4197
4198static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)
4199{
4200 IMGUI_DEMO_MARKER("Widgets");
4201 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
4202 if (!ImGui::CollapsingHeader(label: "Widgets"))
4203 return;
4204
4205 const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom
4206 if (disable_all)
4207 ImGui::BeginDisabled();
4208
4209 DemoWindowWidgetsBasic();
4210 DemoWindowWidgetsBullets();
4211 DemoWindowWidgetsCollapsingHeaders();
4212 DemoWindowWidgetsComboBoxes();
4213 DemoWindowWidgetsColorAndPickers();
4214 DemoWindowWidgetsDataTypes();
4215
4216 if (disable_all)
4217 ImGui::EndDisabled();
4218 DemoWindowWidgetsDisableBlocks(demo_data);
4219 if (disable_all)
4220 ImGui::BeginDisabled();
4221
4222 DemoWindowWidgetsDragAndDrop();
4223 DemoWindowWidgetsDragsAndSliders();
4224 DemoWindowWidgetsImages();
4225 DemoWindowWidgetsListBoxes();
4226 DemoWindowWidgetsMultiComponents();
4227 DemoWindowWidgetsPlotting();
4228 DemoWindowWidgetsProgressBars();
4229 DemoWindowWidgetsQueryingStatuses();
4230 DemoWindowWidgetsSelectables();
4231 DemoWindowWidgetsSelectionAndMultiSelect(demo_data);
4232 DemoWindowWidgetsTabs();
4233 DemoWindowWidgetsText();
4234 DemoWindowWidgetsTextFilter();
4235 DemoWindowWidgetsTextInput();
4236 DemoWindowWidgetsTooltips();
4237 DemoWindowWidgetsTreeNodes();
4238 DemoWindowWidgetsVerticalSliders();
4239
4240 if (disable_all)
4241 ImGui::EndDisabled();
4242}
4243
4244//-----------------------------------------------------------------------------
4245// [SECTION] DemoWindowLayout()
4246//-----------------------------------------------------------------------------
4247
4248static void DemoWindowLayout()
4249{
4250 IMGUI_DEMO_MARKER("Layout");
4251 if (!ImGui::CollapsingHeader(label: "Layout & Scrolling"))
4252 return;
4253
4254 IMGUI_DEMO_MARKER("Layout/Child windows");
4255 if (ImGui::TreeNode(label: "Child windows"))
4256 {
4257 ImGui::SeparatorText(label: "Child windows");
4258
4259 HelpMarker(desc: "Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.");
4260 static bool disable_mouse_wheel = false;
4261 static bool disable_menu = false;
4262 ImGui::Checkbox(label: "Disable Mouse Wheel", v: &disable_mouse_wheel);
4263 ImGui::Checkbox(label: "Disable Menu", v: &disable_menu);
4264
4265 // Child 1: no border, enable horizontal scrollbar
4266 {
4267 ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
4268 if (disable_mouse_wheel)
4269 window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4270 ImGui::BeginChild(str_id: "ChildL", size: ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), child_flags: ImGuiChildFlags_None, window_flags);
4271 for (int i = 0; i < 100; i++)
4272 ImGui::Text(fmt: "%04d: scrollable region", i);
4273 ImGui::EndChild();
4274 }
4275
4276 ImGui::SameLine();
4277
4278 // Child 2: rounded border
4279 {
4280 ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
4281 if (disable_mouse_wheel)
4282 window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4283 if (!disable_menu)
4284 window_flags |= ImGuiWindowFlags_MenuBar;
4285 ImGui::PushStyleVar(idx: ImGuiStyleVar_ChildRounding, val: 5.0f);
4286 ImGui::BeginChild(str_id: "ChildR", size: ImVec2(0, 260), child_flags: ImGuiChildFlags_Borders, window_flags);
4287 if (!disable_menu && ImGui::BeginMenuBar())
4288 {
4289 if (ImGui::BeginMenu(label: "Menu"))
4290 {
4291 ShowExampleMenuFile();
4292 ImGui::EndMenu();
4293 }
4294 ImGui::EndMenuBar();
4295 }
4296 if (ImGui::BeginTable(str_id: "split", columns: 2, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))
4297 {
4298 for (int i = 0; i < 100; i++)
4299 {
4300 char buf[32];
4301 sprintf(s: buf, format: "%03d", i);
4302 ImGui::TableNextColumn();
4303 ImGui::Button(label: buf, size: ImVec2(-FLT_MIN, 0.0f));
4304 }
4305 ImGui::EndTable();
4306 }
4307 ImGui::EndChild();
4308 ImGui::PopStyleVar();
4309 }
4310
4311 // Child 3: manual-resize
4312 ImGui::SeparatorText(label: "Manual-resize");
4313 {
4314 HelpMarker(desc: "Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
4315 //if (ImGui::Button("Set Height to 200"))
4316 // ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
4317
4318 ImGui::PushStyleColor(idx: ImGuiCol_ChildBg, col: ImGui::GetStyleColorVec4(idx: ImGuiCol_FrameBg));
4319 if (ImGui::BeginChild(str_id: "ResizableChild", size: ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
4320 for (int n = 0; n < 10; n++)
4321 ImGui::Text(fmt: "Line %04d", n);
4322 ImGui::PopStyleColor();
4323 ImGui::EndChild();
4324 }
4325
4326 // Child 4: auto-resizing height with a limit
4327 ImGui::SeparatorText(label: "Auto-resize with constraints");
4328 {
4329 static int draw_lines = 3;
4330 static int max_height_in_lines = 10;
4331 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4332 ImGui::DragInt(label: "Lines Count", v: &draw_lines, v_speed: 0.2f);
4333 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4334 ImGui::DragInt(label: "Max Height (in Lines)", v: &max_height_in_lines, v_speed: 0.2f);
4335
4336 ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), size_max: ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));
4337 if (ImGui::BeginChild(str_id: "ConstrainedChild", size: ImVec2(-FLT_MIN, 0.0f), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY))
4338 for (int n = 0; n < draw_lines; n++)
4339 ImGui::Text(fmt: "Line %04d", n);
4340 ImGui::EndChild();
4341 }
4342
4343 ImGui::SeparatorText(label: "Misc/Advanced");
4344
4345 // Demonstrate a few extra things
4346 // - Changing ImGuiCol_ChildBg (which is transparent black in default styles)
4347 // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
4348 // You can also call SetNextWindowPos() to position the child window. The parent window will effectively
4349 // layout from this position.
4350 // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
4351 // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.
4352 {
4353 static int offset_x = 0;
4354 static bool override_bg_color = true;
4355 static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;
4356 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4357 ImGui::DragInt(label: "Offset X", v: &offset_x, v_speed: 1.0f, v_min: -1000, v_max: 1000);
4358 ImGui::Checkbox(label: "Override ChildBg color", v: &override_bg_color);
4359 ImGui::CheckboxFlags(label: "ImGuiChildFlags_Borders", flags: &child_flags, flags_value: ImGuiChildFlags_Borders);
4360 ImGui::CheckboxFlags(label: "ImGuiChildFlags_AlwaysUseWindowPadding", flags: &child_flags, flags_value: ImGuiChildFlags_AlwaysUseWindowPadding);
4361 ImGui::CheckboxFlags(label: "ImGuiChildFlags_ResizeX", flags: &child_flags, flags_value: ImGuiChildFlags_ResizeX);
4362 ImGui::CheckboxFlags(label: "ImGuiChildFlags_ResizeY", flags: &child_flags, flags_value: ImGuiChildFlags_ResizeY);
4363 ImGui::CheckboxFlags(label: "ImGuiChildFlags_FrameStyle", flags: &child_flags, flags_value: ImGuiChildFlags_FrameStyle);
4364 ImGui::SameLine(); HelpMarker(desc: "Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.");
4365 if (child_flags & ImGuiChildFlags_FrameStyle)
4366 override_bg_color = false;
4367
4368 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
4369 if (override_bg_color)
4370 ImGui::PushStyleColor(idx: ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
4371 ImGui::BeginChild(str_id: "Red", size: ImVec2(200, 100), child_flags, window_flags: ImGuiWindowFlags_None);
4372 if (override_bg_color)
4373 ImGui::PopStyleColor();
4374
4375 for (int n = 0; n < 50; n++)
4376 ImGui::Text(fmt: "Some test %d", n);
4377 ImGui::EndChild();
4378 bool child_is_hovered = ImGui::IsItemHovered();
4379 ImVec2 child_rect_min = ImGui::GetItemRectMin();
4380 ImVec2 child_rect_max = ImGui::GetItemRectMax();
4381 ImGui::Text(fmt: "Hovered: %d", child_is_hovered);
4382 ImGui::Text(fmt: "Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y);
4383 }
4384
4385 ImGui::TreePop();
4386 }
4387
4388 IMGUI_DEMO_MARKER("Layout/Widgets Width");
4389 if (ImGui::TreeNode(label: "Widgets Width"))
4390 {
4391 static float f = 0.0f;
4392 static bool show_indented_items = true;
4393 ImGui::Checkbox(label: "Show indented items", v: &show_indented_items);
4394
4395 // Use SetNextItemWidth() to set the width of a single upcoming item.
4396 // Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
4397 // In real code use you'll probably want to choose width values that are proportional to your font size
4398 // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
4399
4400 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(100)");
4401 ImGui::SameLine(); HelpMarker(desc: "Fixed width.");
4402 ImGui::PushItemWidth(item_width: 100);
4403 ImGui::DragFloat(label: "float##1b", v: &f);
4404 if (show_indented_items)
4405 {
4406 ImGui::Indent();
4407 ImGui::DragFloat(label: "float (indented)##1b", v: &f);
4408 ImGui::Unindent();
4409 }
4410 ImGui::PopItemWidth();
4411
4412 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(-100)");
4413 ImGui::SameLine(); HelpMarker(desc: "Align to right edge minus 100");
4414 ImGui::PushItemWidth(item_width: -100);
4415 ImGui::DragFloat(label: "float##2a", v: &f);
4416 if (show_indented_items)
4417 {
4418 ImGui::Indent();
4419 ImGui::DragFloat(label: "float (indented)##2b", v: &f);
4420 ImGui::Unindent();
4421 }
4422 ImGui::PopItemWidth();
4423
4424 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)");
4425 ImGui::SameLine(); HelpMarker(desc: "Half of available width.\n(~ right-cursor_pos)\n(works within a column set)");
4426 ImGui::PushItemWidth(item_width: ImGui::GetContentRegionAvail().x * 0.5f);
4427 ImGui::DragFloat(label: "float##3a", v: &f);
4428 if (show_indented_items)
4429 {
4430 ImGui::Indent();
4431 ImGui::DragFloat(label: "float (indented)##3b", v: &f);
4432 ImGui::Unindent();
4433 }
4434 ImGui::PopItemWidth();
4435
4436 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)");
4437 ImGui::SameLine(); HelpMarker(desc: "Align to right edge minus half");
4438 ImGui::PushItemWidth(item_width: -ImGui::GetContentRegionAvail().x * 0.5f);
4439 ImGui::DragFloat(label: "float##4a", v: &f);
4440 if (show_indented_items)
4441 {
4442 ImGui::Indent();
4443 ImGui::DragFloat(label: "float (indented)##4b", v: &f);
4444 ImGui::Unindent();
4445 }
4446 ImGui::PopItemWidth();
4447
4448 // Demonstrate using PushItemWidth to surround three items.
4449 // Calling SetNextItemWidth() before each of them would have the same effect.
4450 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(-FLT_MIN)");
4451 ImGui::SameLine(); HelpMarker(desc: "Align to right edge");
4452 ImGui::PushItemWidth(item_width: -FLT_MIN);
4453 ImGui::DragFloat(label: "##float5a", v: &f);
4454 if (show_indented_items)
4455 {
4456 ImGui::Indent();
4457 ImGui::DragFloat(label: "float (indented)##5b", v: &f);
4458 ImGui::Unindent();
4459 }
4460 ImGui::PopItemWidth();
4461
4462 ImGui::TreePop();
4463 }
4464
4465 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout");
4466 if (ImGui::TreeNode(label: "Basic Horizontal Layout"))
4467 {
4468 ImGui::TextWrapped(fmt: "(Use ImGui::SameLine() to keep adding items to the right of the preceding item)");
4469
4470 // Text
4471 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine");
4472 ImGui::Text(fmt: "Two items: Hello"); ImGui::SameLine();
4473 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Sailor");
4474
4475 // Adjust spacing
4476 ImGui::Text(fmt: "More spacing: Hello"); ImGui::SameLine(offset_from_start_x: 0, spacing: 20);
4477 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Sailor");
4478
4479 // Button
4480 ImGui::AlignTextToFramePadding();
4481 ImGui::Text(fmt: "Normal buttons"); ImGui::SameLine();
4482 ImGui::Button(label: "Banana"); ImGui::SameLine();
4483 ImGui::Button(label: "Apple"); ImGui::SameLine();
4484 ImGui::Button(label: "Corniflower");
4485
4486 // Button
4487 ImGui::Text(fmt: "Small buttons"); ImGui::SameLine();
4488 ImGui::SmallButton(label: "Like this one"); ImGui::SameLine();
4489 ImGui::Text(fmt: "can fit within a text block.");
4490
4491 // Aligned to arbitrary position. Easy/cheap column.
4492 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)");
4493 ImGui::Text(fmt: "Aligned");
4494 ImGui::SameLine(offset_from_start_x: 150); ImGui::Text(fmt: "x=150");
4495 ImGui::SameLine(offset_from_start_x: 300); ImGui::Text(fmt: "x=300");
4496 ImGui::Text(fmt: "Aligned");
4497 ImGui::SameLine(offset_from_start_x: 150); ImGui::SmallButton(label: "x=150");
4498 ImGui::SameLine(offset_from_start_x: 300); ImGui::SmallButton(label: "x=300");
4499
4500 // Checkbox
4501 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)");
4502 static bool c1 = false, c2 = false, c3 = false, c4 = false;
4503 ImGui::Checkbox(label: "My", v: &c1); ImGui::SameLine();
4504 ImGui::Checkbox(label: "Tailor", v: &c2); ImGui::SameLine();
4505 ImGui::Checkbox(label: "Is", v: &c3); ImGui::SameLine();
4506 ImGui::Checkbox(label: "Rich", v: &c4);
4507
4508 // Various
4509 static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;
4510 ImGui::PushItemWidth(item_width: 80);
4511 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" };
4512 static int item = -1;
4513 ImGui::Combo(label: "Combo", current_item: &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine();
4514 ImGui::SliderFloat(label: "X", v: &f0, v_min: 0.0f, v_max: 5.0f); ImGui::SameLine();
4515 ImGui::SliderFloat(label: "Y", v: &f1, v_min: 0.0f, v_max: 5.0f); ImGui::SameLine();
4516 ImGui::SliderFloat(label: "Z", v: &f2, v_min: 0.0f, v_max: 5.0f);
4517 ImGui::PopItemWidth();
4518
4519 ImGui::PushItemWidth(item_width: 80);
4520 ImGui::Text(fmt: "Lists:");
4521 static int selection[4] = { 0, 1, 2, 3 };
4522 for (int i = 0; i < 4; i++)
4523 {
4524 if (i > 0) ImGui::SameLine();
4525 ImGui::PushID(int_id: i);
4526 ImGui::ListBox(label: "", current_item: &selection[i], items, IM_ARRAYSIZE(items));
4527 ImGui::PopID();
4528 //ImGui::SetItemTooltip("ListBox %d hovered", i);
4529 }
4530 ImGui::PopItemWidth();
4531
4532 // Dummy
4533 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy");
4534 ImVec2 button_sz(40, 40);
4535 ImGui::Button(label: "A", size: button_sz); ImGui::SameLine();
4536 ImGui::Dummy(size: button_sz); ImGui::SameLine();
4537 ImGui::Button(label: "B", size: button_sz);
4538
4539 // Manually wrapping
4540 // (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
4541 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping");
4542 ImGui::Text(fmt: "Manual wrapping:");
4543 ImGuiStyle& style = ImGui::GetStyle();
4544 int buttons_count = 20;
4545 float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
4546 for (int n = 0; n < buttons_count; n++)
4547 {
4548 ImGui::PushID(int_id: n);
4549 ImGui::Button(label: "Box", size: button_sz);
4550 float last_button_x2 = ImGui::GetItemRectMax().x;
4551 float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line
4552 if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)
4553 ImGui::SameLine();
4554 ImGui::PopID();
4555 }
4556
4557 ImGui::TreePop();
4558 }
4559
4560 IMGUI_DEMO_MARKER("Layout/Groups");
4561 if (ImGui::TreeNode(label: "Groups"))
4562 {
4563 HelpMarker(
4564 desc: "BeginGroup() basically locks the horizontal position for new line. "
4565 "EndGroup() bundles the whole group so that you can use \"item\" functions such as "
4566 "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
4567 ImGui::BeginGroup();
4568 {
4569 ImGui::BeginGroup();
4570 ImGui::Button(label: "AAA");
4571 ImGui::SameLine();
4572 ImGui::Button(label: "BBB");
4573 ImGui::SameLine();
4574 ImGui::BeginGroup();
4575 ImGui::Button(label: "CCC");
4576 ImGui::Button(label: "DDD");
4577 ImGui::EndGroup();
4578 ImGui::SameLine();
4579 ImGui::Button(label: "EEE");
4580 ImGui::EndGroup();
4581 ImGui::SetItemTooltip("First group hovered");
4582 }
4583 // Capture the group size and create widgets using the same size
4584 ImVec2 size = ImGui::GetItemRectSize();
4585 const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };
4586 ImGui::PlotHistogram(label: "##values", values, IM_ARRAYSIZE(values), values_offset: 0, NULL, scale_min: 0.0f, scale_max: 1.0f, graph_size: size);
4587
4588 ImGui::Button(label: "ACTION", size: ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4589 ImGui::SameLine();
4590 ImGui::Button(label: "REACTION", size: ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4591 ImGui::EndGroup();
4592 ImGui::SameLine();
4593
4594 ImGui::Button(label: "LEVERAGE\nBUZZWORD", size);
4595 ImGui::SameLine();
4596
4597 if (ImGui::BeginListBox(label: "List", size))
4598 {
4599 ImGui::Selectable(label: "Selected", selected: true);
4600 ImGui::Selectable(label: "Not Selected", selected: false);
4601 ImGui::EndListBox();
4602 }
4603
4604 ImGui::TreePop();
4605 }
4606
4607 IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment");
4608 if (ImGui::TreeNode(label: "Text Baseline Alignment"))
4609 {
4610 {
4611 ImGui::BulletText(fmt: "Text baseline:");
4612 ImGui::SameLine(); HelpMarker(
4613 desc: "This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. "
4614 "Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets.");
4615 ImGui::Indent();
4616
4617 ImGui::Text(fmt: "KO Blahblah"); ImGui::SameLine();
4618 ImGui::Button(label: "Some framed item"); ImGui::SameLine();
4619 HelpMarker(desc: "Baseline of button will look misaligned with text..");
4620
4621 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4622 // (because we don't know what's coming after the Text() statement, we need to move the text baseline
4623 // down by FramePadding.y ahead of time)
4624 ImGui::AlignTextToFramePadding();
4625 ImGui::Text(fmt: "OK Blahblah"); ImGui::SameLine();
4626 ImGui::Button(label: "Some framed item##2"); ImGui::SameLine();
4627 HelpMarker(desc: "We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y");
4628
4629 // SmallButton() uses the same vertical padding as Text
4630 ImGui::Button(label: "TEST##1"); ImGui::SameLine();
4631 ImGui::Text(fmt: "TEST"); ImGui::SameLine();
4632 ImGui::SmallButton(label: "TEST##2");
4633
4634 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4635 ImGui::AlignTextToFramePadding();
4636 ImGui::Text(fmt: "Text aligned to framed item"); ImGui::SameLine();
4637 ImGui::Button(label: "Item##1"); ImGui::SameLine();
4638 ImGui::Text(fmt: "Item"); ImGui::SameLine();
4639 ImGui::SmallButton(label: "Item##2"); ImGui::SameLine();
4640 ImGui::Button(label: "Item##3");
4641
4642 ImGui::Unindent();
4643 }
4644
4645 ImGui::Spacing();
4646
4647 {
4648 ImGui::BulletText(fmt: "Multi-line text:");
4649 ImGui::Indent();
4650 ImGui::Text(fmt: "One\nTwo\nThree"); ImGui::SameLine();
4651 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4652 ImGui::Text(fmt: "Banana");
4653
4654 ImGui::Text(fmt: "Banana"); ImGui::SameLine();
4655 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4656 ImGui::Text(fmt: "One\nTwo\nThree");
4657
4658 ImGui::Button(label: "HOP##1"); ImGui::SameLine();
4659 ImGui::Text(fmt: "Banana"); ImGui::SameLine();
4660 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4661 ImGui::Text(fmt: "Banana");
4662
4663 ImGui::Button(label: "HOP##2"); ImGui::SameLine();
4664 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4665 ImGui::Text(fmt: "Banana");
4666 ImGui::Unindent();
4667 }
4668
4669 ImGui::Spacing();
4670
4671 {
4672 ImGui::BulletText(fmt: "Misc items:");
4673 ImGui::Indent();
4674
4675 // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
4676 ImGui::Button(label: "80x80", size: ImVec2(80, 80));
4677 ImGui::SameLine();
4678 ImGui::Button(label: "50x50", size: ImVec2(50, 50));
4679 ImGui::SameLine();
4680 ImGui::Button(label: "Button()");
4681 ImGui::SameLine();
4682 ImGui::SmallButton(label: "SmallButton()");
4683
4684 // Tree
4685 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
4686 ImGui::Button(label: "Button##1");
4687 ImGui::SameLine(offset_from_start_x: 0.0f, spacing);
4688 if (ImGui::TreeNode(label: "Node##1"))
4689 {
4690 // Placeholder tree data
4691 for (int i = 0; i < 6; i++)
4692 ImGui::BulletText(fmt: "Item %d..", i);
4693 ImGui::TreePop();
4694 }
4695
4696 // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
4697 // Otherwise you can use SmallButton() (smaller fit).
4698 ImGui::AlignTextToFramePadding();
4699
4700 // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
4701 // other contents below the node.
4702 bool node_open = ImGui::TreeNode(label: "Node##2");
4703 ImGui::SameLine(offset_from_start_x: 0.0f, spacing); ImGui::Button(label: "Button##2");
4704 if (node_open)
4705 {
4706 // Placeholder tree data
4707 for (int i = 0; i < 6; i++)
4708 ImGui::BulletText(fmt: "Item %d..", i);
4709 ImGui::TreePop();
4710 }
4711
4712 // Bullet
4713 ImGui::Button(label: "Button##3");
4714 ImGui::SameLine(offset_from_start_x: 0.0f, spacing);
4715 ImGui::BulletText(fmt: "Bullet text");
4716
4717 ImGui::AlignTextToFramePadding();
4718 ImGui::BulletText(fmt: "Node");
4719 ImGui::SameLine(offset_from_start_x: 0.0f, spacing); ImGui::Button(label: "Button##4");
4720 ImGui::Unindent();
4721 }
4722
4723 ImGui::TreePop();
4724 }
4725
4726 IMGUI_DEMO_MARKER("Layout/Scrolling");
4727 if (ImGui::TreeNode(label: "Scrolling"))
4728 {
4729 // Vertical scroll functions
4730 IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical");
4731 HelpMarker(desc: "Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.");
4732
4733 static int track_item = 50;
4734 static bool enable_track = true;
4735 static bool enable_extra_decorations = false;
4736 static float scroll_to_off_px = 0.0f;
4737 static float scroll_to_pos_px = 200.0f;
4738
4739 ImGui::Checkbox(label: "Decoration", v: &enable_extra_decorations);
4740
4741 ImGui::Checkbox(label: "Track", v: &enable_track);
4742 ImGui::PushItemWidth(item_width: 100);
4743 ImGui::SameLine(offset_from_start_x: 140); enable_track |= ImGui::DragInt(label: "##item", v: &track_item, v_speed: 0.25f, v_min: 0, v_max: 99, format: "Item = %d");
4744
4745 bool scroll_to_off = ImGui::Button(label: "Scroll Offset");
4746 ImGui::SameLine(offset_from_start_x: 140); scroll_to_off |= ImGui::DragFloat(label: "##off", v: &scroll_to_off_px, v_speed: 1.00f, v_min: 0, FLT_MAX, format: "+%.0f px");
4747
4748 bool scroll_to_pos = ImGui::Button(label: "Scroll To Pos");
4749 ImGui::SameLine(offset_from_start_x: 140); scroll_to_pos |= ImGui::DragFloat(label: "##pos", v: &scroll_to_pos_px, v_speed: 1.00f, v_min: -10, FLT_MAX, format: "X/Y = %.0f px");
4750 ImGui::PopItemWidth();
4751
4752 if (scroll_to_off || scroll_to_pos)
4753 enable_track = false;
4754
4755 ImGuiStyle& style = ImGui::GetStyle();
4756 float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;
4757 if (child_w < 1.0f)
4758 child_w = 1.0f;
4759 ImGui::PushID(str_id: "##VerticalScrolling");
4760 for (int i = 0; i < 5; i++)
4761 {
4762 if (i > 0) ImGui::SameLine();
4763 ImGui::BeginGroup();
4764 const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" };
4765 ImGui::TextUnformatted(text: names[i]);
4766
4767 const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
4768 const ImGuiID child_id = ImGui::GetID(ptr_id: (void*)(intptr_t)i);
4769 const bool child_is_visible = ImGui::BeginChild(id: child_id, size: ImVec2(child_w, 200.0f), child_flags: ImGuiChildFlags_Borders, window_flags: child_flags);
4770 if (ImGui::BeginMenuBar())
4771 {
4772 ImGui::TextUnformatted(text: "abc");
4773 ImGui::EndMenuBar();
4774 }
4775 if (scroll_to_off)
4776 ImGui::SetScrollY(scroll_to_off_px);
4777 if (scroll_to_pos)
4778 ImGui::SetScrollFromPosY(local_y: ImGui::GetCursorStartPos().y + scroll_to_pos_px, center_y_ratio: i * 0.25f);
4779 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4780 {
4781 for (int item = 0; item < 100; item++)
4782 {
4783 if (enable_track && item == track_item)
4784 {
4785 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Item %d", item);
4786 ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom
4787 }
4788 else
4789 {
4790 ImGui::Text(fmt: "Item %d", item);
4791 }
4792 }
4793 }
4794 float scroll_y = ImGui::GetScrollY();
4795 float scroll_max_y = ImGui::GetScrollMaxY();
4796 ImGui::EndChild();
4797 ImGui::Text(fmt: "%.0f/%.0f", scroll_y, scroll_max_y);
4798 ImGui::EndGroup();
4799 }
4800 ImGui::PopID();
4801
4802 // Horizontal scroll functions
4803 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal");
4804 ImGui::Spacing();
4805 HelpMarker(
4806 desc: "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
4807 "Because the clipping rectangle of most window hides half worth of WindowPadding on the "
4808 "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the "
4809 "equivalent SetScrollFromPosY(+1) wouldn't.");
4810 ImGui::PushID(str_id: "##HorizontalScrolling");
4811 for (int i = 0; i < 5; i++)
4812 {
4813 float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
4814 ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
4815 ImGuiID child_id = ImGui::GetID(ptr_id: (void*)(intptr_t)i);
4816 bool child_is_visible = ImGui::BeginChild(id: child_id, size: ImVec2(-100, child_height), child_flags: ImGuiChildFlags_Borders, window_flags: child_flags);
4817 if (scroll_to_off)
4818 ImGui::SetScrollX(scroll_to_off_px);
4819 if (scroll_to_pos)
4820 ImGui::SetScrollFromPosX(local_x: ImGui::GetCursorStartPos().x + scroll_to_pos_px, center_x_ratio: i * 0.25f);
4821 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4822 {
4823 for (int item = 0; item < 100; item++)
4824 {
4825 if (item > 0)
4826 ImGui::SameLine();
4827 if (enable_track && item == track_item)
4828 {
4829 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Item %d", item);
4830 ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right
4831 }
4832 else
4833 {
4834 ImGui::Text(fmt: "Item %d", item);
4835 }
4836 }
4837 }
4838 float scroll_x = ImGui::GetScrollX();
4839 float scroll_max_x = ImGui::GetScrollMaxX();
4840 ImGui::EndChild();
4841 ImGui::SameLine();
4842 const char* names[] = { "Left", "25%", "Center", "75%", "Right" };
4843 ImGui::Text(fmt: "%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x);
4844 ImGui::Spacing();
4845 }
4846 ImGui::PopID();
4847
4848 // Miscellaneous Horizontal Scrolling Demo
4849 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)");
4850 HelpMarker(
4851 desc: "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n"
4852 "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().");
4853 static int lines = 7;
4854 ImGui::SliderInt(label: "Lines", v: &lines, v_min: 1, v_max: 15);
4855 ImGui::PushStyleVar(idx: ImGuiStyleVar_FrameRounding, val: 3.0f);
4856 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(2.0f, 1.0f));
4857 ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
4858 ImGui::BeginChild(str_id: "scrolling", size: scrolling_child_size, child_flags: ImGuiChildFlags_Borders, window_flags: ImGuiWindowFlags_HorizontalScrollbar);
4859 for (int line = 0; line < lines; line++)
4860 {
4861 // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
4862 // If you want to create your own time line for a real application you may be better off manipulating
4863 // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
4864 // yourself. You may also want to use the lower-level ImDrawList API.
4865 int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);
4866 for (int n = 0; n < num_buttons; n++)
4867 {
4868 if (n > 0) ImGui::SameLine();
4869 ImGui::PushID(int_id: n + line * 1000);
4870 char num_buf[16];
4871 sprintf(s: num_buf, format: "%d", n);
4872 const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf;
4873 float hue = n * 0.05f;
4874 ImGui::PushStyleColor(idx: ImGuiCol_Button, col: (ImVec4)ImColor::HSV(h: hue, s: 0.6f, v: 0.6f));
4875 ImGui::PushStyleColor(idx: ImGuiCol_ButtonHovered, col: (ImVec4)ImColor::HSV(h: hue, s: 0.7f, v: 0.7f));
4876 ImGui::PushStyleColor(idx: ImGuiCol_ButtonActive, col: (ImVec4)ImColor::HSV(h: hue, s: 0.8f, v: 0.8f));
4877 ImGui::Button(label, size: ImVec2(40.0f + sinf(x: (float)(line + n)) * 20.0f, 0.0f));
4878 ImGui::PopStyleColor(count: 3);
4879 ImGui::PopID();
4880 }
4881 }
4882 float scroll_x = ImGui::GetScrollX();
4883 float scroll_max_x = ImGui::GetScrollMaxX();
4884 ImGui::EndChild();
4885 ImGui::PopStyleVar(count: 2);
4886 float scroll_x_delta = 0.0f;
4887 ImGui::SmallButton(label: "<<");
4888 if (ImGui::IsItemActive())
4889 scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;
4890 ImGui::SameLine();
4891 ImGui::Text(fmt: "Scroll from code"); ImGui::SameLine();
4892 ImGui::SmallButton(label: ">>");
4893 if (ImGui::IsItemActive())
4894 scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;
4895 ImGui::SameLine();
4896 ImGui::Text(fmt: "%.0f/%.0f", scroll_x, scroll_max_x);
4897 if (scroll_x_delta != 0.0f)
4898 {
4899 // Demonstrate a trick: you can use Begin to set yourself in the context of another window
4900 // (here we are already out of your child window)
4901 ImGui::BeginChild(str_id: "scrolling");
4902 ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
4903 ImGui::EndChild();
4904 }
4905 ImGui::Spacing();
4906
4907 static bool show_horizontal_contents_size_demo_window = false;
4908 ImGui::Checkbox(label: "Show Horizontal contents size demo window", v: &show_horizontal_contents_size_demo_window);
4909
4910 if (show_horizontal_contents_size_demo_window)
4911 {
4912 static bool show_h_scrollbar = true;
4913 static bool show_button = true;
4914 static bool show_tree_nodes = true;
4915 static bool show_text_wrapped = false;
4916 static bool show_columns = true;
4917 static bool show_tab_bar = true;
4918 static bool show_child = false;
4919 static bool explicit_content_size = false;
4920 static float contents_size_x = 300.0f;
4921 if (explicit_content_size)
4922 ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));
4923 ImGui::Begin(name: "Horizontal contents size demo window", p_open: &show_horizontal_contents_size_demo_window, flags: show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);
4924 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window");
4925 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(2, 0));
4926 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(2, 0));
4927 HelpMarker(
4928 desc: "Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n"
4929 "Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles.");
4930 ImGui::Checkbox(label: "H-scrollbar", v: &show_h_scrollbar);
4931 ImGui::Checkbox(label: "Button", v: &show_button); // Will grow contents size (unless explicitly overwritten)
4932 ImGui::Checkbox(label: "Tree nodes", v: &show_tree_nodes); // Will grow contents size and display highlight over full width
4933 ImGui::Checkbox(label: "Text wrapped", v: &show_text_wrapped);// Will grow and use contents size
4934 ImGui::Checkbox(label: "Columns", v: &show_columns); // Will use contents size
4935 ImGui::Checkbox(label: "Tab bar", v: &show_tab_bar); // Will use contents size
4936 ImGui::Checkbox(label: "Child", v: &show_child); // Will grow and use contents size
4937 ImGui::Checkbox(label: "Explicit content size", v: &explicit_content_size);
4938 ImGui::Text(fmt: "Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY());
4939 if (explicit_content_size)
4940 {
4941 ImGui::SameLine();
4942 ImGui::SetNextItemWidth(100);
4943 ImGui::DragFloat(label: "##csx", v: &contents_size_x);
4944 ImVec2 p = ImGui::GetCursorScreenPos();
4945 ImGui::GetWindowDrawList()->AddRectFilled(p_min: p, p_max: ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);
4946 ImGui::GetWindowDrawList()->AddRectFilled(p_min: ImVec2(p.x + contents_size_x - 10, p.y), p_max: ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);
4947 ImGui::Dummy(size: ImVec2(0, 10));
4948 }
4949 ImGui::PopStyleVar(count: 2);
4950 ImGui::Separator();
4951 if (show_button)
4952 {
4953 ImGui::Button(label: "this is a 300-wide button", size: ImVec2(300, 0));
4954 }
4955 if (show_tree_nodes)
4956 {
4957 bool open = true;
4958 if (ImGui::TreeNode(label: "this is a tree node"))
4959 {
4960 if (ImGui::TreeNode(label: "another one of those tree node..."))
4961 {
4962 ImGui::Text(fmt: "Some tree contents");
4963 ImGui::TreePop();
4964 }
4965 ImGui::TreePop();
4966 }
4967 ImGui::CollapsingHeader(label: "CollapsingHeader", p_visible: &open);
4968 }
4969 if (show_text_wrapped)
4970 {
4971 ImGui::TextWrapped(fmt: "This text should automatically wrap on the edge of the work rectangle.");
4972 }
4973 if (show_columns)
4974 {
4975 ImGui::Text(fmt: "Tables:");
4976 if (ImGui::BeginTable(str_id: "table", columns: 4, flags: ImGuiTableFlags_Borders))
4977 {
4978 for (int n = 0; n < 4; n++)
4979 {
4980 ImGui::TableNextColumn();
4981 ImGui::Text(fmt: "Width %.2f", ImGui::GetContentRegionAvail().x);
4982 }
4983 ImGui::EndTable();
4984 }
4985 ImGui::Text(fmt: "Columns:");
4986 ImGui::Columns(count: 4);
4987 for (int n = 0; n < 4; n++)
4988 {
4989 ImGui::Text(fmt: "Width %.2f", ImGui::GetColumnWidth());
4990 ImGui::NextColumn();
4991 }
4992 ImGui::Columns(count: 1);
4993 }
4994 if (show_tab_bar && ImGui::BeginTabBar(str_id: "Hello"))
4995 {
4996 if (ImGui::BeginTabItem(label: "OneOneOne")) { ImGui::EndTabItem(); }
4997 if (ImGui::BeginTabItem(label: "TwoTwoTwo")) { ImGui::EndTabItem(); }
4998 if (ImGui::BeginTabItem(label: "ThreeThreeThree")) { ImGui::EndTabItem(); }
4999 if (ImGui::BeginTabItem(label: "FourFourFour")) { ImGui::EndTabItem(); }
5000 ImGui::EndTabBar();
5001 }
5002 if (show_child)
5003 {
5004 ImGui::BeginChild(str_id: "child", size: ImVec2(0, 0), child_flags: ImGuiChildFlags_Borders);
5005 ImGui::EndChild();
5006 }
5007 ImGui::End();
5008 }
5009
5010 ImGui::TreePop();
5011 }
5012
5013 IMGUI_DEMO_MARKER("Layout/Text Clipping");
5014 if (ImGui::TreeNode(label: "Text Clipping"))
5015 {
5016 static ImVec2 size(100.0f, 100.0f);
5017 static ImVec2 offset(30.0f, 30.0f);
5018 ImGui::DragFloat2(label: "size", v: (float*)&size, v_speed: 0.5f, v_min: 1.0f, v_max: 200.0f, format: "%.0f");
5019 ImGui::TextWrapped(fmt: "(Click and drag to scroll)");
5020
5021 HelpMarker(
5022 desc: "(Left) Using ImGui::PushClipRect():\n"
5023 "Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
5024 "(use this if you want your clipping rectangle to affect interactions)\n\n"
5025 "(Center) Using ImDrawList::PushClipRect():\n"
5026 "Will alter ImDrawList rendering only.\n"
5027 "(use this as a shortcut if you are only using ImDrawList calls)\n\n"
5028 "(Right) Using ImDrawList::AddText() with a fine ClipRect:\n"
5029 "Will alter only this specific ImDrawList::AddText() rendering.\n"
5030 "This is often used internally to avoid altering the clipping rectangle and minimize draw calls.");
5031
5032 for (int n = 0; n < 3; n++)
5033 {
5034 if (n > 0)
5035 ImGui::SameLine();
5036
5037 ImGui::PushID(int_id: n);
5038 ImGui::InvisibleButton(str_id: "##canvas", size);
5039 if (ImGui::IsItemActive() && ImGui::IsMouseDragging(button: ImGuiMouseButton_Left))
5040 {
5041 offset.x += ImGui::GetIO().MouseDelta.x;
5042 offset.y += ImGui::GetIO().MouseDelta.y;
5043 }
5044 ImGui::PopID();
5045 if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.
5046 continue;
5047
5048 const ImVec2 p0 = ImGui::GetItemRectMin();
5049 const ImVec2 p1 = ImGui::GetItemRectMax();
5050 const char* text_str = "Line 1 hello\nLine 2 clip me!";
5051 const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
5052 ImDrawList* draw_list = ImGui::GetWindowDrawList();
5053 switch (n)
5054 {
5055 case 0:
5056 ImGui::PushClipRect(clip_rect_min: p0, clip_rect_max: p1, intersect_with_current_clip_rect: true);
5057 draw_list->AddRectFilled(p_min: p0, p_max: p1, IM_COL32(90, 90, 120, 255));
5058 draw_list->AddText(pos: text_pos, IM_COL32_WHITE, text_begin: text_str);
5059 ImGui::PopClipRect();
5060 break;
5061 case 1:
5062 draw_list->PushClipRect(clip_rect_min: p0, clip_rect_max: p1, intersect_with_current_clip_rect: true);
5063 draw_list->AddRectFilled(p_min: p0, p_max: p1, IM_COL32(90, 90, 120, 255));
5064 draw_list->AddText(pos: text_pos, IM_COL32_WHITE, text_begin: text_str);
5065 draw_list->PopClipRect();
5066 break;
5067 case 2:
5068 ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
5069 draw_list->AddRectFilled(p_min: p0, p_max: p1, IM_COL32(90, 90, 120, 255));
5070 draw_list->AddText(font: ImGui::GetFont(), font_size: ImGui::GetFontSize(), pos: text_pos, IM_COL32_WHITE, text_begin: text_str, NULL, wrap_width: 0.0f, cpu_fine_clip_rect: &clip_rect);
5071 break;
5072 }
5073 }
5074
5075 ImGui::TreePop();
5076 }
5077
5078 IMGUI_DEMO_MARKER("Layout/Overlap Mode");
5079 if (ImGui::TreeNode(label: "Overlap Mode"))
5080 {
5081 static bool enable_allow_overlap = true;
5082
5083 HelpMarker(
5084 desc: "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n"
5085 "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. "
5086 "Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.");
5087 ImGui::Checkbox(label: "Enable AllowOverlap", v: &enable_allow_overlap);
5088
5089 ImVec2 button1_pos = ImGui::GetCursorScreenPos();
5090 ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f);
5091 if (enable_allow_overlap)
5092 ImGui::SetNextItemAllowOverlap();
5093 ImGui::Button(label: "Button 1", size: ImVec2(80, 80));
5094 ImGui::SetCursorScreenPos(button2_pos);
5095 ImGui::Button(label: "Button 2", size: ImVec2(80, 80));
5096
5097 // This is typically used with width-spanning items.
5098 // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut
5099 // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)
5100 if (enable_allow_overlap)
5101 ImGui::SetNextItemAllowOverlap();
5102 ImGui::Selectable(label: "Some Selectable", selected: false);
5103 ImGui::SameLine();
5104 ImGui::SmallButton(label: "++");
5105
5106 ImGui::TreePop();
5107 }
5108}
5109
5110//-----------------------------------------------------------------------------
5111// [SECTION] DemoWindowPopups()
5112//-----------------------------------------------------------------------------
5113
5114static void DemoWindowPopups()
5115{
5116 IMGUI_DEMO_MARKER("Popups");
5117 if (!ImGui::CollapsingHeader(label: "Popups & Modal windows"))
5118 return;
5119
5120 // The properties of popups windows are:
5121 // - They block normal mouse hovering detection outside them. (*)
5122 // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
5123 // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
5124 // we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
5125 // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even
5126 // when normally blocked by a popup.
5127 // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
5128 // popups at any time.
5129
5130 // Typical use for regular windows:
5131 // 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();
5132 // Typical use for popups:
5133 // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); }
5134
5135 // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
5136 // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
5137
5138 IMGUI_DEMO_MARKER("Popups/Popups");
5139 if (ImGui::TreeNode(label: "Popups"))
5140 {
5141 ImGui::TextWrapped(
5142 fmt: "When a popup is active, it inhibits interacting with windows that are behind the popup. "
5143 "Clicking outside the popup closes it.");
5144
5145 static int selected_fish = -1;
5146 const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" };
5147 static bool toggles[] = { true, false, false, false, false };
5148
5149 // Simple selection popup (if you want to show the current selection inside the Button itself,
5150 // you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
5151 if (ImGui::Button(label: "Select.."))
5152 ImGui::OpenPopup(str_id: "my_select_popup");
5153 ImGui::SameLine();
5154 ImGui::TextUnformatted(text: selected_fish == -1 ? "<None>" : names[selected_fish]);
5155 if (ImGui::BeginPopup(str_id: "my_select_popup"))
5156 {
5157 ImGui::SeparatorText(label: "Aquarium");
5158 for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5159 if (ImGui::Selectable(label: names[i]))
5160 selected_fish = i;
5161 ImGui::EndPopup();
5162 }
5163
5164 // Showing a menu with toggles
5165 if (ImGui::Button(label: "Toggle.."))
5166 ImGui::OpenPopup(str_id: "my_toggle_popup");
5167 if (ImGui::BeginPopup(str_id: "my_toggle_popup"))
5168 {
5169 for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5170 ImGui::MenuItem(label: names[i], shortcut: "", p_selected: &toggles[i]);
5171 if (ImGui::BeginMenu(label: "Sub-menu"))
5172 {
5173 ImGui::MenuItem(label: "Click me");
5174 ImGui::EndMenu();
5175 }
5176
5177 ImGui::Separator();
5178 ImGui::Text(fmt: "Tooltip here");
5179 ImGui::SetItemTooltip("I am a tooltip over a popup");
5180
5181 if (ImGui::Button(label: "Stacked Popup"))
5182 ImGui::OpenPopup(str_id: "another popup");
5183 if (ImGui::BeginPopup(str_id: "another popup"))
5184 {
5185 for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5186 ImGui::MenuItem(label: names[i], shortcut: "", p_selected: &toggles[i]);
5187 if (ImGui::BeginMenu(label: "Sub-menu"))
5188 {
5189 ImGui::MenuItem(label: "Click me");
5190 if (ImGui::Button(label: "Stacked Popup"))
5191 ImGui::OpenPopup(str_id: "another popup");
5192 if (ImGui::BeginPopup(str_id: "another popup"))
5193 {
5194 ImGui::Text(fmt: "I am the last one here.");
5195 ImGui::EndPopup();
5196 }
5197 ImGui::EndMenu();
5198 }
5199 ImGui::EndPopup();
5200 }
5201 ImGui::EndPopup();
5202 }
5203
5204 // Call the more complete ShowExampleMenuFile which we use in various places of this demo
5205 if (ImGui::Button(label: "With a menu.."))
5206 ImGui::OpenPopup(str_id: "my_file_popup");
5207 if (ImGui::BeginPopup(str_id: "my_file_popup", flags: ImGuiWindowFlags_MenuBar))
5208 {
5209 if (ImGui::BeginMenuBar())
5210 {
5211 if (ImGui::BeginMenu(label: "File"))
5212 {
5213 ShowExampleMenuFile();
5214 ImGui::EndMenu();
5215 }
5216 if (ImGui::BeginMenu(label: "Edit"))
5217 {
5218 ImGui::MenuItem(label: "Dummy");
5219 ImGui::EndMenu();
5220 }
5221 ImGui::EndMenuBar();
5222 }
5223 ImGui::Text(fmt: "Hello from popup!");
5224 ImGui::Button(label: "This is a dummy button..");
5225 ImGui::EndPopup();
5226 }
5227
5228 ImGui::TreePop();
5229 }
5230
5231 IMGUI_DEMO_MARKER("Popups/Context menus");
5232 if (ImGui::TreeNode(label: "Context menus"))
5233 {
5234 HelpMarker(desc: "\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.");
5235
5236 // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
5237 // if (id == 0)
5238 // id = GetItemID(); // Use last item id
5239 // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
5240 // OpenPopup(id);
5241 // return BeginPopup(id);
5242 // For advanced uses you may want to replicate and customize this code.
5243 // See more details in BeginPopupContextItem().
5244
5245 // Example 1
5246 // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
5247 // and BeginPopupContextItem() will use the last item ID as the popup ID.
5248 {
5249 const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" };
5250 static int selected = -1;
5251 for (int n = 0; n < 5; n++)
5252 {
5253 if (ImGui::Selectable(label: names[n], selected: selected == n))
5254 selected = n;
5255 if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
5256 {
5257 selected = n;
5258 ImGui::Text(fmt: "This a popup for \"%s\"!", names[n]);
5259 if (ImGui::Button(label: "Close"))
5260 ImGui::CloseCurrentPopup();
5261 ImGui::EndPopup();
5262 }
5263 ImGui::SetItemTooltip("Right-click to open popup");
5264 }
5265 }
5266
5267 // Example 2
5268 // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().
5269 // Using an explicit identifier is also convenient if you want to activate the popups from different locations.
5270 {
5271 HelpMarker(desc: "Text() elements don't have stable identifiers so we need to provide one.");
5272 static float value = 0.5f;
5273 ImGui::Text(fmt: "Value = %.3f <-- (1) right-click this text", value);
5274 if (ImGui::BeginPopupContextItem(str_id: "my popup"))
5275 {
5276 if (ImGui::Selectable(label: "Set to zero")) value = 0.0f;
5277 if (ImGui::Selectable(label: "Set to PI")) value = 3.1415f;
5278 ImGui::SetNextItemWidth(-FLT_MIN);
5279 ImGui::DragFloat(label: "##Value", v: &value, v_speed: 0.1f, v_min: 0.0f, v_max: 0.0f);
5280 ImGui::EndPopup();
5281 }
5282
5283 // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
5284 // Here we make it that right-clicking this other text element opens the same popup as above.
5285 // The popup itself will be submitted by the code above.
5286 ImGui::Text(fmt: "(2) Or right-click this text");
5287 ImGui::OpenPopupOnItemClick(str_id: "my popup", popup_flags: ImGuiPopupFlags_MouseButtonRight);
5288
5289 // Back to square one: manually open the same popup.
5290 if (ImGui::Button(label: "(3) Or click this button"))
5291 ImGui::OpenPopup(str_id: "my popup");
5292 }
5293
5294 // Example 3
5295 // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),
5296 // we need to make sure your item identifier is stable.
5297 // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).
5298 {
5299 HelpMarker(desc: "Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.");
5300 static char name[32] = "Label1";
5301 char buf[64];
5302 sprintf(s: buf, format: "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
5303 ImGui::Button(label: buf);
5304 if (ImGui::BeginPopupContextItem())
5305 {
5306 ImGui::Text(fmt: "Edit name:");
5307 ImGui::InputText(label: "##edit", buf: name, IM_ARRAYSIZE(name));
5308 if (ImGui::Button(label: "Close"))
5309 ImGui::CloseCurrentPopup();
5310 ImGui::EndPopup();
5311 }
5312 ImGui::SameLine(); ImGui::Text(fmt: "(<-- right-click here)");
5313 }
5314
5315 ImGui::TreePop();
5316 }
5317
5318 IMGUI_DEMO_MARKER("Popups/Modals");
5319 if (ImGui::TreeNode(label: "Modals"))
5320 {
5321 ImGui::TextWrapped(fmt: "Modal windows are like popups but the user cannot close them by clicking outside.");
5322
5323 if (ImGui::Button(label: "Delete.."))
5324 ImGui::OpenPopup(str_id: "Delete?");
5325
5326 // Always center this window when appearing
5327 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
5328 ImGui::SetNextWindowPos(pos: center, cond: ImGuiCond_Appearing, pivot: ImVec2(0.5f, 0.5f));
5329
5330 if (ImGui::BeginPopupModal(name: "Delete?", NULL, flags: ImGuiWindowFlags_AlwaysAutoResize))
5331 {
5332 ImGui::Text(fmt: "All those beautiful files will be deleted.\nThis operation cannot be undone!");
5333 ImGui::Separator();
5334
5335 //static int unused_i = 0;
5336 //ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0");
5337
5338 static bool dont_ask_me_next_time = false;
5339 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(0, 0));
5340 ImGui::Checkbox(label: "Don't ask me next time", v: &dont_ask_me_next_time);
5341 ImGui::PopStyleVar();
5342
5343 if (ImGui::Button(label: "OK", size: ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5344 ImGui::SetItemDefaultFocus();
5345 ImGui::SameLine();
5346 if (ImGui::Button(label: "Cancel", size: ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5347 ImGui::EndPopup();
5348 }
5349
5350 if (ImGui::Button(label: "Stacked modals.."))
5351 ImGui::OpenPopup(str_id: "Stacked 1");
5352 if (ImGui::BeginPopupModal(name: "Stacked 1", NULL, flags: ImGuiWindowFlags_MenuBar))
5353 {
5354 if (ImGui::BeginMenuBar())
5355 {
5356 if (ImGui::BeginMenu(label: "File"))
5357 {
5358 if (ImGui::MenuItem(label: "Some menu item")) {}
5359 ImGui::EndMenu();
5360 }
5361 ImGui::EndMenuBar();
5362 }
5363 ImGui::Text(fmt: "Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.");
5364
5365 // Testing behavior of widgets stacking their own regular popups over the modal.
5366 static int item = 1;
5367 static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
5368 ImGui::Combo(label: "Combo", current_item: &item, items_separated_by_zeros: "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
5369 ImGui::ColorEdit4(label: "Color", col: color);
5370
5371 if (ImGui::Button(label: "Add another modal.."))
5372 ImGui::OpenPopup(str_id: "Stacked 2");
5373
5374 // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
5375 // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
5376 // of the bool actually doesn't matter here.
5377 bool unused_open = true;
5378 if (ImGui::BeginPopupModal(name: "Stacked 2", p_open: &unused_open))
5379 {
5380 ImGui::Text(fmt: "Hello from Stacked The Second!");
5381 ImGui::ColorEdit4(label: "Color", col: color); // Allow opening another nested popup
5382 if (ImGui::Button(label: "Close"))
5383 ImGui::CloseCurrentPopup();
5384 ImGui::EndPopup();
5385 }
5386
5387 if (ImGui::Button(label: "Close"))
5388 ImGui::CloseCurrentPopup();
5389 ImGui::EndPopup();
5390 }
5391
5392 ImGui::TreePop();
5393 }
5394
5395 IMGUI_DEMO_MARKER("Popups/Menus inside a regular window");
5396 if (ImGui::TreeNode(label: "Menus inside a regular window"))
5397 {
5398 ImGui::TextWrapped(fmt: "Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
5399 ImGui::Separator();
5400
5401 ImGui::MenuItem(label: "Menu item", shortcut: "CTRL+M");
5402 if (ImGui::BeginMenu(label: "Menu inside a regular window"))
5403 {
5404 ShowExampleMenuFile();
5405 ImGui::EndMenu();
5406 }
5407 ImGui::Separator();
5408 ImGui::TreePop();
5409 }
5410}
5411
5412// Dummy data structure that we use for the Table demo.
5413// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)
5414namespace
5415{
5416// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.
5417// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.
5418// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)
5419// If you don't use sorting, you will generally never care about giving column an ID!
5420enum MyItemColumnID
5421{
5422 MyItemColumnID_ID,
5423 MyItemColumnID_Name,
5424 MyItemColumnID_Action,
5425 MyItemColumnID_Quantity,
5426 MyItemColumnID_Description
5427};
5428
5429struct MyItem
5430{
5431 int ID;
5432 const char* Name;
5433 int Quantity;
5434
5435 // We have a problem which is affecting _only this demo_ and should not affect your code:
5436 // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
5437 // however qsort doesn't allow passing user data to comparing function.
5438 // As a workaround, we are storing the sort specs in a static/global for the comparing function to access.
5439 // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.
5440 // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called
5441 // very often by the sorting algorithm it would be a little wasteful.
5442 static const ImGuiTableSortSpecs* s_current_sort_specs;
5443
5444 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count)
5445 {
5446 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
5447 if (items_count > 1)
5448 qsort(base: items, nmemb: (size_t)items_count, size: sizeof(items[0]), compar: MyItem::CompareWithSortSpecs);
5449 s_current_sort_specs = NULL;
5450 }
5451
5452 // Compare function to be used by qsort()
5453 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
5454 {
5455 const MyItem* a = (const MyItem*)lhs;
5456 const MyItem* b = (const MyItem*)rhs;
5457 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
5458 {
5459 // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()
5460 // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!
5461 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
5462 int delta = 0;
5463 switch (sort_spec->ColumnUserID)
5464 {
5465 case MyItemColumnID_ID: delta = (a->ID - b->ID); break;
5466 case MyItemColumnID_Name: delta = (strcmp(s1: a->Name, s2: b->Name)); break;
5467 case MyItemColumnID_Quantity: delta = (a->Quantity - b->Quantity); break;
5468 case MyItemColumnID_Description: delta = (strcmp(s1: a->Name, s2: b->Name)); break;
5469 default: IM_ASSERT(0); break;
5470 }
5471 if (delta > 0)
5472 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
5473 if (delta < 0)
5474 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
5475 }
5476
5477 // qsort() is instable so always return a way to differenciate items.
5478 // Your own compare function may want to avoid fallback on implicit sort specs.
5479 // e.g. a Name compare if it wasn't already part of the sort specs.
5480 return (a->ID - b->ID);
5481 }
5482};
5483const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;
5484}
5485
5486// Make the UI compact because there are so many fields
5487static void PushStyleCompact()
5488{
5489 ImGuiStyle& style = ImGui::GetStyle();
5490 ImGui::PushStyleVarY(idx: ImGuiStyleVar_FramePadding, val_y: (float)(int)(style.FramePadding.y * 0.60f));
5491 ImGui::PushStyleVarY(idx: ImGuiStyleVar_ItemSpacing, val_y: (float)(int)(style.ItemSpacing.y * 0.60f));
5492}
5493
5494static void PopStyleCompact()
5495{
5496 ImGui::PopStyleVar(count: 2);
5497}
5498
5499// Show a combo box with a choice of sizing policies
5500static void EditTableSizingFlags(ImGuiTableFlags* p_flags)
5501{
5502 struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };
5503 static const EnumDesc policies[] =
5504 {
5505 { .Value: ImGuiTableFlags_None, .Name: "Default", .Tooltip: "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." },
5506 { .Value: ImGuiTableFlags_SizingFixedFit, .Name: "ImGuiTableFlags_SizingFixedFit", .Tooltip: "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." },
5507 { .Value: ImGuiTableFlags_SizingFixedSame, .Name: "ImGuiTableFlags_SizingFixedSame", .Tooltip: "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." },
5508 { .Value: ImGuiTableFlags_SizingStretchProp, .Name: "ImGuiTableFlags_SizingStretchProp", .Tooltip: "Columns default to _WidthStretch with weights proportional to their widths." },
5509 { .Value: ImGuiTableFlags_SizingStretchSame, .Name: "ImGuiTableFlags_SizingStretchSame", .Tooltip: "Columns default to _WidthStretch with same weights." }
5510 };
5511 int idx;
5512 for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++)
5513 if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))
5514 break;
5515 const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen(s: "ImGuiTableFlags") : 0) : "";
5516 if (ImGui::BeginCombo(label: "Sizing Policy", preview_value: preview_text))
5517 {
5518 for (int n = 0; n < IM_ARRAYSIZE(policies); n++)
5519 if (ImGui::Selectable(label: policies[n].Name, selected: idx == n))
5520 *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;
5521 ImGui::EndCombo();
5522 }
5523 ImGui::SameLine();
5524 ImGui::TextDisabled(fmt: "(?)");
5525 if (ImGui::BeginItemTooltip())
5526 {
5527 ImGui::PushTextWrapPos(wrap_local_pos_x: ImGui::GetFontSize() * 50.0f);
5528 for (int m = 0; m < IM_ARRAYSIZE(policies); m++)
5529 {
5530 ImGui::Separator();
5531 ImGui::Text(fmt: "%s:", policies[m].Name);
5532 ImGui::Separator();
5533 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);
5534 ImGui::TextUnformatted(text: policies[m].Tooltip);
5535 }
5536 ImGui::PopTextWrapPos();
5537 ImGui::EndTooltip();
5538 }
5539}
5540
5541static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)
5542{
5543 ImGui::CheckboxFlags(label: "_Disabled", flags: p_flags, flags_value: ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker(desc: "Master disable flag (also hide from context menu)");
5544 ImGui::CheckboxFlags(label: "_DefaultHide", flags: p_flags, flags_value: ImGuiTableColumnFlags_DefaultHide);
5545 ImGui::CheckboxFlags(label: "_DefaultSort", flags: p_flags, flags_value: ImGuiTableColumnFlags_DefaultSort);
5546 if (ImGui::CheckboxFlags(label: "_WidthStretch", flags: p_flags, flags_value: ImGuiTableColumnFlags_WidthStretch))
5547 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);
5548 if (ImGui::CheckboxFlags(label: "_WidthFixed", flags: p_flags, flags_value: ImGuiTableColumnFlags_WidthFixed))
5549 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);
5550 ImGui::CheckboxFlags(label: "_NoResize", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoResize);
5551 ImGui::CheckboxFlags(label: "_NoReorder", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoReorder);
5552 ImGui::CheckboxFlags(label: "_NoHide", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoHide);
5553 ImGui::CheckboxFlags(label: "_NoClip", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoClip);
5554 ImGui::CheckboxFlags(label: "_NoSort", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoSort);
5555 ImGui::CheckboxFlags(label: "_NoSortAscending", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoSortAscending);
5556 ImGui::CheckboxFlags(label: "_NoSortDescending", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoSortDescending);
5557 ImGui::CheckboxFlags(label: "_NoHeaderLabel", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoHeaderLabel);
5558 ImGui::CheckboxFlags(label: "_NoHeaderWidth", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoHeaderWidth);
5559 ImGui::CheckboxFlags(label: "_PreferSortAscending", flags: p_flags, flags_value: ImGuiTableColumnFlags_PreferSortAscending);
5560 ImGui::CheckboxFlags(label: "_PreferSortDescending", flags: p_flags, flags_value: ImGuiTableColumnFlags_PreferSortDescending);
5561 ImGui::CheckboxFlags(label: "_IndentEnable", flags: p_flags, flags_value: ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker(desc: "Default for column 0");
5562 ImGui::CheckboxFlags(label: "_IndentDisable", flags: p_flags, flags_value: ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker(desc: "Default for column >0");
5563 ImGui::CheckboxFlags(label: "_AngledHeader", flags: p_flags, flags_value: ImGuiTableColumnFlags_AngledHeader);
5564}
5565
5566static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)
5567{
5568 ImGui::CheckboxFlags(label: "_IsEnabled", flags: &flags, flags_value: ImGuiTableColumnFlags_IsEnabled);
5569 ImGui::CheckboxFlags(label: "_IsVisible", flags: &flags, flags_value: ImGuiTableColumnFlags_IsVisible);
5570 ImGui::CheckboxFlags(label: "_IsSorted", flags: &flags, flags_value: ImGuiTableColumnFlags_IsSorted);
5571 ImGui::CheckboxFlags(label: "_IsHovered", flags: &flags, flags_value: ImGuiTableColumnFlags_IsHovered);
5572}
5573
5574//-----------------------------------------------------------------------------
5575// [SECTION] DemoWindowTables()
5576//-----------------------------------------------------------------------------
5577
5578static void DemoWindowTables()
5579{
5580 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
5581 IMGUI_DEMO_MARKER("Tables");
5582 if (!ImGui::CollapsingHeader(label: "Tables & Columns"))
5583 return;
5584
5585 // Using those as a base value to create width/height that are factor of the size of our font
5586 const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(text: "A").x;
5587 const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
5588
5589 ImGui::PushID(str_id: "Tables");
5590
5591 int open_action = -1;
5592 if (ImGui::Button(label: "Expand all"))
5593 open_action = 1;
5594 ImGui::SameLine();
5595 if (ImGui::Button(label: "Collapse all"))
5596 open_action = 0;
5597 ImGui::SameLine();
5598
5599 // Options
5600 static bool disable_indent = false;
5601 ImGui::Checkbox(label: "Disable tree indentation", v: &disable_indent);
5602 ImGui::SameLine();
5603 HelpMarker(desc: "Disable the indenting of tree nodes so demo tables can use the full window width.");
5604 ImGui::Separator();
5605 if (disable_indent)
5606 ImGui::PushStyleVar(idx: ImGuiStyleVar_IndentSpacing, val: 0.0f);
5607
5608 // About Styling of tables
5609 // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.
5610 // There are however a few settings that a shared and part of the ImGuiStyle structure:
5611 // style.CellPadding // Padding within each cell
5612 // style.Colors[ImGuiCol_TableHeaderBg] // Table header background
5613 // style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders
5614 // style.Colors[ImGuiCol_TableBorderLight] // Table inner borders
5615 // style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even rows)
5616 // style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)
5617
5618 // Demos
5619 if (open_action != -1)
5620 ImGui::SetNextItemOpen(is_open: open_action != 0);
5621 IMGUI_DEMO_MARKER("Tables/Basic");
5622 if (ImGui::TreeNode(label: "Basic"))
5623 {
5624 // Here we will showcase three different ways to output a table.
5625 // They are very simple variations of a same thing!
5626
5627 // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
5628 // In many situations, this is the most flexible and easy to use pattern.
5629 HelpMarker(desc: "Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
5630 if (ImGui::BeginTable(str_id: "table1", columns: 3))
5631 {
5632 for (int row = 0; row < 4; row++)
5633 {
5634 ImGui::TableNextRow();
5635 for (int column = 0; column < 3; column++)
5636 {
5637 ImGui::TableSetColumnIndex(column_n: column);
5638 ImGui::Text(fmt: "Row %d Column %d", row, column);
5639 }
5640 }
5641 ImGui::EndTable();
5642 }
5643
5644 // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().
5645 // This is generally more convenient when you have code manually submitting the contents of each column.
5646 HelpMarker(desc: "Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.");
5647 if (ImGui::BeginTable(str_id: "table2", columns: 3))
5648 {
5649 for (int row = 0; row < 4; row++)
5650 {
5651 ImGui::TableNextRow();
5652 ImGui::TableNextColumn();
5653 ImGui::Text(fmt: "Row %d", row);
5654 ImGui::TableNextColumn();
5655 ImGui::Text(fmt: "Some contents");
5656 ImGui::TableNextColumn();
5657 ImGui::Text(fmt: "123.456");
5658 }
5659 ImGui::EndTable();
5660 }
5661
5662 // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),
5663 // as TableNextColumn() will automatically wrap around and create new rows as needed.
5664 // This is generally more convenient when your cells all contains the same type of data.
5665 HelpMarker(
5666 desc: "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains "
5667 "the same type of contents.\n This is also more similar to the old NextColumn() function of the "
5668 "Columns API, and provided to facilitate the Columns->Tables API transition.");
5669 if (ImGui::BeginTable(str_id: "table3", columns: 3))
5670 {
5671 for (int item = 0; item < 14; item++)
5672 {
5673 ImGui::TableNextColumn();
5674 ImGui::Text(fmt: "Item %d", item);
5675 }
5676 ImGui::EndTable();
5677 }
5678
5679 ImGui::TreePop();
5680 }
5681
5682 if (open_action != -1)
5683 ImGui::SetNextItemOpen(is_open: open_action != 0);
5684 IMGUI_DEMO_MARKER("Tables/Borders, background");
5685 if (ImGui::TreeNode(label: "Borders, background"))
5686 {
5687 // Expose a few Borders related flags interactively
5688 enum ContentsType { CT_Text, CT_FillButton };
5689 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
5690 static bool display_headers = false;
5691 static int contents_type = CT_Text;
5692
5693 PushStyleCompact();
5694 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags, flags_value: ImGuiTableFlags_RowBg);
5695 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Borders", flags: &flags, flags_value: ImGuiTableFlags_Borders);
5696 ImGui::SameLine(); HelpMarker(desc: "ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH");
5697 ImGui::Indent();
5698
5699 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersH", flags: &flags, flags_value: ImGuiTableFlags_BordersH);
5700 ImGui::Indent();
5701 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterH", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterH);
5702 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerH", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerH);
5703 ImGui::Unindent();
5704
5705 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags, flags_value: ImGuiTableFlags_BordersV);
5706 ImGui::Indent();
5707 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterV);
5708 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerV);
5709 ImGui::Unindent();
5710
5711 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuter", flags: &flags, flags_value: ImGuiTableFlags_BordersOuter);
5712 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInner", flags: &flags, flags_value: ImGuiTableFlags_BordersInner);
5713 ImGui::Unindent();
5714
5715 ImGui::AlignTextToFramePadding(); ImGui::Text(fmt: "Cell contents:");
5716 ImGui::SameLine(); ImGui::RadioButton(label: "Text", v: &contents_type, v_button: CT_Text);
5717 ImGui::SameLine(); ImGui::RadioButton(label: "FillButton", v: &contents_type, v_button: CT_FillButton);
5718 ImGui::Checkbox(label: "Display headers", v: &display_headers);
5719 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBody", flags: &flags, flags_value: ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker(desc: "Disable vertical borders in columns Body (borders will always appear in Headers");
5720 PopStyleCompact();
5721
5722 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5723 {
5724 // Display headers so we can inspect their interaction with borders
5725 // (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)
5726 if (display_headers)
5727 {
5728 ImGui::TableSetupColumn(label: "One");
5729 ImGui::TableSetupColumn(label: "Two");
5730 ImGui::TableSetupColumn(label: "Three");
5731 ImGui::TableHeadersRow();
5732 }
5733
5734 for (int row = 0; row < 5; row++)
5735 {
5736 ImGui::TableNextRow();
5737 for (int column = 0; column < 3; column++)
5738 {
5739 ImGui::TableSetColumnIndex(column_n: column);
5740 char buf[32];
5741 sprintf(s: buf, format: "Hello %d,%d", column, row);
5742 if (contents_type == CT_Text)
5743 ImGui::TextUnformatted(text: buf);
5744 else if (contents_type == CT_FillButton)
5745 ImGui::Button(label: buf, size: ImVec2(-FLT_MIN, 0.0f));
5746 }
5747 }
5748 ImGui::EndTable();
5749 }
5750 ImGui::TreePop();
5751 }
5752
5753 if (open_action != -1)
5754 ImGui::SetNextItemOpen(is_open: open_action != 0);
5755 IMGUI_DEMO_MARKER("Tables/Resizable, stretch");
5756 if (ImGui::TreeNode(label: "Resizable, stretch"))
5757 {
5758 // By default, if we don't enable ScrollX the sizing policy for each column is "Stretch"
5759 // All columns maintain a sizing weight, and they will occupy all available width.
5760 static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5761 PushStyleCompact();
5762 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
5763 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags, flags_value: ImGuiTableFlags_BordersV);
5764 ImGui::SameLine(); HelpMarker(
5765 desc: "Using the _Resizable flag automatically enables the _BordersInnerV flag as well, "
5766 "this is why the resize borders are still showing when unchecking this.");
5767 PopStyleCompact();
5768
5769 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5770 {
5771 for (int row = 0; row < 5; row++)
5772 {
5773 ImGui::TableNextRow();
5774 for (int column = 0; column < 3; column++)
5775 {
5776 ImGui::TableSetColumnIndex(column_n: column);
5777 ImGui::Text(fmt: "Hello %d,%d", column, row);
5778 }
5779 }
5780 ImGui::EndTable();
5781 }
5782 ImGui::TreePop();
5783 }
5784
5785 if (open_action != -1)
5786 ImGui::SetNextItemOpen(is_open: open_action != 0);
5787 IMGUI_DEMO_MARKER("Tables/Resizable, fixed");
5788 if (ImGui::TreeNode(label: "Resizable, fixed"))
5789 {
5790 // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)
5791 // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small)
5792 // If there is not enough available width to fit all columns, they will however be resized down.
5793 // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings
5794 HelpMarker(
5795 desc: "Using _Resizable + _SizingFixedFit flags.\n"
5796 "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n"
5797 "Double-click a column border to auto-fit the column to its contents.");
5798 PushStyleCompact();
5799 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5800 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendX);
5801 PopStyleCompact();
5802
5803 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5804 {
5805 for (int row = 0; row < 5; row++)
5806 {
5807 ImGui::TableNextRow();
5808 for (int column = 0; column < 3; column++)
5809 {
5810 ImGui::TableSetColumnIndex(column_n: column);
5811 ImGui::Text(fmt: "Hello %d,%d", column, row);
5812 }
5813 }
5814 ImGui::EndTable();
5815 }
5816 ImGui::TreePop();
5817 }
5818
5819 if (open_action != -1)
5820 ImGui::SetNextItemOpen(is_open: open_action != 0);
5821 IMGUI_DEMO_MARKER("Tables/Resizable, mixed");
5822 if (ImGui::TreeNode(label: "Resizable, mixed"))
5823 {
5824 HelpMarker(
5825 desc: "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n"
5826 "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.");
5827 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
5828
5829 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5830 {
5831 ImGui::TableSetupColumn(label: "AAA", flags: ImGuiTableColumnFlags_WidthFixed);
5832 ImGui::TableSetupColumn(label: "BBB", flags: ImGuiTableColumnFlags_WidthFixed);
5833 ImGui::TableSetupColumn(label: "CCC", flags: ImGuiTableColumnFlags_WidthStretch);
5834 ImGui::TableHeadersRow();
5835 for (int row = 0; row < 5; row++)
5836 {
5837 ImGui::TableNextRow();
5838 for (int column = 0; column < 3; column++)
5839 {
5840 ImGui::TableSetColumnIndex(column_n: column);
5841 ImGui::Text(fmt: "%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row);
5842 }
5843 }
5844 ImGui::EndTable();
5845 }
5846 if (ImGui::BeginTable(str_id: "table2", columns: 6, flags))
5847 {
5848 ImGui::TableSetupColumn(label: "AAA", flags: ImGuiTableColumnFlags_WidthFixed);
5849 ImGui::TableSetupColumn(label: "BBB", flags: ImGuiTableColumnFlags_WidthFixed);
5850 ImGui::TableSetupColumn(label: "CCC", flags: ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);
5851 ImGui::TableSetupColumn(label: "DDD", flags: ImGuiTableColumnFlags_WidthStretch);
5852 ImGui::TableSetupColumn(label: "EEE", flags: ImGuiTableColumnFlags_WidthStretch);
5853 ImGui::TableSetupColumn(label: "FFF", flags: ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);
5854 ImGui::TableHeadersRow();
5855 for (int row = 0; row < 5; row++)
5856 {
5857 ImGui::TableNextRow();
5858 for (int column = 0; column < 6; column++)
5859 {
5860 ImGui::TableSetColumnIndex(column_n: column);
5861 ImGui::Text(fmt: "%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row);
5862 }
5863 }
5864 ImGui::EndTable();
5865 }
5866 ImGui::TreePop();
5867 }
5868
5869 if (open_action != -1)
5870 ImGui::SetNextItemOpen(is_open: open_action != 0);
5871 IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers");
5872 if (ImGui::TreeNode(label: "Reorderable, hideable, with headers"))
5873 {
5874 HelpMarker(
5875 desc: "Click and drag column headers to reorder columns.\n\n"
5876 "Right-click on a header to open a context menu.");
5877 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;
5878 PushStyleCompact();
5879 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
5880 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Reorderable", flags: &flags, flags_value: ImGuiTableFlags_Reorderable);
5881 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Hideable", flags: &flags, flags_value: ImGuiTableFlags_Hideable);
5882 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBody", flags: &flags, flags_value: ImGuiTableFlags_NoBordersInBody);
5883 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBodyUntilResize", flags: &flags, flags_value: ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker(desc: "Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
5884 ImGui::CheckboxFlags(label: "ImGuiTableFlags_HighlightHoveredColumn", flags: &flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
5885 PopStyleCompact();
5886
5887 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5888 {
5889 // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.
5890 // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)
5891 ImGui::TableSetupColumn(label: "One");
5892 ImGui::TableSetupColumn(label: "Two");
5893 ImGui::TableSetupColumn(label: "Three");
5894 ImGui::TableHeadersRow();
5895 for (int row = 0; row < 6; row++)
5896 {
5897 ImGui::TableNextRow();
5898 for (int column = 0; column < 3; column++)
5899 {
5900 ImGui::TableSetColumnIndex(column_n: column);
5901 ImGui::Text(fmt: "Hello %d,%d", column, row);
5902 }
5903 }
5904 ImGui::EndTable();
5905 }
5906
5907 // Use outer_size.x == 0.0f instead of default to make the table as tight as possible
5908 // (only valid when no scrolling and no stretch column)
5909 if (ImGui::BeginTable(str_id: "table2", columns: 3, flags: flags | ImGuiTableFlags_SizingFixedFit, outer_size: ImVec2(0.0f, 0.0f)))
5910 {
5911 ImGui::TableSetupColumn(label: "One");
5912 ImGui::TableSetupColumn(label: "Two");
5913 ImGui::TableSetupColumn(label: "Three");
5914 ImGui::TableHeadersRow();
5915 for (int row = 0; row < 6; row++)
5916 {
5917 ImGui::TableNextRow();
5918 for (int column = 0; column < 3; column++)
5919 {
5920 ImGui::TableSetColumnIndex(column_n: column);
5921 ImGui::Text(fmt: "Fixed %d,%d", column, row);
5922 }
5923 }
5924 ImGui::EndTable();
5925 }
5926 ImGui::TreePop();
5927 }
5928
5929 if (open_action != -1)
5930 ImGui::SetNextItemOpen(is_open: open_action != 0);
5931 IMGUI_DEMO_MARKER("Tables/Padding");
5932 if (ImGui::TreeNode(label: "Padding"))
5933 {
5934 // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.
5935 // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.
5936 HelpMarker(
5937 desc: "We often want outer padding activated when any using features which makes the edges of a column visible:\n"
5938 "e.g.:\n"
5939 "- BorderOuterV\n"
5940 "- any form of row selection\n"
5941 "Because of this, activating BorderOuterV sets the default to PadOuterX. "
5942 "Using PadOuterX or NoPadOuterX you can override the default.\n\n"
5943 "Actual padding values are using style.CellPadding.\n\n"
5944 "In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.");
5945
5946 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;
5947 PushStyleCompact();
5948 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PadOuterX", flags: &flags1, flags_value: ImGuiTableFlags_PadOuterX);
5949 ImGui::SameLine(); HelpMarker(desc: "Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)");
5950 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadOuterX", flags: &flags1, flags_value: ImGuiTableFlags_NoPadOuterX);
5951 ImGui::SameLine(); HelpMarker(desc: "Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)");
5952 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadInnerX", flags: &flags1, flags_value: ImGuiTableFlags_NoPadInnerX);
5953 ImGui::SameLine(); HelpMarker(desc: "Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)");
5954 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags1, flags_value: ImGuiTableFlags_BordersOuterV);
5955 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags1, flags_value: ImGuiTableFlags_BordersInnerV);
5956 static bool show_headers = false;
5957 ImGui::Checkbox(label: "show_headers", v: &show_headers);
5958 PopStyleCompact();
5959
5960 if (ImGui::BeginTable(str_id: "table_padding", columns: 3, flags: flags1))
5961 {
5962 if (show_headers)
5963 {
5964 ImGui::TableSetupColumn(label: "One");
5965 ImGui::TableSetupColumn(label: "Two");
5966 ImGui::TableSetupColumn(label: "Three");
5967 ImGui::TableHeadersRow();
5968 }
5969
5970 for (int row = 0; row < 5; row++)
5971 {
5972 ImGui::TableNextRow();
5973 for (int column = 0; column < 3; column++)
5974 {
5975 ImGui::TableSetColumnIndex(column_n: column);
5976 if (row == 0)
5977 {
5978 ImGui::Text(fmt: "Avail %.2f", ImGui::GetContentRegionAvail().x);
5979 }
5980 else
5981 {
5982 char buf[32];
5983 sprintf(s: buf, format: "Hello %d,%d", column, row);
5984 ImGui::Button(label: buf, size: ImVec2(-FLT_MIN, 0.0f));
5985 }
5986 //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)
5987 // ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));
5988 }
5989 }
5990 ImGui::EndTable();
5991 }
5992
5993 // Second example: set style.CellPadding to (0.0) or a custom value.
5994 // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...
5995 HelpMarker(desc: "Setting style.CellPadding to (0,0) or a custom value.");
5996 static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
5997 static ImVec2 cell_padding(0.0f, 0.0f);
5998 static bool show_widget_frame_bg = true;
5999
6000 PushStyleCompact();
6001 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Borders", flags: &flags2, flags_value: ImGuiTableFlags_Borders);
6002 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersH", flags: &flags2, flags_value: ImGuiTableFlags_BordersH);
6003 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags2, flags_value: ImGuiTableFlags_BordersV);
6004 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInner", flags: &flags2, flags_value: ImGuiTableFlags_BordersInner);
6005 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuter", flags: &flags2, flags_value: ImGuiTableFlags_BordersOuter);
6006 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags2, flags_value: ImGuiTableFlags_RowBg);
6007 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags2, flags_value: ImGuiTableFlags_Resizable);
6008 ImGui::Checkbox(label: "show_widget_frame_bg", v: &show_widget_frame_bg);
6009 ImGui::SliderFloat2(label: "CellPadding", v: &cell_padding.x, v_min: 0.0f, v_max: 10.0f, format: "%.0f");
6010 PopStyleCompact();
6011
6012 ImGui::PushStyleVar(idx: ImGuiStyleVar_CellPadding, val: cell_padding);
6013 if (ImGui::BeginTable(str_id: "table_padding_2", columns: 3, flags: flags2))
6014 {
6015 static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells
6016 static bool init = true;
6017 if (!show_widget_frame_bg)
6018 ImGui::PushStyleColor(idx: ImGuiCol_FrameBg, col: 0);
6019 for (int cell = 0; cell < 3 * 5; cell++)
6020 {
6021 ImGui::TableNextColumn();
6022 if (init)
6023 strcpy(dest: text_bufs[cell], src: "edit me");
6024 ImGui::SetNextItemWidth(-FLT_MIN);
6025 ImGui::PushID(int_id: cell);
6026 ImGui::InputText(label: "##cell", buf: text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell]));
6027 ImGui::PopID();
6028 }
6029 if (!show_widget_frame_bg)
6030 ImGui::PopStyleColor();
6031 init = false;
6032 ImGui::EndTable();
6033 }
6034 ImGui::PopStyleVar();
6035
6036 ImGui::TreePop();
6037 }
6038
6039 if (open_action != -1)
6040 ImGui::SetNextItemOpen(is_open: open_action != 0);
6041 IMGUI_DEMO_MARKER("Tables/Explicit widths");
6042 if (ImGui::TreeNode(label: "Sizing policies"))
6043 {
6044 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6045 PushStyleCompact();
6046 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags1, flags_value: ImGuiTableFlags_Resizable);
6047 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags1, flags_value: ImGuiTableFlags_NoHostExtendX);
6048 PopStyleCompact();
6049
6050 static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame };
6051 for (int table_n = 0; table_n < 4; table_n++)
6052 {
6053 ImGui::PushID(int_id: table_n);
6054 ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);
6055 EditTableSizingFlags(p_flags: &sizing_policy_flags[table_n]);
6056
6057 // To make it easier to understand the different sizing policy,
6058 // For each policy: we display one table where the columns have equal contents width,
6059 // and one where the columns have different contents width.
6060 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags: sizing_policy_flags[table_n] | flags1))
6061 {
6062 for (int row = 0; row < 3; row++)
6063 {
6064 ImGui::TableNextRow();
6065 ImGui::TableNextColumn(); ImGui::Text(fmt: "Oh dear");
6066 ImGui::TableNextColumn(); ImGui::Text(fmt: "Oh dear");
6067 ImGui::TableNextColumn(); ImGui::Text(fmt: "Oh dear");
6068 }
6069 ImGui::EndTable();
6070 }
6071 if (ImGui::BeginTable(str_id: "table2", columns: 3, flags: sizing_policy_flags[table_n] | flags1))
6072 {
6073 for (int row = 0; row < 3; row++)
6074 {
6075 ImGui::TableNextRow();
6076 ImGui::TableNextColumn(); ImGui::Text(fmt: "AAAA");
6077 ImGui::TableNextColumn(); ImGui::Text(fmt: "BBBBBBBB");
6078 ImGui::TableNextColumn(); ImGui::Text(fmt: "CCCCCCCCCCCC");
6079 }
6080 ImGui::EndTable();
6081 }
6082 ImGui::PopID();
6083 }
6084
6085 ImGui::Spacing();
6086 ImGui::TextUnformatted(text: "Advanced");
6087 ImGui::SameLine();
6088 HelpMarker(
6089 desc: "This section allows you to interact and see the effect of various sizing policies "
6090 "depending on whether Scroll is enabled and the contents of your columns.");
6091
6092 enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };
6093 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
6094 static int contents_type = CT_ShowWidth;
6095 static int column_count = 3;
6096
6097 PushStyleCompact();
6098 ImGui::PushID(str_id: "Advanced");
6099 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 30);
6100 EditTableSizingFlags(p_flags: &flags);
6101 ImGui::Combo(label: "Contents", current_item: &contents_type, items_separated_by_zeros: "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0");
6102 if (contents_type == CT_FillButton)
6103 {
6104 ImGui::SameLine();
6105 HelpMarker(
6106 desc: "Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop "
6107 "where contents width can feed into auto-column width can feed into contents width.");
6108 }
6109 ImGui::DragInt(label: "Columns", v: &column_count, v_speed: 0.1f, v_min: 1, v_max: 64, format: "%d", flags: ImGuiSliderFlags_AlwaysClamp);
6110 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
6111 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PreciseWidths", flags: &flags, flags_value: ImGuiTableFlags_PreciseWidths);
6112 ImGui::SameLine(); HelpMarker(desc: "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.");
6113 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags, flags_value: ImGuiTableFlags_ScrollX);
6114 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
6115 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoClip", flags: &flags, flags_value: ImGuiTableFlags_NoClip);
6116 ImGui::PopItemWidth();
6117 ImGui::PopID();
6118 PopStyleCompact();
6119
6120 if (ImGui::BeginTable(str_id: "table2", columns: column_count, flags, outer_size: ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))
6121 {
6122 for (int cell = 0; cell < 10 * column_count; cell++)
6123 {
6124 ImGui::TableNextColumn();
6125 int column = ImGui::TableGetColumnIndex();
6126 int row = ImGui::TableGetRowIndex();
6127
6128 ImGui::PushID(int_id: cell);
6129 char label[32];
6130 static char text_buf[32] = "";
6131 sprintf(s: label, format: "Hello %d,%d", column, row);
6132 switch (contents_type)
6133 {
6134 case CT_ShortText: ImGui::TextUnformatted(text: label); break;
6135 case CT_LongText: ImGui::Text(fmt: "Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break;
6136 case CT_ShowWidth: ImGui::Text(fmt: "W: %.1f", ImGui::GetContentRegionAvail().x); break;
6137 case CT_Button: ImGui::Button(label); break;
6138 case CT_FillButton: ImGui::Button(label, size: ImVec2(-FLT_MIN, 0.0f)); break;
6139 case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText(label: "##", buf: text_buf, IM_ARRAYSIZE(text_buf)); break;
6140 }
6141 ImGui::PopID();
6142 }
6143 ImGui::EndTable();
6144 }
6145 ImGui::TreePop();
6146 }
6147
6148 if (open_action != -1)
6149 ImGui::SetNextItemOpen(is_open: open_action != 0);
6150 IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping");
6151 if (ImGui::TreeNode(label: "Vertical scrolling, with clipping"))
6152 {
6153 HelpMarker(
6154 desc: "Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n"
6155 "We also demonstrate using ImGuiListClipper to virtualize the submission of many items.");
6156 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6157
6158 PushStyleCompact();
6159 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
6160 PopStyleCompact();
6161
6162 // When using ScrollX or ScrollY we need to specify a size for our table container!
6163 // Otherwise by default the table will fit all available space, like a BeginChild() call.
6164 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6165 if (ImGui::BeginTable(str_id: "table_scrolly", columns: 3, flags, outer_size))
6166 {
6167 ImGui::TableSetupScrollFreeze(cols: 0, rows: 1); // Make top row always visible
6168 ImGui::TableSetupColumn(label: "One", flags: ImGuiTableColumnFlags_None);
6169 ImGui::TableSetupColumn(label: "Two", flags: ImGuiTableColumnFlags_None);
6170 ImGui::TableSetupColumn(label: "Three", flags: ImGuiTableColumnFlags_None);
6171 ImGui::TableHeadersRow();
6172
6173 // Demonstrate using clipper for large vertical lists
6174 ImGuiListClipper clipper;
6175 clipper.Begin(items_count: 1000);
6176 while (clipper.Step())
6177 {
6178 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)
6179 {
6180 ImGui::TableNextRow();
6181 for (int column = 0; column < 3; column++)
6182 {
6183 ImGui::TableSetColumnIndex(column_n: column);
6184 ImGui::Text(fmt: "Hello %d,%d", column, row);
6185 }
6186 }
6187 }
6188 ImGui::EndTable();
6189 }
6190 ImGui::TreePop();
6191 }
6192
6193 if (open_action != -1)
6194 ImGui::SetNextItemOpen(is_open: open_action != 0);
6195 IMGUI_DEMO_MARKER("Tables/Horizontal scrolling");
6196 if (ImGui::TreeNode(label: "Horizontal scrolling"))
6197 {
6198 HelpMarker(
6199 desc: "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, "
6200 "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n"
6201 "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, "
6202 "because the container window won't automatically extend vertically to fix contents "
6203 "(this may be improved in future versions).");
6204 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6205 static int freeze_cols = 1;
6206 static int freeze_rows = 1;
6207
6208 PushStyleCompact();
6209 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
6210 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags, flags_value: ImGuiTableFlags_ScrollX);
6211 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
6212 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6213 ImGui::DragInt(label: "freeze_cols", v: &freeze_cols, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
6214 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6215 ImGui::DragInt(label: "freeze_rows", v: &freeze_rows, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
6216 PopStyleCompact();
6217
6218 // When using ScrollX or ScrollY we need to specify a size for our table container!
6219 // Otherwise by default the table will fit all available space, like a BeginChild() call.
6220 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6221 if (ImGui::BeginTable(str_id: "table_scrollx", columns: 7, flags, outer_size))
6222 {
6223 ImGui::TableSetupScrollFreeze(cols: freeze_cols, rows: freeze_rows);
6224 ImGui::TableSetupColumn(label: "Line #", flags: ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()
6225 ImGui::TableSetupColumn(label: "One");
6226 ImGui::TableSetupColumn(label: "Two");
6227 ImGui::TableSetupColumn(label: "Three");
6228 ImGui::TableSetupColumn(label: "Four");
6229 ImGui::TableSetupColumn(label: "Five");
6230 ImGui::TableSetupColumn(label: "Six");
6231 ImGui::TableHeadersRow();
6232 for (int row = 0; row < 20; row++)
6233 {
6234 ImGui::TableNextRow();
6235 for (int column = 0; column < 7; column++)
6236 {
6237 // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.
6238 // Because here we know that:
6239 // - A) all our columns are contributing the same to row height
6240 // - B) column 0 is always visible,
6241 // We only always submit this one column and can skip others.
6242 // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().
6243 if (!ImGui::TableSetColumnIndex(column_n: column) && column > 0)
6244 continue;
6245 if (column == 0)
6246 ImGui::Text(fmt: "Line %d", row);
6247 else
6248 ImGui::Text(fmt: "Hello world %d,%d", column, row);
6249 }
6250 }
6251 ImGui::EndTable();
6252 }
6253
6254 ImGui::Spacing();
6255 ImGui::TextUnformatted(text: "Stretch + ScrollX");
6256 ImGui::SameLine();
6257 HelpMarker(
6258 desc: "Showcase using Stretch columns + ScrollX together: "
6259 "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n"
6260 "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns "
6261 "along with ScrollX doesn't make sense.");
6262 static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6263 static float inner_width = 1000.0f;
6264 PushStyleCompact();
6265 ImGui::PushID(str_id: "flags3");
6266 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 30);
6267 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags2, flags_value: ImGuiTableFlags_ScrollX);
6268 ImGui::DragFloat(label: "inner_width", v: &inner_width, v_speed: 1.0f, v_min: 0.0f, FLT_MAX, format: "%.1f");
6269 ImGui::PopItemWidth();
6270 ImGui::PopID();
6271 PopStyleCompact();
6272 if (ImGui::BeginTable(str_id: "table2", columns: 7, flags: flags2, outer_size, inner_width))
6273 {
6274 for (int cell = 0; cell < 20 * 7; cell++)
6275 {
6276 ImGui::TableNextColumn();
6277 ImGui::Text(fmt: "Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());
6278 }
6279 ImGui::EndTable();
6280 }
6281 ImGui::TreePop();
6282 }
6283
6284 if (open_action != -1)
6285 ImGui::SetNextItemOpen(is_open: open_action != 0);
6286 IMGUI_DEMO_MARKER("Tables/Columns flags");
6287 if (ImGui::TreeNode(label: "Columns flags"))
6288 {
6289 // Create a first table just to show all the options/flags we want to make visible in our example!
6290 const int column_count = 3;
6291 const char* column_names[column_count] = { "One", "Two", "Three" };
6292 static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide };
6293 static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags()
6294
6295 if (ImGui::BeginTable(str_id: "table_columns_flags_checkboxes", columns: column_count, flags: ImGuiTableFlags_None))
6296 {
6297 PushStyleCompact();
6298 for (int column = 0; column < column_count; column++)
6299 {
6300 ImGui::TableNextColumn();
6301 ImGui::PushID(int_id: column);
6302 ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns
6303 ImGui::Text(fmt: "'%s'", column_names[column]);
6304 ImGui::Spacing();
6305 ImGui::Text(fmt: "Input flags:");
6306 EditTableColumnsFlags(p_flags: &column_flags[column]);
6307 ImGui::Spacing();
6308 ImGui::Text(fmt: "Output flags:");
6309 ImGui::BeginDisabled();
6310 ShowTableColumnsStatusFlags(flags: column_flags_out[column]);
6311 ImGui::EndDisabled();
6312 ImGui::PopID();
6313 }
6314 PopStyleCompact();
6315 ImGui::EndTable();
6316 }
6317
6318 // Create the real table we care about for the example!
6319 // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above,
6320 // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible
6321 // + resizing the parent window down).
6322 const ImGuiTableFlags flags
6323 = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
6324 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV
6325 | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;
6326 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);
6327 if (ImGui::BeginTable(str_id: "table_columns_flags", columns: column_count, flags, outer_size))
6328 {
6329 bool has_angled_header = false;
6330 for (int column = 0; column < column_count; column++)
6331 {
6332 has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;
6333 ImGui::TableSetupColumn(label: column_names[column], flags: column_flags[column]);
6334 }
6335 if (has_angled_header)
6336 ImGui::TableAngledHeadersRow();
6337 ImGui::TableHeadersRow();
6338 for (int column = 0; column < column_count; column++)
6339 column_flags_out[column] = ImGui::TableGetColumnFlags(column_n: column);
6340 float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);
6341 for (int row = 0; row < 8; row++)
6342 {
6343 // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.
6344 ImGui::Indent(indent_w: indent_step);
6345 ImGui::TableNextRow();
6346 for (int column = 0; column < column_count; column++)
6347 {
6348 ImGui::TableSetColumnIndex(column_n: column);
6349 ImGui::Text(fmt: "%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column_n: column));
6350 }
6351 }
6352 ImGui::Unindent(indent_w: indent_step * 8.0f);
6353
6354 ImGui::EndTable();
6355 }
6356 ImGui::TreePop();
6357 }
6358
6359 if (open_action != -1)
6360 ImGui::SetNextItemOpen(is_open: open_action != 0);
6361 IMGUI_DEMO_MARKER("Tables/Columns widths");
6362 if (ImGui::TreeNode(label: "Columns widths"))
6363 {
6364 HelpMarker(desc: "Using TableSetupColumn() to setup default width.");
6365
6366 static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;
6367 PushStyleCompact();
6368 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags1, flags_value: ImGuiTableFlags_Resizable);
6369 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBodyUntilResize", flags: &flags1, flags_value: ImGuiTableFlags_NoBordersInBodyUntilResize);
6370 PopStyleCompact();
6371 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags: flags1))
6372 {
6373 // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.
6374 ImGui::TableSetupColumn(label: "one", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 100.0f); // Default to 100.0f
6375 ImGui::TableSetupColumn(label: "two", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 200.0f); // Default to 200.0f
6376 ImGui::TableSetupColumn(label: "three", flags: ImGuiTableColumnFlags_WidthFixed); // Default to auto
6377 ImGui::TableHeadersRow();
6378 for (int row = 0; row < 4; row++)
6379 {
6380 ImGui::TableNextRow();
6381 for (int column = 0; column < 3; column++)
6382 {
6383 ImGui::TableSetColumnIndex(column_n: column);
6384 if (row == 0)
6385 ImGui::Text(fmt: "(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6386 else
6387 ImGui::Text(fmt: "Hello %d,%d", column, row);
6388 }
6389 }
6390 ImGui::EndTable();
6391 }
6392
6393 HelpMarker(
6394 desc: "Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, "
6395 "fixed columns with set width may still be shrunk down if there's not enough space in the host.");
6396
6397 static ImGuiTableFlags flags2 = ImGuiTableFlags_None;
6398 PushStyleCompact();
6399 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoKeepColumnsVisible", flags: &flags2, flags_value: ImGuiTableFlags_NoKeepColumnsVisible);
6400 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags2, flags_value: ImGuiTableFlags_BordersInnerV);
6401 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags2, flags_value: ImGuiTableFlags_BordersOuterV);
6402 PopStyleCompact();
6403 if (ImGui::BeginTable(str_id: "table2", columns: 4, flags: flags2))
6404 {
6405 // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns
6406 // will default to ImGuiTableColumnFlags_WidthFixed.
6407 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 100.0f);
6408 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 15.0f);
6409 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 30.0f);
6410 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 15.0f);
6411 for (int row = 0; row < 5; row++)
6412 {
6413 ImGui::TableNextRow();
6414 for (int column = 0; column < 4; column++)
6415 {
6416 ImGui::TableSetColumnIndex(column_n: column);
6417 if (row == 0)
6418 ImGui::Text(fmt: "(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6419 else
6420 ImGui::Text(fmt: "Hello %d,%d", column, row);
6421 }
6422 }
6423 ImGui::EndTable();
6424 }
6425 ImGui::TreePop();
6426 }
6427
6428 if (open_action != -1)
6429 ImGui::SetNextItemOpen(is_open: open_action != 0);
6430 IMGUI_DEMO_MARKER("Tables/Nested tables");
6431 if (ImGui::TreeNode(label: "Nested tables"))
6432 {
6433 HelpMarker(desc: "This demonstrates embedding a table into another table cell.");
6434
6435 if (ImGui::BeginTable(str_id: "table_nested1", columns: 2, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6436 {
6437 ImGui::TableSetupColumn(label: "A0");
6438 ImGui::TableSetupColumn(label: "A1");
6439 ImGui::TableHeadersRow();
6440
6441 ImGui::TableNextColumn();
6442 ImGui::Text(fmt: "A0 Row 0");
6443 {
6444 float rows_height = TEXT_BASE_HEIGHT * 2;
6445 if (ImGui::BeginTable(str_id: "table_nested2", columns: 2, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6446 {
6447 ImGui::TableSetupColumn(label: "B0");
6448 ImGui::TableSetupColumn(label: "B1");
6449 ImGui::TableHeadersRow();
6450
6451 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height: rows_height);
6452 ImGui::TableNextColumn();
6453 ImGui::Text(fmt: "B0 Row 0");
6454 ImGui::TableNextColumn();
6455 ImGui::Text(fmt: "B1 Row 0");
6456 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height: rows_height);
6457 ImGui::TableNextColumn();
6458 ImGui::Text(fmt: "B0 Row 1");
6459 ImGui::TableNextColumn();
6460 ImGui::Text(fmt: "B1 Row 1");
6461
6462 ImGui::EndTable();
6463 }
6464 }
6465 ImGui::TableNextColumn(); ImGui::Text(fmt: "A1 Row 0");
6466 ImGui::TableNextColumn(); ImGui::Text(fmt: "A0 Row 1");
6467 ImGui::TableNextColumn(); ImGui::Text(fmt: "A1 Row 1");
6468 ImGui::EndTable();
6469 }
6470 ImGui::TreePop();
6471 }
6472
6473 if (open_action != -1)
6474 ImGui::SetNextItemOpen(is_open: open_action != 0);
6475 IMGUI_DEMO_MARKER("Tables/Row height");
6476 if (ImGui::TreeNode(label: "Row height"))
6477 {
6478 HelpMarker(
6479 desc: "You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, "
6480 "so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n"
6481 "We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.");
6482 if (ImGui::BeginTable(str_id: "table_row_height", columns: 1, flags: ImGuiTableFlags_Borders))
6483 {
6484 for (int row = 0; row < 8; row++)
6485 {
6486 float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row);
6487 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height);
6488 ImGui::TableNextColumn();
6489 ImGui::Text(fmt: "min_row_height = %.2f", min_row_height);
6490 }
6491 ImGui::EndTable();
6492 }
6493
6494 HelpMarker(
6495 desc: "Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n"
6496 "Please note that Tables Row Height is not the same thing as Current Line Height, "
6497 "as a table cell may contains multiple lines.");
6498 if (ImGui::BeginTable(str_id: "table_share_lineheight", columns: 2, flags: ImGuiTableFlags_Borders))
6499 {
6500 ImGui::TableNextRow();
6501 ImGui::TableNextColumn();
6502 ImGui::ColorButton(desc_id: "##1", col: ImVec4(0.13f, 0.26f, 0.40f, 1.0f), flags: ImGuiColorEditFlags_None, size: ImVec2(40, 40));
6503 ImGui::TableNextColumn();
6504 ImGui::Text(fmt: "Line 1");
6505 ImGui::Text(fmt: "Line 2");
6506
6507 ImGui::TableNextRow();
6508 ImGui::TableNextColumn();
6509 ImGui::ColorButton(desc_id: "##2", col: ImVec4(0.13f, 0.26f, 0.40f, 1.0f), flags: ImGuiColorEditFlags_None, size: ImVec2(40, 40));
6510 ImGui::TableNextColumn();
6511 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: 0.0f); // Reuse line height from previous column
6512 ImGui::Text(fmt: "Line 1, with SameLine(0,0)");
6513 ImGui::Text(fmt: "Line 2");
6514
6515 ImGui::EndTable();
6516 }
6517
6518 HelpMarker(desc: "Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.");
6519 if (ImGui::BeginTable(str_id: "table_changing_cellpadding_y", columns: 1, flags: ImGuiTableFlags_Borders))
6520 {
6521 ImGuiStyle& style = ImGui::GetStyle();
6522 for (int row = 0; row < 8; row++)
6523 {
6524 if ((row % 3) == 2)
6525 ImGui::PushStyleVarY(idx: ImGuiStyleVar_CellPadding, val_y: 20.0f);
6526 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None);
6527 ImGui::TableNextColumn();
6528 ImGui::Text(fmt: "CellPadding.y = %.2f", style.CellPadding.y);
6529 if ((row % 3) == 2)
6530 ImGui::PopStyleVar();
6531 }
6532 ImGui::EndTable();
6533 }
6534
6535 ImGui::TreePop();
6536 }
6537
6538 if (open_action != -1)
6539 ImGui::SetNextItemOpen(is_open: open_action != 0);
6540 IMGUI_DEMO_MARKER("Tables/Outer size");
6541 if (ImGui::TreeNode(label: "Outer size"))
6542 {
6543 // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY
6544 // Important to that note how the two flags have slightly different behaviors!
6545 ImGui::Text(fmt: "Using NoHostExtendX and NoHostExtendY:");
6546 PushStyleCompact();
6547 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
6548 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendX);
6549 ImGui::SameLine(); HelpMarker(desc: "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.");
6550 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendY", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendY);
6551 ImGui::SameLine(); HelpMarker(desc: "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.");
6552 PopStyleCompact();
6553
6554 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
6555 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags, outer_size))
6556 {
6557 for (int row = 0; row < 10; row++)
6558 {
6559 ImGui::TableNextRow();
6560 for (int column = 0; column < 3; column++)
6561 {
6562 ImGui::TableNextColumn();
6563 ImGui::Text(fmt: "Cell %d,%d", column, row);
6564 }
6565 }
6566 ImGui::EndTable();
6567 }
6568 ImGui::SameLine();
6569 ImGui::Text(fmt: "Hello!");
6570
6571 ImGui::Spacing();
6572
6573 ImGui::Text(fmt: "Using explicit size:");
6574 if (ImGui::BeginTable(str_id: "table2", columns: 3, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, outer_size: ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6575 {
6576 for (int row = 0; row < 5; row++)
6577 {
6578 ImGui::TableNextRow();
6579 for (int column = 0; column < 3; column++)
6580 {
6581 ImGui::TableNextColumn();
6582 ImGui::Text(fmt: "Cell %d,%d", column, row);
6583 }
6584 }
6585 ImGui::EndTable();
6586 }
6587 ImGui::SameLine();
6588 if (ImGui::BeginTable(str_id: "table3", columns: 3, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, outer_size: ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6589 {
6590 for (int row = 0; row < 3; row++)
6591 {
6592 ImGui::TableNextRow(row_flags: 0, min_row_height: TEXT_BASE_HEIGHT * 1.5f);
6593 for (int column = 0; column < 3; column++)
6594 {
6595 ImGui::TableNextColumn();
6596 ImGui::Text(fmt: "Cell %d,%d", column, row);
6597 }
6598 }
6599 ImGui::EndTable();
6600 }
6601
6602 ImGui::TreePop();
6603 }
6604
6605 if (open_action != -1)
6606 ImGui::SetNextItemOpen(is_open: open_action != 0);
6607 IMGUI_DEMO_MARKER("Tables/Background color");
6608 if (ImGui::TreeNode(label: "Background color"))
6609 {
6610 static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;
6611 static int row_bg_type = 1;
6612 static int row_bg_target = 1;
6613 static int cell_bg_type = 1;
6614
6615 PushStyleCompact();
6616 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Borders", flags: &flags, flags_value: ImGuiTableFlags_Borders);
6617 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags, flags_value: ImGuiTableFlags_RowBg);
6618 ImGui::SameLine(); HelpMarker(desc: "ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.");
6619 ImGui::Combo(label: "row bg type", current_item: (int*)&row_bg_type, items_separated_by_zeros: "None\0Red\0Gradient\0");
6620 ImGui::Combo(label: "row bg target", current_item: (int*)&row_bg_target, items_separated_by_zeros: "RowBg0\0RowBg1\0"); ImGui::SameLine(); HelpMarker(desc: "Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them.");
6621 ImGui::Combo(label: "cell bg type", current_item: (int*)&cell_bg_type, items_separated_by_zeros: "None\0Blue\0"); ImGui::SameLine(); HelpMarker(desc: "We are colorizing cells to B1->C2 here.");
6622 IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);
6623 IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);
6624 IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);
6625 PopStyleCompact();
6626
6627 if (ImGui::BeginTable(str_id: "table1", columns: 5, flags))
6628 {
6629 for (int row = 0; row < 6; row++)
6630 {
6631 ImGui::TableNextRow();
6632
6633 // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'
6634 // 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.
6635 if (row_bg_type != 0)
6636 {
6637 ImU32 row_bg_color = ImGui::GetColorU32(col: 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?
6638 ImGui::TableSetBgColor(target: ImGuiTableBgTarget_RowBg0 + row_bg_target, color: row_bg_color);
6639 }
6640
6641 // Fill cells
6642 for (int column = 0; column < 5; column++)
6643 {
6644 ImGui::TableSetColumnIndex(column_n: column);
6645 ImGui::Text(fmt: "%c%c", 'A' + row, '0' + column);
6646
6647 // Change background of Cells B1->C2
6648 // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'
6649 // (the CellBg color will be blended over the RowBg and ColumnBg colors)
6650 // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.
6651 if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)
6652 {
6653 ImU32 cell_bg_color = ImGui::GetColorU32(col: ImVec4(0.3f, 0.3f, 0.7f, 0.65f));
6654 ImGui::TableSetBgColor(target: ImGuiTableBgTarget_CellBg, color: cell_bg_color);
6655 }
6656 }
6657 }
6658 ImGui::EndTable();
6659 }
6660 ImGui::TreePop();
6661 }
6662
6663 if (open_action != -1)
6664 ImGui::SetNextItemOpen(is_open: open_action != 0);
6665 IMGUI_DEMO_MARKER("Tables/Tree view");
6666 if (ImGui::TreeNode(label: "Tree view"))
6667 {
6668 static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
6669
6670 static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns;
6671 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanFullWidth", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_SpanFullWidth);
6672 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanLabelWidth", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_SpanLabelWidth);
6673 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanAllColumns", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_SpanAllColumns);
6674 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_LabelSpanAllColumns", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_LabelSpanAllColumns);
6675 ImGui::SameLine(); HelpMarker(desc: "Useful if you know that you aren't displaying contents in other columns");
6676
6677 HelpMarker(desc: "See \"Columns flags\" section to configure how indentation is applied to individual columns.");
6678 if (ImGui::BeginTable(str_id: "3ways", columns: 3, flags: table_flags))
6679 {
6680 // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
6681 ImGui::TableSetupColumn(label: "Name", flags: ImGuiTableColumnFlags_NoHide);
6682 ImGui::TableSetupColumn(label: "Size", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 12.0f);
6683 ImGui::TableSetupColumn(label: "Type", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 18.0f);
6684 ImGui::TableHeadersRow();
6685
6686 // Simple storage to output a dummy file-system.
6687 struct MyTreeNode
6688 {
6689 const char* Name;
6690 const char* Type;
6691 int Size;
6692 int ChildIdx;
6693 int ChildCount;
6694 static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
6695 {
6696 ImGui::TableNextRow();
6697 ImGui::TableNextColumn();
6698 const bool is_folder = (node->ChildCount > 0);
6699
6700 ImGuiTreeNodeFlags node_flags = tree_node_flags_base;
6701 if (node != &all_nodes[0])
6702 node_flags &= ~ImGuiTreeNodeFlags_LabelSpanAllColumns; // Only demonstrate this on the root node.
6703
6704 if (is_folder)
6705 {
6706 bool open = ImGui::TreeNodeEx(label: node->Name, flags: node_flags);
6707 if ((node_flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) == 0)
6708 {
6709 ImGui::TableNextColumn();
6710 ImGui::TextDisabled(fmt: "--");
6711 ImGui::TableNextColumn();
6712 ImGui::TextUnformatted(text: node->Type);
6713 }
6714 if (open)
6715 {
6716 for (int child_n = 0; child_n < node->ChildCount; child_n++)
6717 DisplayNode(node: &all_nodes[node->ChildIdx + child_n], all_nodes);
6718 ImGui::TreePop();
6719 }
6720 }
6721 else
6722 {
6723 ImGui::TreeNodeEx(label: node->Name, flags: node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen);
6724 ImGui::TableNextColumn();
6725 ImGui::Text(fmt: "%d", node->Size);
6726 ImGui::TableNextColumn();
6727 ImGui::TextUnformatted(text: node->Type);
6728 }
6729 }
6730 };
6731 static const MyTreeNode nodes[] =
6732 {
6733 { .Name: "Root with Long Name", .Type: "Folder", .Size: -1, .ChildIdx: 1, .ChildCount: 3 }, // 0
6734 { .Name: "Music", .Type: "Folder", .Size: -1, .ChildIdx: 4, .ChildCount: 2 }, // 1
6735 { .Name: "Textures", .Type: "Folder", .Size: -1, .ChildIdx: 6, .ChildCount: 3 }, // 2
6736 { .Name: "desktop.ini", .Type: "System file", .Size: 1024, .ChildIdx: -1,.ChildCount: -1 }, // 3
6737 { .Name: "File1_a.wav", .Type: "Audio file", .Size: 123000, .ChildIdx: -1,.ChildCount: -1 }, // 4
6738 { .Name: "File1_b.wav", .Type: "Audio file", .Size: 456000, .ChildIdx: -1,.ChildCount: -1 }, // 5
6739 { .Name: "Image001.png", .Type: "Image file", .Size: 203128, .ChildIdx: -1,.ChildCount: -1 }, // 6
6740 { .Name: "Copy of Image001.png", .Type: "Image file", .Size: 203256, .ChildIdx: -1,.ChildCount: -1 }, // 7
6741 { .Name: "Copy of Image001 (Final2).png",.Type: "Image file", .Size: 203512, .ChildIdx: -1,.ChildCount: -1 }, // 8
6742 };
6743
6744 MyTreeNode::DisplayNode(node: &nodes[0], all_nodes: nodes);
6745
6746 ImGui::EndTable();
6747 }
6748 ImGui::TreePop();
6749 }
6750
6751 if (open_action != -1)
6752 ImGui::SetNextItemOpen(is_open: open_action != 0);
6753 IMGUI_DEMO_MARKER("Tables/Item width");
6754 if (ImGui::TreeNode(label: "Item width"))
6755 {
6756 HelpMarker(
6757 desc: "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n"
6758 "Note that on auto-resizing non-resizable fixed columns, querying the content width for "
6759 "e.g. right-alignment doesn't make sense.");
6760 if (ImGui::BeginTable(str_id: "table_item_width", columns: 3, flags: ImGuiTableFlags_Borders))
6761 {
6762 ImGui::TableSetupColumn(label: "small");
6763 ImGui::TableSetupColumn(label: "half");
6764 ImGui::TableSetupColumn(label: "right-align");
6765 ImGui::TableHeadersRow();
6766
6767 for (int row = 0; row < 3; row++)
6768 {
6769 ImGui::TableNextRow();
6770 if (row == 0)
6771 {
6772 // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)
6773 ImGui::TableSetColumnIndex(column_n: 0);
6774 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 3.0f); // Small
6775 ImGui::TableSetColumnIndex(column_n: 1);
6776 ImGui::PushItemWidth(item_width: -ImGui::GetContentRegionAvail().x * 0.5f);
6777 ImGui::TableSetColumnIndex(column_n: 2);
6778 ImGui::PushItemWidth(item_width: -FLT_MIN); // Right-aligned
6779 }
6780
6781 // Draw our contents
6782 static float dummy_f = 0.0f;
6783 ImGui::PushID(int_id: row);
6784 ImGui::TableSetColumnIndex(column_n: 0);
6785 ImGui::SliderFloat(label: "float0", v: &dummy_f, v_min: 0.0f, v_max: 1.0f);
6786 ImGui::TableSetColumnIndex(column_n: 1);
6787 ImGui::SliderFloat(label: "float1", v: &dummy_f, v_min: 0.0f, v_max: 1.0f);
6788 ImGui::TableSetColumnIndex(column_n: 2);
6789 ImGui::SliderFloat(label: "##float2", v: &dummy_f, v_min: 0.0f, v_max: 1.0f); // No visible label since right-aligned
6790 ImGui::PopID();
6791 }
6792 ImGui::EndTable();
6793 }
6794 ImGui::TreePop();
6795 }
6796
6797 // Demonstrate using TableHeader() calls instead of TableHeadersRow()
6798 if (open_action != -1)
6799 ImGui::SetNextItemOpen(is_open: open_action != 0);
6800 IMGUI_DEMO_MARKER("Tables/Custom headers");
6801 if (ImGui::TreeNode(label: "Custom headers"))
6802 {
6803 const int COLUMNS_COUNT = 3;
6804 if (ImGui::BeginTable(str_id: "table_custom_headers", columns: COLUMNS_COUNT, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6805 {
6806 ImGui::TableSetupColumn(label: "Apricot");
6807 ImGui::TableSetupColumn(label: "Banana");
6808 ImGui::TableSetupColumn(label: "Cherry");
6809
6810 // Dummy entire-column selection storage
6811 // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
6812 static bool column_selected[3] = {};
6813
6814 // Instead of calling TableHeadersRow() we'll submit custom headers ourselves.
6815 // (A different approach is also possible:
6816 // - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.
6817 // - Call TableHeadersRow() normally. This will submit TableHeader() with no name.
6818 // - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)
6819 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_Headers);
6820 for (int column = 0; column < COLUMNS_COUNT; column++)
6821 {
6822 ImGui::TableSetColumnIndex(column_n: column);
6823 const char* column_name = ImGui::TableGetColumnName(column_n: column); // Retrieve name passed to TableSetupColumn()
6824 ImGui::PushID(int_id: column);
6825 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(0, 0));
6826 ImGui::Checkbox(label: "##checkall", v: &column_selected[column]);
6827 ImGui::PopStyleVar();
6828 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
6829 ImGui::TableHeader(label: column_name);
6830 ImGui::PopID();
6831 }
6832
6833 // Submit table contents
6834 for (int row = 0; row < 5; row++)
6835 {
6836 ImGui::TableNextRow();
6837 for (int column = 0; column < 3; column++)
6838 {
6839 char buf[32];
6840 sprintf(s: buf, format: "Cell %d,%d", column, row);
6841 ImGui::TableSetColumnIndex(column_n: column);
6842 ImGui::Selectable(label: buf, selected: column_selected[column]);
6843 }
6844 }
6845 ImGui::EndTable();
6846 }
6847 ImGui::TreePop();
6848 }
6849
6850 // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers
6851 if (open_action != -1)
6852 ImGui::SetNextItemOpen(is_open: open_action != 0);
6853 IMGUI_DEMO_MARKER("Tables/Angled headers");
6854 if (ImGui::TreeNode(label: "Angled headers"))
6855 {
6856 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" };
6857 const int columns_count = IM_ARRAYSIZE(column_names);
6858 const int rows_count = 12;
6859
6860 static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;
6861 static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed;
6862 static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage
6863 static int frozen_cols = 1;
6864 static int frozen_rows = 2;
6865 ImGui::CheckboxFlags(label: "_ScrollX", flags: &table_flags, flags_value: ImGuiTableFlags_ScrollX);
6866 ImGui::CheckboxFlags(label: "_ScrollY", flags: &table_flags, flags_value: ImGuiTableFlags_ScrollY);
6867 ImGui::CheckboxFlags(label: "_Resizable", flags: &table_flags, flags_value: ImGuiTableFlags_Resizable);
6868 ImGui::CheckboxFlags(label: "_Sortable", flags: &table_flags, flags_value: ImGuiTableFlags_Sortable);
6869 ImGui::CheckboxFlags(label: "_NoBordersInBody", flags: &table_flags, flags_value: ImGuiTableFlags_NoBordersInBody);
6870 ImGui::CheckboxFlags(label: "_HighlightHoveredColumn", flags: &table_flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
6871 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6872 ImGui::SliderInt(label: "Frozen columns", v: &frozen_cols, v_min: 0, v_max: 2);
6873 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6874 ImGui::SliderInt(label: "Frozen rows", v: &frozen_rows, v_min: 0, v_max: 2);
6875 ImGui::CheckboxFlags(label: "Disable header contributing to column width", flags: &column_flags, flags_value: ImGuiTableColumnFlags_NoHeaderWidth);
6876
6877 if (ImGui::TreeNode(label: "Style settings"))
6878 {
6879 ImGui::SameLine();
6880 HelpMarker(desc: "Giving access to some ImGuiStyle value in this demo for convenience.");
6881 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6882 ImGui::SliderAngle(label: "style.TableAngledHeadersAngle", v_rad: &ImGui::GetStyle().TableAngledHeadersAngle, v_degrees_min: -50.0f, v_degrees_max: +50.0f);
6883 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6884 ImGui::SliderFloat2(label: "style.TableAngledHeadersTextAlign", v: (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
6885 ImGui::TreePop();
6886 }
6887
6888 if (ImGui::BeginTable(str_id: "table_angled_headers", columns: columns_count, flags: table_flags, outer_size: ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))
6889 {
6890 ImGui::TableSetupColumn(label: column_names[0], flags: ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
6891 for (int n = 1; n < columns_count; n++)
6892 ImGui::TableSetupColumn(label: column_names[n], flags: column_flags);
6893 ImGui::TableSetupScrollFreeze(cols: frozen_cols, rows: frozen_rows);
6894
6895 ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.
6896 ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions.
6897 for (int row = 0; row < rows_count; row++)
6898 {
6899 ImGui::PushID(int_id: row);
6900 ImGui::TableNextRow();
6901 ImGui::TableSetColumnIndex(column_n: 0);
6902 ImGui::AlignTextToFramePadding();
6903 ImGui::Text(fmt: "Track %d", row);
6904 for (int column = 1; column < columns_count; column++)
6905 if (ImGui::TableSetColumnIndex(column_n: column))
6906 {
6907 ImGui::PushID(int_id: column);
6908 ImGui::Checkbox(label: "", v: &bools[row * columns_count + column]);
6909 ImGui::PopID();
6910 }
6911 ImGui::PopID();
6912 }
6913 ImGui::EndTable();
6914 }
6915 ImGui::TreePop();
6916 }
6917
6918 // Demonstrate creating custom context menus inside columns,
6919 // while playing it nice with context menus provided by TableHeadersRow()/TableHeader()
6920 if (open_action != -1)
6921 ImGui::SetNextItemOpen(is_open: open_action != 0);
6922 IMGUI_DEMO_MARKER("Tables/Context menus");
6923 if (ImGui::TreeNode(label: "Context menus"))
6924 {
6925 HelpMarker(
6926 desc: "By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n"
6927 "Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.");
6928 static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody;
6929
6930 PushStyleCompact();
6931 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ContextMenuInBody", flags: &flags1, flags_value: ImGuiTableFlags_ContextMenuInBody);
6932 PopStyleCompact();
6933
6934 // Context Menus: first example
6935 // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.
6936 // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)
6937 const int COLUMNS_COUNT = 3;
6938 if (ImGui::BeginTable(str_id: "table_context_menu", columns: COLUMNS_COUNT, flags: flags1))
6939 {
6940 ImGui::TableSetupColumn(label: "One");
6941 ImGui::TableSetupColumn(label: "Two");
6942 ImGui::TableSetupColumn(label: "Three");
6943
6944 // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.
6945 ImGui::TableHeadersRow();
6946
6947 // Submit dummy contents
6948 for (int row = 0; row < 4; row++)
6949 {
6950 ImGui::TableNextRow();
6951 for (int column = 0; column < COLUMNS_COUNT; column++)
6952 {
6953 ImGui::TableSetColumnIndex(column_n: column);
6954 ImGui::Text(fmt: "Cell %d,%d", column, row);
6955 }
6956 }
6957 ImGui::EndTable();
6958 }
6959
6960 // Context Menus: second example
6961 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
6962 // [2.2] Right-click on the ".." to open a custom popup
6963 // [2.3] Right-click in columns to open another custom popup
6964 HelpMarker(
6965 desc: "Demonstrate mixing table context menu (over header), item context button (over button) "
6966 "and custom per-colunm context menu (over column body).");
6967 ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
6968 if (ImGui::BeginTable(str_id: "table_context_menu_2", columns: COLUMNS_COUNT, flags: flags2))
6969 {
6970 ImGui::TableSetupColumn(label: "One");
6971 ImGui::TableSetupColumn(label: "Two");
6972 ImGui::TableSetupColumn(label: "Three");
6973
6974 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
6975 ImGui::TableHeadersRow();
6976 for (int row = 0; row < 4; row++)
6977 {
6978 ImGui::TableNextRow();
6979 for (int column = 0; column < COLUMNS_COUNT; column++)
6980 {
6981 // Submit dummy contents
6982 ImGui::TableSetColumnIndex(column_n: column);
6983 ImGui::Text(fmt: "Cell %d,%d", column, row);
6984 ImGui::SameLine();
6985
6986 // [2.2] Right-click on the ".." to open a custom popup
6987 ImGui::PushID(int_id: row * COLUMNS_COUNT + column);
6988 ImGui::SmallButton(label: "..");
6989 if (ImGui::BeginPopupContextItem())
6990 {
6991 ImGui::Text(fmt: "This is the popup for Button(\"..\") in Cell %d,%d", column, row);
6992 if (ImGui::Button(label: "Close"))
6993 ImGui::CloseCurrentPopup();
6994 ImGui::EndPopup();
6995 }
6996 ImGui::PopID();
6997 }
6998 }
6999
7000 // [2.3] Right-click anywhere in columns to open another custom popup
7001 // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup
7002 // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping)
7003 int hovered_column = -1;
7004 for (int column = 0; column < COLUMNS_COUNT + 1; column++)
7005 {
7006 ImGui::PushID(int_id: column);
7007 if (ImGui::TableGetColumnFlags(column_n: column) & ImGuiTableColumnFlags_IsHovered)
7008 hovered_column = column;
7009 if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(button: 1))
7010 ImGui::OpenPopup(str_id: "MyPopup");
7011 if (ImGui::BeginPopup(str_id: "MyPopup"))
7012 {
7013 if (column == COLUMNS_COUNT)
7014 ImGui::Text(fmt: "This is a custom popup for unused space after the last column.");
7015 else
7016 ImGui::Text(fmt: "This is a custom popup for Column %d", column);
7017 if (ImGui::Button(label: "Close"))
7018 ImGui::CloseCurrentPopup();
7019 ImGui::EndPopup();
7020 }
7021 ImGui::PopID();
7022 }
7023
7024 ImGui::EndTable();
7025 ImGui::Text(fmt: "Hovered column: %d", hovered_column);
7026 }
7027 ImGui::TreePop();
7028 }
7029
7030 // Demonstrate creating multiple tables with the same ID
7031 if (open_action != -1)
7032 ImGui::SetNextItemOpen(is_open: open_action != 0);
7033 IMGUI_DEMO_MARKER("Tables/Synced instances");
7034 if (ImGui::TreeNode(label: "Synced instances"))
7035 {
7036 HelpMarker(desc: "Multiple tables with the same identifier will share their settings, width, visibility, order etc.");
7037
7038 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;
7039 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
7040 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
7041 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SizingFixedFit", flags: &flags, flags_value: ImGuiTableFlags_SizingFixedFit);
7042 ImGui::CheckboxFlags(label: "ImGuiTableFlags_HighlightHoveredColumn", flags: &flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
7043 for (int n = 0; n < 3; n++)
7044 {
7045 char buf[32];
7046 sprintf(s: buf, format: "Synced Table %d", n);
7047 bool open = ImGui::CollapsingHeader(label: buf, flags: ImGuiTreeNodeFlags_DefaultOpen);
7048 if (open && ImGui::BeginTable(str_id: "Table", columns: 3, flags, outer_size: ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5)))
7049 {
7050 ImGui::TableSetupColumn(label: "One");
7051 ImGui::TableSetupColumn(label: "Two");
7052 ImGui::TableSetupColumn(label: "Three");
7053 ImGui::TableHeadersRow();
7054 const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions.
7055 for (int cell = 0; cell < cell_count; cell++)
7056 {
7057 ImGui::TableNextColumn();
7058 ImGui::Text(fmt: "this cell %d", cell);
7059 }
7060 ImGui::EndTable();
7061 }
7062 }
7063 ImGui::TreePop();
7064 }
7065
7066 // Demonstrate using Sorting facilities
7067 // This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting.
7068 // Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified)
7069 static const char* template_items_names[] =
7070 {
7071 "Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango",
7072 "Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot"
7073 };
7074 if (open_action != -1)
7075 ImGui::SetNextItemOpen(is_open: open_action != 0);
7076 IMGUI_DEMO_MARKER("Tables/Sorting");
7077 if (ImGui::TreeNode(label: "Sorting"))
7078 {
7079 // Create item list
7080 static ImVector<MyItem> items;
7081 if (items.Size == 0)
7082 {
7083 items.resize(new_size: 50, v: MyItem());
7084 for (int n = 0; n < items.Size; n++)
7085 {
7086 const int template_n = n % IM_ARRAYSIZE(template_items_names);
7087 MyItem& item = items[n];
7088 item.ID = n;
7089 item.Name = template_items_names[template_n];
7090 item.Quantity = (n * n - n) % 20; // Assign default quantities
7091 }
7092 }
7093
7094 // Options
7095 static ImGuiTableFlags flags =
7096 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7097 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody
7098 | ImGuiTableFlags_ScrollY;
7099 PushStyleCompact();
7100 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortMulti", flags: &flags, flags_value: ImGuiTableFlags_SortMulti);
7101 ImGui::SameLine(); HelpMarker(desc: "When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7102 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortTristate", flags: &flags, flags_value: ImGuiTableFlags_SortTristate);
7103 ImGui::SameLine(); HelpMarker(desc: "When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7104 PopStyleCompact();
7105
7106 if (ImGui::BeginTable(str_id: "table_sorting", columns: 4, flags, outer_size: ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), inner_width: 0.0f))
7107 {
7108 // Declare columns
7109 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7110 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7111 // Demonstrate using a mixture of flags among available sort-related flags:
7112 // - ImGuiTableColumnFlags_DefaultSort
7113 // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending
7114 // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending
7115 ImGui::TableSetupColumn(label: "ID", flags: ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_ID);
7116 ImGui::TableSetupColumn(label: "Name", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Name);
7117 ImGui::TableSetupColumn(label: "Action", flags: ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Action);
7118 ImGui::TableSetupColumn(label: "Quantity", flags: ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Quantity);
7119 ImGui::TableSetupScrollFreeze(cols: 0, rows: 1); // Make row always visible
7120 ImGui::TableHeadersRow();
7121
7122 // Sort our data if sort specs have been changed!
7123 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
7124 if (sort_specs->SpecsDirty)
7125 {
7126 MyItem::SortWithSortSpecs(sort_specs, items: items.Data, items_count: items.Size);
7127 sort_specs->SpecsDirty = false;
7128 }
7129
7130 // Demonstrate using clipper for large vertical lists
7131 ImGuiListClipper clipper;
7132 clipper.Begin(items_count: items.Size);
7133 while (clipper.Step())
7134 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7135 {
7136 // Display a data item
7137 MyItem* item = &items[row_n];
7138 ImGui::PushID(int_id: item->ID);
7139 ImGui::TableNextRow();
7140 ImGui::TableNextColumn();
7141 ImGui::Text(fmt: "%04d", item->ID);
7142 ImGui::TableNextColumn();
7143 ImGui::TextUnformatted(text: item->Name);
7144 ImGui::TableNextColumn();
7145 ImGui::SmallButton(label: "None");
7146 ImGui::TableNextColumn();
7147 ImGui::Text(fmt: "%d", item->Quantity);
7148 ImGui::PopID();
7149 }
7150 ImGui::EndTable();
7151 }
7152 ImGui::TreePop();
7153 }
7154
7155 // In this example we'll expose most table flags and settings.
7156 // For specific flags and settings refer to the corresponding section for more detailed explanation.
7157 // This section is mostly useful to experiment with combining certain flags or settings with each others.
7158 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]
7159 if (open_action != -1)
7160 ImGui::SetNextItemOpen(is_open: open_action != 0);
7161 IMGUI_DEMO_MARKER("Tables/Advanced");
7162 if (ImGui::TreeNode(label: "Advanced"))
7163 {
7164 static ImGuiTableFlags flags =
7165 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable
7166 | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7167 | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody
7168 | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
7169 | ImGuiTableFlags_SizingFixedFit;
7170 static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;
7171
7172 enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };
7173 static int contents_type = CT_SelectableSpanRow;
7174 const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" };
7175 static int freeze_cols = 1;
7176 static int freeze_rows = 1;
7177 static int items_count = IM_ARRAYSIZE(template_items_names) * 2;
7178 static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);
7179 static float row_min_height = 0.0f; // Auto
7180 static float inner_width_with_scroll = 0.0f; // Auto-extend
7181 static bool outer_size_enabled = true;
7182 static bool show_headers = true;
7183 static bool show_wrapped_text = false;
7184 //static ImGuiTextFilter filter;
7185 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing
7186 if (ImGui::TreeNode(label: "Options"))
7187 {
7188 // Make the UI compact because there are so many fields
7189 PushStyleCompact();
7190 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 28.0f);
7191
7192 if (ImGui::TreeNodeEx(label: "Features:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7193 {
7194 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
7195 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Reorderable", flags: &flags, flags_value: ImGuiTableFlags_Reorderable);
7196 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Hideable", flags: &flags, flags_value: ImGuiTableFlags_Hideable);
7197 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Sortable", flags: &flags, flags_value: ImGuiTableFlags_Sortable);
7198 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoSavedSettings", flags: &flags, flags_value: ImGuiTableFlags_NoSavedSettings);
7199 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ContextMenuInBody", flags: &flags, flags_value: ImGuiTableFlags_ContextMenuInBody);
7200 ImGui::TreePop();
7201 }
7202
7203 if (ImGui::TreeNodeEx(label: "Decorations:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7204 {
7205 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags, flags_value: ImGuiTableFlags_RowBg);
7206 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags, flags_value: ImGuiTableFlags_BordersV);
7207 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterV);
7208 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerV);
7209 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersH", flags: &flags, flags_value: ImGuiTableFlags_BordersH);
7210 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterH", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterH);
7211 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerH", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerH);
7212 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBody", flags: &flags, flags_value: ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker(desc: "Disable vertical borders in columns Body (borders will always appear in Headers");
7213 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBodyUntilResize", flags: &flags, flags_value: ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker(desc: "Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
7214 ImGui::TreePop();
7215 }
7216
7217 if (ImGui::TreeNodeEx(label: "Sizing:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7218 {
7219 EditTableSizingFlags(p_flags: &flags);
7220 ImGui::SameLine(); HelpMarker(desc: "In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical.");
7221 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendX);
7222 ImGui::SameLine(); HelpMarker(desc: "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.");
7223 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendY", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendY);
7224 ImGui::SameLine(); HelpMarker(desc: "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.");
7225 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoKeepColumnsVisible", flags: &flags, flags_value: ImGuiTableFlags_NoKeepColumnsVisible);
7226 ImGui::SameLine(); HelpMarker(desc: "Only available if ScrollX is disabled.");
7227 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PreciseWidths", flags: &flags, flags_value: ImGuiTableFlags_PreciseWidths);
7228 ImGui::SameLine(); HelpMarker(desc: "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.");
7229 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoClip", flags: &flags, flags_value: ImGuiTableFlags_NoClip);
7230 ImGui::SameLine(); HelpMarker(desc: "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.");
7231 ImGui::TreePop();
7232 }
7233
7234 if (ImGui::TreeNodeEx(label: "Padding:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7235 {
7236 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PadOuterX", flags: &flags, flags_value: ImGuiTableFlags_PadOuterX);
7237 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadOuterX", flags: &flags, flags_value: ImGuiTableFlags_NoPadOuterX);
7238 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadInnerX", flags: &flags, flags_value: ImGuiTableFlags_NoPadInnerX);
7239 ImGui::TreePop();
7240 }
7241
7242 if (ImGui::TreeNodeEx(label: "Scrolling:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7243 {
7244 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags, flags_value: ImGuiTableFlags_ScrollX);
7245 ImGui::SameLine();
7246 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7247 ImGui::DragInt(label: "freeze_cols", v: &freeze_cols, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
7248 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
7249 ImGui::SameLine();
7250 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7251 ImGui::DragInt(label: "freeze_rows", v: &freeze_rows, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
7252 ImGui::TreePop();
7253 }
7254
7255 if (ImGui::TreeNodeEx(label: "Sorting:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7256 {
7257 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortMulti", flags: &flags, flags_value: ImGuiTableFlags_SortMulti);
7258 ImGui::SameLine(); HelpMarker(desc: "When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7259 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortTristate", flags: &flags, flags_value: ImGuiTableFlags_SortTristate);
7260 ImGui::SameLine(); HelpMarker(desc: "When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7261 ImGui::TreePop();
7262 }
7263
7264 if (ImGui::TreeNodeEx(label: "Headers:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7265 {
7266 ImGui::Checkbox(label: "show_headers", v: &show_headers);
7267 ImGui::CheckboxFlags(label: "ImGuiTableFlags_HighlightHoveredColumn", flags: &flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
7268 ImGui::CheckboxFlags(label: "ImGuiTableColumnFlags_AngledHeader", flags: &columns_base_flags, flags_value: ImGuiTableColumnFlags_AngledHeader);
7269 ImGui::SameLine(); HelpMarker(desc: "Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo).");
7270 ImGui::TreePop();
7271 }
7272
7273 if (ImGui::TreeNodeEx(label: "Other:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7274 {
7275 ImGui::Checkbox(label: "show_wrapped_text", v: &show_wrapped_text);
7276
7277 ImGui::DragFloat2(label: "##OuterSize", v: &outer_size_value.x);
7278 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
7279 ImGui::Checkbox(label: "outer_size", v: &outer_size_enabled);
7280 ImGui::SameLine();
7281 HelpMarker(desc: "If scrolling is disabled (ScrollX and ScrollY not set):\n"
7282 "- The table is output directly in the parent window.\n"
7283 "- OuterSize.x < 0.0f will right-align the table.\n"
7284 "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n"
7285 "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).");
7286
7287 // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.
7288 // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.
7289 ImGui::DragFloat(label: "inner_width (when ScrollX active)", v: &inner_width_with_scroll, v_speed: 1.0f, v_min: 0.0f, FLT_MAX);
7290
7291 ImGui::DragFloat(label: "row_min_height", v: &row_min_height, v_speed: 1.0f, v_min: 0.0f, FLT_MAX);
7292 ImGui::SameLine(); HelpMarker(desc: "Specify height of the Selectable item.");
7293
7294 ImGui::DragInt(label: "items_count", v: &items_count, v_speed: 0.1f, v_min: 0, v_max: 9999);
7295 ImGui::Combo(label: "items_type (first column)", current_item: &contents_type, items: contents_type_names, IM_ARRAYSIZE(contents_type_names));
7296 //filter.Draw("filter");
7297 ImGui::TreePop();
7298 }
7299
7300 ImGui::PopItemWidth();
7301 PopStyleCompact();
7302 ImGui::Spacing();
7303 ImGui::TreePop();
7304 }
7305
7306 // Update item list if we changed the number of items
7307 static ImVector<MyItem> items;
7308 static ImVector<int> selection;
7309 static bool items_need_sort = false;
7310 if (items.Size != items_count)
7311 {
7312 items.resize(new_size: items_count, v: MyItem());
7313 for (int n = 0; n < items_count; n++)
7314 {
7315 const int template_n = n % IM_ARRAYSIZE(template_items_names);
7316 MyItem& item = items[n];
7317 item.ID = n;
7318 item.Name = template_items_names[template_n];
7319 item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities
7320 }
7321 }
7322
7323 const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();
7324 const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;
7325 ImVec2 table_scroll_cur, table_scroll_max; // For debug display
7326 const ImDrawList* table_draw_list = NULL; // "
7327
7328 // Submit table
7329 const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;
7330 if (ImGui::BeginTable(str_id: "table_advanced", columns: 6, flags, outer_size: outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width: inner_width_to_use))
7331 {
7332 // Declare columns
7333 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7334 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7335 ImGui::TableSetupColumn(label: "ID", flags: columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, init_width_or_weight: 0.0f, user_id: MyItemColumnID_ID);
7336 ImGui::TableSetupColumn(label: "Name", flags: columns_base_flags | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Name);
7337 ImGui::TableSetupColumn(label: "Action", flags: columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Action);
7338 ImGui::TableSetupColumn(label: "Quantity", flags: columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Quantity);
7339 ImGui::TableSetupColumn(label: "Description", flags: columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), init_width_or_weight: 0.0f, user_id: MyItemColumnID_Description);
7340 ImGui::TableSetupColumn(label: "Hidden", flags: columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);
7341 ImGui::TableSetupScrollFreeze(cols: freeze_cols, rows: freeze_rows);
7342
7343 // Sort our data if sort specs have been changed!
7344 ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();
7345 if (sort_specs && sort_specs->SpecsDirty)
7346 items_need_sort = true;
7347 if (sort_specs && items_need_sort && items.Size > 1)
7348 {
7349 MyItem::SortWithSortSpecs(sort_specs, items: items.Data, items_count: items.Size);
7350 sort_specs->SpecsDirty = false;
7351 }
7352 items_need_sort = false;
7353
7354 // Take note of whether we are currently sorting based on the Quantity field,
7355 // we will use this to trigger sorting when we know the data of this column has been modified.
7356 const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(column_n: 3) & ImGuiTableColumnFlags_IsSorted) != 0;
7357
7358 // Show headers
7359 if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)
7360 ImGui::TableAngledHeadersRow();
7361 if (show_headers)
7362 ImGui::TableHeadersRow();
7363
7364 // Show data
7365 // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?
7366#if 1
7367 // Demonstrate using clipper for large vertical lists
7368 ImGuiListClipper clipper;
7369 clipper.Begin(items_count: items.Size);
7370 while (clipper.Step())
7371 {
7372 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7373#else
7374 // Without clipper
7375 {
7376 for (int row_n = 0; row_n < items.Size; row_n++)
7377#endif
7378 {
7379 MyItem* item = &items[row_n];
7380 //if (!filter.PassFilter(item->Name))
7381 // continue;
7382
7383 const bool item_is_selected = selection.contains(v: item->ID);
7384 ImGui::PushID(int_id: item->ID);
7385 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height: row_min_height);
7386
7387 // For the demo purpose we can select among different type of items submitted in the first column
7388 ImGui::TableSetColumnIndex(column_n: 0);
7389 char label[32];
7390 sprintf(s: label, format: "%04d", item->ID);
7391 if (contents_type == CT_Text)
7392 ImGui::TextUnformatted(text: label);
7393 else if (contents_type == CT_Button)
7394 ImGui::Button(label);
7395 else if (contents_type == CT_SmallButton)
7396 ImGui::SmallButton(label);
7397 else if (contents_type == CT_FillButton)
7398 ImGui::Button(label, size: ImVec2(-FLT_MIN, 0.0f));
7399 else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)
7400 {
7401 ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None;
7402 if (ImGui::Selectable(label, selected: item_is_selected, flags: selectable_flags, size: ImVec2(0, row_min_height)))
7403 {
7404 if (ImGui::GetIO().KeyCtrl)
7405 {
7406 if (item_is_selected)
7407 selection.find_erase_unsorted(v: item->ID);
7408 else
7409 selection.push_back(v: item->ID);
7410 }
7411 else
7412 {
7413 selection.clear();
7414 selection.push_back(v: item->ID);
7415 }
7416 }
7417 }
7418
7419 if (ImGui::TableSetColumnIndex(column_n: 1))
7420 ImGui::TextUnformatted(text: item->Name);
7421
7422 // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
7423 // and we are currently sorting on the column showing the Quantity.
7424 // To avoid triggering a sort while holding the button, we only trigger it when the button has been released.
7425 // You will probably need some extra logic if you want to automatically sort when a specific entry changes.
7426 if (ImGui::TableSetColumnIndex(column_n: 2))
7427 {
7428 if (ImGui::SmallButton(label: "Chop")) { item->Quantity += 1; }
7429 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7430 ImGui::SameLine();
7431 if (ImGui::SmallButton(label: "Eat")) { item->Quantity -= 1; }
7432 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7433 }
7434
7435 if (ImGui::TableSetColumnIndex(column_n: 3))
7436 ImGui::Text(fmt: "%d", item->Quantity);
7437
7438 ImGui::TableSetColumnIndex(column_n: 4);
7439 if (show_wrapped_text)
7440 ImGui::TextWrapped(fmt: "Lorem ipsum dolor sit amet");
7441 else
7442 ImGui::Text(fmt: "Lorem ipsum dolor sit amet");
7443
7444 if (ImGui::TableSetColumnIndex(column_n: 5))
7445 ImGui::Text(fmt: "1234");
7446
7447 ImGui::PopID();
7448 }
7449 }
7450
7451 // Store some info to display debug details below
7452 table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());
7453 table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());
7454 table_draw_list = ImGui::GetWindowDrawList();
7455 ImGui::EndTable();
7456 }
7457 static bool show_debug_details = false;
7458 ImGui::Checkbox(label: "Debug details", v: &show_debug_details);
7459 if (show_debug_details && table_draw_list)
7460 {
7461 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: 0.0f);
7462 const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;
7463 if (table_draw_list == parent_draw_list)
7464 ImGui::Text(fmt: ": DrawCmd: +%d (in same window)",
7465 table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);
7466 else
7467 ImGui::Text(fmt: ": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)",
7468 table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y);
7469 }
7470 ImGui::TreePop();
7471 }
7472
7473 ImGui::PopID();
7474
7475 DemoWindowColumns();
7476
7477 if (disable_indent)
7478 ImGui::PopStyleVar();
7479}
7480
7481// Demonstrate old/legacy Columns API!
7482// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]
7483static void DemoWindowColumns()
7484{
7485 IMGUI_DEMO_MARKER("Columns (legacy API)");
7486 bool open = ImGui::TreeNode(label: "Legacy Columns API");
7487 ImGui::SameLine();
7488 HelpMarker(desc: "Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!");
7489 if (!open)
7490 return;
7491
7492 // Basic columns
7493 IMGUI_DEMO_MARKER("Columns (legacy API)/Basic");
7494 if (ImGui::TreeNode(label: "Basic"))
7495 {
7496 ImGui::Text(fmt: "Without border:");
7497 ImGui::Columns(count: 3, id: "mycolumns3", borders: false); // 3-ways, no border
7498 ImGui::Separator();
7499 for (int n = 0; n < 14; n++)
7500 {
7501 char label[32];
7502 sprintf(s: label, format: "Item %d", n);
7503 if (ImGui::Selectable(label)) {}
7504 //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}
7505 ImGui::NextColumn();
7506 }
7507 ImGui::Columns(count: 1);
7508 ImGui::Separator();
7509
7510 ImGui::Text(fmt: "With border:");
7511 ImGui::Columns(count: 4, id: "mycolumns"); // 4-ways, with border
7512 ImGui::Separator();
7513 ImGui::Text(fmt: "ID"); ImGui::NextColumn();
7514 ImGui::Text(fmt: "Name"); ImGui::NextColumn();
7515 ImGui::Text(fmt: "Path"); ImGui::NextColumn();
7516 ImGui::Text(fmt: "Hovered"); ImGui::NextColumn();
7517 ImGui::Separator();
7518 const char* names[3] = { "One", "Two", "Three" };
7519 const char* paths[3] = { "/path/one", "/path/two", "/path/three" };
7520 static int selected = -1;
7521 for (int i = 0; i < 3; i++)
7522 {
7523 char label[32];
7524 sprintf(s: label, format: "%04d", i);
7525 if (ImGui::Selectable(label, selected: selected == i, flags: ImGuiSelectableFlags_SpanAllColumns))
7526 selected = i;
7527 bool hovered = ImGui::IsItemHovered();
7528 ImGui::NextColumn();
7529 ImGui::Text(fmt: names[i]); ImGui::NextColumn();
7530 ImGui::Text(fmt: paths[i]); ImGui::NextColumn();
7531 ImGui::Text(fmt: "%d", hovered); ImGui::NextColumn();
7532 }
7533 ImGui::Columns(count: 1);
7534 ImGui::Separator();
7535 ImGui::TreePop();
7536 }
7537
7538 IMGUI_DEMO_MARKER("Columns (legacy API)/Borders");
7539 if (ImGui::TreeNode(label: "Borders"))
7540 {
7541 // NB: Future columns API should allow automatic horizontal borders.
7542 static bool h_borders = true;
7543 static bool v_borders = true;
7544 static int columns_count = 4;
7545 const int lines_count = 3;
7546 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7547 ImGui::DragInt(label: "##columns_count", v: &columns_count, v_speed: 0.1f, v_min: 2, v_max: 10, format: "%d columns");
7548 if (columns_count < 2)
7549 columns_count = 2;
7550 ImGui::SameLine();
7551 ImGui::Checkbox(label: "horizontal", v: &h_borders);
7552 ImGui::SameLine();
7553 ImGui::Checkbox(label: "vertical", v: &v_borders);
7554 ImGui::Columns(count: columns_count, NULL, borders: v_borders);
7555 for (int i = 0; i < columns_count * lines_count; i++)
7556 {
7557 if (h_borders && ImGui::GetColumnIndex() == 0)
7558 ImGui::Separator();
7559 ImGui::PushID(int_id: i);
7560 ImGui::Text(fmt: "%c%c%c", 'a' + i, 'a' + i, 'a' + i);
7561 ImGui::Text(fmt: "Width %.2f", ImGui::GetColumnWidth());
7562 ImGui::Text(fmt: "Avail %.2f", ImGui::GetContentRegionAvail().x);
7563 ImGui::Text(fmt: "Offset %.2f", ImGui::GetColumnOffset());
7564 ImGui::Text(fmt: "Long text that is likely to clip");
7565 ImGui::Button(label: "Button", size: ImVec2(-FLT_MIN, 0.0f));
7566 ImGui::PopID();
7567 ImGui::NextColumn();
7568 }
7569 ImGui::Columns(count: 1);
7570 if (h_borders)
7571 ImGui::Separator();
7572 ImGui::TreePop();
7573 }
7574
7575 // Create multiple items in a same cell before switching to next column
7576 IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items");
7577 if (ImGui::TreeNode(label: "Mixed items"))
7578 {
7579 ImGui::Columns(count: 3, id: "mixed");
7580 ImGui::Separator();
7581
7582 ImGui::Text(fmt: "Hello");
7583 ImGui::Button(label: "Banana");
7584 ImGui::NextColumn();
7585
7586 ImGui::Text(fmt: "ImGui");
7587 ImGui::Button(label: "Apple");
7588 static float foo = 1.0f;
7589 ImGui::InputFloat(label: "red", v: &foo, step: 0.05f, step_fast: 0, format: "%.3f");
7590 ImGui::Text(fmt: "An extra line here.");
7591 ImGui::NextColumn();
7592
7593 ImGui::Text(fmt: "Sailor");
7594 ImGui::Button(label: "Corniflower");
7595 static float bar = 1.0f;
7596 ImGui::InputFloat(label: "blue", v: &bar, step: 0.05f, step_fast: 0, format: "%.3f");
7597 ImGui::NextColumn();
7598
7599 if (ImGui::CollapsingHeader(label: "Category A")) { ImGui::Text(fmt: "Blah blah blah"); } ImGui::NextColumn();
7600 if (ImGui::CollapsingHeader(label: "Category B")) { ImGui::Text(fmt: "Blah blah blah"); } ImGui::NextColumn();
7601 if (ImGui::CollapsingHeader(label: "Category C")) { ImGui::Text(fmt: "Blah blah blah"); } ImGui::NextColumn();
7602 ImGui::Columns(count: 1);
7603 ImGui::Separator();
7604 ImGui::TreePop();
7605 }
7606
7607 // Word wrapping
7608 IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping");
7609 if (ImGui::TreeNode(label: "Word-wrapping"))
7610 {
7611 ImGui::Columns(count: 2, id: "word-wrapping");
7612 ImGui::Separator();
7613 ImGui::TextWrapped(fmt: "The quick brown fox jumps over the lazy dog.");
7614 ImGui::TextWrapped(fmt: "Hello Left");
7615 ImGui::NextColumn();
7616 ImGui::TextWrapped(fmt: "The quick brown fox jumps over the lazy dog.");
7617 ImGui::TextWrapped(fmt: "Hello Right");
7618 ImGui::Columns(count: 1);
7619 ImGui::Separator();
7620 ImGui::TreePop();
7621 }
7622
7623 IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling");
7624 if (ImGui::TreeNode(label: "Horizontal Scrolling"))
7625 {
7626 ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));
7627 ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);
7628 ImGui::BeginChild(str_id: "##ScrollingRegion", size: child_size, child_flags: ImGuiChildFlags_None, window_flags: ImGuiWindowFlags_HorizontalScrollbar);
7629 ImGui::Columns(count: 10);
7630
7631 // Also demonstrate using clipper for large vertical lists
7632 int ITEMS_COUNT = 2000;
7633 ImGuiListClipper clipper;
7634 clipper.Begin(items_count: ITEMS_COUNT);
7635 while (clipper.Step())
7636 {
7637 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
7638 for (int j = 0; j < 10; j++)
7639 {
7640 ImGui::Text(fmt: "Line %d Column %d...", i, j);
7641 ImGui::NextColumn();
7642 }
7643 }
7644 ImGui::Columns(count: 1);
7645 ImGui::EndChild();
7646 ImGui::TreePop();
7647 }
7648
7649 IMGUI_DEMO_MARKER("Columns (legacy API)/Tree");
7650 if (ImGui::TreeNode(label: "Tree"))
7651 {
7652 ImGui::Columns(count: 2, id: "tree", borders: true);
7653 for (int x = 0; x < 3; x++)
7654 {
7655 bool open1 = ImGui::TreeNode(ptr_id: (void*)(intptr_t)x, fmt: "Node%d", x);
7656 ImGui::NextColumn();
7657 ImGui::Text(fmt: "Node contents");
7658 ImGui::NextColumn();
7659 if (open1)
7660 {
7661 for (int y = 0; y < 3; y++)
7662 {
7663 bool open2 = ImGui::TreeNode(ptr_id: (void*)(intptr_t)y, fmt: "Node%d.%d", x, y);
7664 ImGui::NextColumn();
7665 ImGui::Text(fmt: "Node contents");
7666 if (open2)
7667 {
7668 ImGui::Text(fmt: "Even more contents");
7669 if (ImGui::TreeNode(label: "Tree in column"))
7670 {
7671 ImGui::Text(fmt: "The quick brown fox jumps over the lazy dog");
7672 ImGui::TreePop();
7673 }
7674 }
7675 ImGui::NextColumn();
7676 if (open2)
7677 ImGui::TreePop();
7678 }
7679 ImGui::TreePop();
7680 }
7681 }
7682 ImGui::Columns(count: 1);
7683 ImGui::TreePop();
7684 }
7685
7686 ImGui::TreePop();
7687}
7688
7689//-----------------------------------------------------------------------------
7690// [SECTION] DemoWindowInputs()
7691//-----------------------------------------------------------------------------
7692
7693static void DemoWindowInputs()
7694{
7695 IMGUI_DEMO_MARKER("Inputs & Focus");
7696 if (ImGui::CollapsingHeader(label: "Inputs & Focus"))
7697 {
7698 ImGuiIO& io = ImGui::GetIO();
7699
7700 // Display inputs submitted to ImGuiIO
7701 IMGUI_DEMO_MARKER("Inputs & Focus/Inputs");
7702 ImGui::SetNextItemOpen(is_open: true, cond: ImGuiCond_Once);
7703 bool inputs_opened = ImGui::TreeNode(label: "Inputs");
7704 ImGui::SameLine();
7705 HelpMarker(
7706 desc: "This is a simplified view. See more detailed input state:\n"
7707 "- in 'Tools->Metrics/Debugger->Inputs'.\n"
7708 "- in 'Tools->Debug Log->IO'.");
7709 if (inputs_opened)
7710 {
7711 if (ImGui::IsMousePosValid())
7712 ImGui::Text(fmt: "Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
7713 else
7714 ImGui::Text(fmt: "Mouse pos: <INVALID>");
7715 ImGui::Text(fmt: "Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
7716 ImGui::Text(fmt: "Mouse down:");
7717 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(button: i)) { ImGui::SameLine(); ImGui::Text(fmt: "b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
7718 ImGui::Text(fmt: "Mouse wheel: %.1f", io.MouseWheel);
7719 ImGui::Text(fmt: "Mouse clicked count:");
7720 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text(fmt: "b%d: %d", i, io.MouseClickedCount[i]); }
7721
7722 // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows
7723 // displaying the data for old/new backends.
7724 // User code should never have to go through such hoops!
7725 // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
7726 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
7727 ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;
7728 ImGui::Text(fmt: "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(fmt: (key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); }
7729 ImGui::Text(fmt: "Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
7730 ImGui::Text(fmt: "Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text(fmt: "\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
7731
7732 ImGui::TreePop();
7733 }
7734
7735 // Display ImGuiIO output flags
7736 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs");
7737 ImGui::SetNextItemOpen(is_open: true, cond: ImGuiCond_Once);
7738 bool outputs_opened = ImGui::TreeNode(label: "Outputs");
7739 ImGui::SameLine();
7740 HelpMarker(
7741 desc: "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui "
7742 "to instruct your application of how to route inputs. Typically, when a value is true, it means "
7743 "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n"
7744 "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, "
7745 "and underlying application should ignore mouse inputs (in practice there are many and more subtle "
7746 "rules leading to how those flags are set).");
7747 if (outputs_opened)
7748 {
7749 ImGui::Text(fmt: "io.WantCaptureMouse: %d", io.WantCaptureMouse);
7750 ImGui::Text(fmt: "io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
7751 ImGui::Text(fmt: "io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
7752 ImGui::Text(fmt: "io.WantTextInput: %d", io.WantTextInput);
7753 ImGui::Text(fmt: "io.WantSetMousePos: %d", io.WantSetMousePos);
7754 ImGui::Text(fmt: "io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible);
7755
7756 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override");
7757 if (ImGui::TreeNode(label: "WantCapture override"))
7758 {
7759 HelpMarker(
7760 desc: "Hovering the colored canvas will override io.WantCaptureXXX fields.\n"
7761 "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering "
7762 "and true when clicking.");
7763 static int capture_override_mouse = -1;
7764 static int capture_override_keyboard = -1;
7765 const char* capture_override_desc[] = { "None", "Set to false", "Set to true" };
7766 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7767 ImGui::SliderInt(label: "SetNextFrameWantCaptureMouse() on hover", v: &capture_override_mouse, v_min: -1, v_max: +1, format: capture_override_desc[capture_override_mouse + 1], flags: ImGuiSliderFlags_AlwaysClamp);
7768 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7769 ImGui::SliderInt(label: "SetNextFrameWantCaptureKeyboard() on hover", v: &capture_override_keyboard, v_min: -1, v_max: +1, format: capture_override_desc[capture_override_keyboard + 1], flags: ImGuiSliderFlags_AlwaysClamp);
7770
7771 ImGui::ColorButton(desc_id: "##panel", col: ImVec4(0.7f, 0.1f, 0.7f, 1.0f), flags: ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, size: ImVec2(128.0f, 96.0f)); // Dummy item
7772 if (ImGui::IsItemHovered() && capture_override_mouse != -1)
7773 ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1);
7774 if (ImGui::IsItemHovered() && capture_override_keyboard != -1)
7775 ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1);
7776
7777 ImGui::TreePop();
7778 }
7779 ImGui::TreePop();
7780 }
7781
7782 // Demonstrate using Shortcut() and Routing Policies.
7783 // The general flow is:
7784 // - Code interested in a chord (e.g. "Ctrl+A") declares their intent.
7785 // - Multiple locations may be interested in same chord! Routing helps find a winner.
7786 // - Every frame, we resolve all claims and assign one owner if the modifiers are matching.
7787 // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route.
7788 // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags.
7789 // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused.
7790 // TL;DR: Most uses will simply be:
7791 // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy.
7792 IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts");
7793 if (ImGui::TreeNode(label: "Shortcuts"))
7794 {
7795 static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat;
7796 static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused;
7797 ImGui::CheckboxFlags(label: "ImGuiInputFlags_Repeat", flags: &route_options, flags_value: ImGuiInputFlags_Repeat);
7798 ImGui::RadioButton(label: "ImGuiInputFlags_RouteActive", v: &route_type, v_button: ImGuiInputFlags_RouteActive);
7799 ImGui::RadioButton(label: "ImGuiInputFlags_RouteFocused (default)", v: &route_type, v_button: ImGuiInputFlags_RouteFocused);
7800 ImGui::RadioButton(label: "ImGuiInputFlags_RouteGlobal", v: &route_type, v_button: ImGuiInputFlags_RouteGlobal);
7801 ImGui::Indent();
7802 ImGui::BeginDisabled(disabled: route_type != ImGuiInputFlags_RouteGlobal);
7803 ImGui::CheckboxFlags(label: "ImGuiInputFlags_RouteOverFocused", flags: &route_options, flags_value: ImGuiInputFlags_RouteOverFocused);
7804 ImGui::CheckboxFlags(label: "ImGuiInputFlags_RouteOverActive", flags: &route_options, flags_value: ImGuiInputFlags_RouteOverActive);
7805 ImGui::CheckboxFlags(label: "ImGuiInputFlags_RouteUnlessBgFocused", flags: &route_options, flags_value: ImGuiInputFlags_RouteUnlessBgFocused);
7806 ImGui::EndDisabled();
7807 ImGui::Unindent();
7808 ImGui::RadioButton(label: "ImGuiInputFlags_RouteAlways", v: &route_type, v_button: ImGuiInputFlags_RouteAlways);
7809 ImGuiInputFlags flags = route_type | route_options; // Merged flags
7810 if (route_type != ImGuiInputFlags_RouteGlobal)
7811 flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused);
7812
7813 ImGui::SeparatorText(label: "Using SetNextItemShortcut()");
7814 ImGui::Text(fmt: "Ctrl+S");
7815 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_S, flags: flags | ImGuiInputFlags_Tooltip);
7816 ImGui::Button(label: "Save");
7817 ImGui::Text(fmt: "Alt+F");
7818 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Alt | ImGuiKey_F, flags: flags | ImGuiInputFlags_Tooltip);
7819 static float f = 0.5f;
7820 ImGui::SliderFloat(label: "Factor", v: &f, v_min: 0.0f, v_max: 1.0f);
7821
7822 ImGui::SeparatorText(label: "Using Shortcut()");
7823 const float line_height = ImGui::GetTextLineHeightWithSpacing();
7824 const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A;
7825
7826 ImGui::Text(fmt: "Ctrl+A");
7827 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7828
7829 ImGui::PushStyleColor(idx: ImGuiCol_ChildBg, col: ImVec4(1.0f, 0.0f, 1.0f, 0.1f));
7830
7831 ImGui::BeginChild(str_id: "WindowA", size: ImVec2(-FLT_MIN, line_height * 14), child_flags: true);
7832 ImGui::Text(fmt: "Press CTRL+A and see who receives it!");
7833 ImGui::Separator();
7834
7835 // 1: Window polling for CTRL+A
7836 ImGui::Text(fmt: "(in WindowA)");
7837 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7838
7839 // 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active)
7840 // (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h)
7841 //char str[16] = "Press CTRL+A";
7842 //ImGui::Spacing();
7843 //ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
7844 //ImGuiID item_id = ImGui::GetItemID();
7845 //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused");
7846 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "...");
7847
7848 // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA
7849 ImGui::BeginChild(str_id: "ChildD", size: ImVec2(-FLT_MIN, line_height * 4), child_flags: true);
7850 ImGui::Text(fmt: "(in ChildD: not using same Shortcut)");
7851 ImGui::Text(fmt: "IsWindowFocused: %d", ImGui::IsWindowFocused());
7852 ImGui::EndChild();
7853
7854 // 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused.
7855 ImGui::BeginChild(str_id: "ChildE", size: ImVec2(-FLT_MIN, line_height * 4), child_flags: true);
7856 ImGui::Text(fmt: "(in ChildE: using same Shortcut)");
7857 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7858 ImGui::EndChild();
7859
7860 // 5: In a popup
7861 if (ImGui::Button(label: "Open Popup"))
7862 ImGui::OpenPopup(str_id: "PopupF");
7863 if (ImGui::BeginPopup(str_id: "PopupF"))
7864 {
7865 ImGui::Text(fmt: "(in PopupF)");
7866 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7867 // (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h)
7868 //ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
7869 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "...");
7870 ImGui::EndPopup();
7871 }
7872 ImGui::EndChild();
7873 ImGui::PopStyleColor();
7874
7875 ImGui::TreePop();
7876 }
7877
7878 // Display mouse cursors
7879 IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors");
7880 if (ImGui::TreeNode(label: "Mouse Cursors"))
7881 {
7882 const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" };
7883 IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
7884
7885 ImGuiMouseCursor current = ImGui::GetMouseCursor();
7886 const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A";
7887 ImGui::Text(fmt: "Current mouse cursor = %d: %s", current, cursor_name);
7888 ImGui::BeginDisabled(disabled: true);
7889 ImGui::CheckboxFlags(label: "io.BackendFlags: HasMouseCursors", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasMouseCursors);
7890 ImGui::EndDisabled();
7891
7892 ImGui::Text(fmt: "Hover to see mouse cursors:");
7893 ImGui::SameLine(); HelpMarker(
7894 desc: "Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. "
7895 "If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, "
7896 "otherwise your backend needs to handle it.");
7897 for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)
7898 {
7899 char label[32];
7900 sprintf(s: label, format: "Mouse cursor %d: %s", i, mouse_cursors_names[i]);
7901 ImGui::Bullet(); ImGui::Selectable(label, selected: false);
7902 if (ImGui::IsItemHovered())
7903 ImGui::SetMouseCursor(i);
7904 }
7905 ImGui::TreePop();
7906 }
7907
7908 IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing");
7909 if (ImGui::TreeNode(label: "Tabbing"))
7910 {
7911 ImGui::Text(fmt: "Use TAB/SHIFT+TAB to cycle through keyboard editable fields.");
7912 static char buf[32] = "hello";
7913 ImGui::InputText(label: "1", buf, IM_ARRAYSIZE(buf));
7914 ImGui::InputText(label: "2", buf, IM_ARRAYSIZE(buf));
7915 ImGui::InputText(label: "3", buf, IM_ARRAYSIZE(buf));
7916 ImGui::PushItemFlag(option: ImGuiItemFlags_NoTabStop, enabled: true);
7917 ImGui::InputText(label: "4 (tab skip)", buf, IM_ARRAYSIZE(buf));
7918 ImGui::SameLine(); HelpMarker(desc: "Item won't be cycled through when using TAB or Shift+Tab.");
7919 ImGui::PopItemFlag();
7920 ImGui::InputText(label: "5", buf, IM_ARRAYSIZE(buf));
7921 ImGui::TreePop();
7922 }
7923
7924 IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code");
7925 if (ImGui::TreeNode(label: "Focus from code"))
7926 {
7927 bool focus_1 = ImGui::Button(label: "Focus on 1"); ImGui::SameLine();
7928 bool focus_2 = ImGui::Button(label: "Focus on 2"); ImGui::SameLine();
7929 bool focus_3 = ImGui::Button(label: "Focus on 3");
7930 int has_focus = 0;
7931 static char buf[128] = "click on a button to set focus";
7932
7933 if (focus_1) ImGui::SetKeyboardFocusHere();
7934 ImGui::InputText(label: "1", buf, IM_ARRAYSIZE(buf));
7935 if (ImGui::IsItemActive()) has_focus = 1;
7936
7937 if (focus_2) ImGui::SetKeyboardFocusHere();
7938 ImGui::InputText(label: "2", buf, IM_ARRAYSIZE(buf));
7939 if (ImGui::IsItemActive()) has_focus = 2;
7940
7941 ImGui::PushItemFlag(option: ImGuiItemFlags_NoTabStop, enabled: true);
7942 if (focus_3) ImGui::SetKeyboardFocusHere();
7943 ImGui::InputText(label: "3 (tab skip)", buf, IM_ARRAYSIZE(buf));
7944 if (ImGui::IsItemActive()) has_focus = 3;
7945 ImGui::SameLine(); HelpMarker(desc: "Item won't be cycled through when using TAB or Shift+Tab.");
7946 ImGui::PopItemFlag();
7947
7948 if (has_focus)
7949 ImGui::Text(fmt: "Item with focus: %d", has_focus);
7950 else
7951 ImGui::Text(fmt: "Item with focus: <none>");
7952
7953 // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item
7954 static float f3[3] = { 0.0f, 0.0f, 0.0f };
7955 int focus_ahead = -1;
7956 if (ImGui::Button(label: "Focus on X")) { focus_ahead = 0; } ImGui::SameLine();
7957 if (ImGui::Button(label: "Focus on Y")) { focus_ahead = 1; } ImGui::SameLine();
7958 if (ImGui::Button(label: "Focus on Z")) { focus_ahead = 2; }
7959 if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead);
7960 ImGui::SliderFloat3(label: "Float3", v: &f3[0], v_min: 0.0f, v_max: 1.0f);
7961
7962 ImGui::TextWrapped(fmt: "NB: Cursor & selection are preserved when refocusing last used item in code.");
7963 ImGui::TreePop();
7964 }
7965
7966 IMGUI_DEMO_MARKER("Inputs & Focus/Dragging");
7967 if (ImGui::TreeNode(label: "Dragging"))
7968 {
7969 ImGui::TextWrapped(fmt: "You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");
7970 for (int button = 0; button < 3; button++)
7971 {
7972 ImGui::Text(fmt: "IsMouseDragging(%d):", button);
7973 ImGui::Text(fmt: " w/ default threshold: %d,", ImGui::IsMouseDragging(button));
7974 ImGui::Text(fmt: " w/ zero threshold: %d,", ImGui::IsMouseDragging(button, lock_threshold: 0.0f));
7975 ImGui::Text(fmt: " w/ large threshold: %d,", ImGui::IsMouseDragging(button, lock_threshold: 20.0f));
7976 }
7977
7978 ImGui::Button(label: "Drag Me");
7979 if (ImGui::IsItemActive())
7980 ImGui::GetForegroundDrawList()->AddLine(p1: io.MouseClickedPos[0], p2: io.MousePos, col: ImGui::GetColorU32(idx: ImGuiCol_Button), thickness: 4.0f); // Draw a line between the button and the mouse cursor
7981
7982 // Drag operations gets "unlocked" when the mouse has moved past a certain threshold
7983 // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
7984 // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
7985 ImVec2 value_raw = ImGui::GetMouseDragDelta(button: 0, lock_threshold: 0.0f);
7986 ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(button: 0);
7987 ImVec2 mouse_delta = io.MouseDelta;
7988 ImGui::Text(fmt: "GetMouseDragDelta(0):");
7989 ImGui::Text(fmt: " w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y);
7990 ImGui::Text(fmt: " w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y);
7991 ImGui::Text(fmt: "io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y);
7992 ImGui::TreePop();
7993 }
7994 }
7995}
7996
7997//-----------------------------------------------------------------------------
7998// [SECTION] About Window / ShowAboutWindow()
7999// Access from Dear ImGui Demo -> Tools -> About
8000//-----------------------------------------------------------------------------
8001
8002void ImGui::ShowAboutWindow(bool* p_open)
8003{
8004 if (!ImGui::Begin(name: "About Dear ImGui", p_open, flags: ImGuiWindowFlags_AlwaysAutoResize))
8005 {
8006 ImGui::End();
8007 return;
8008 }
8009 IMGUI_DEMO_MARKER("Tools/About Dear ImGui");
8010 ImGui::Text(fmt: "Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8011
8012 ImGui::TextLinkOpenURL(label: "Homepage", url: "https://github.com/ocornut/imgui");
8013 ImGui::SameLine();
8014 ImGui::TextLinkOpenURL(label: "FAQ", url: "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md");
8015 ImGui::SameLine();
8016 ImGui::TextLinkOpenURL(label: "Wiki", url: "https://github.com/ocornut/imgui/wiki");
8017 ImGui::SameLine();
8018 ImGui::TextLinkOpenURL(label: "Releases", url: "https://github.com/ocornut/imgui/releases");
8019 ImGui::SameLine();
8020 ImGui::TextLinkOpenURL(label: "Funding", url: "https://github.com/ocornut/imgui/wiki/Funding");
8021
8022 ImGui::Separator();
8023 ImGui::Text(fmt: "(c) 2014-2025 Omar Cornut");
8024 ImGui::Text(fmt: "Developed by Omar Cornut and all Dear ImGui contributors.");
8025 ImGui::Text(fmt: "Dear ImGui is licensed under the MIT License, see LICENSE for more information.");
8026 ImGui::Text(fmt: "If your company uses this, please consider funding the project.");
8027
8028 static bool show_config_info = false;
8029 ImGui::Checkbox(label: "Config/Build Information", v: &show_config_info);
8030 if (show_config_info)
8031 {
8032 ImGuiIO& io = ImGui::GetIO();
8033 ImGuiStyle& style = ImGui::GetStyle();
8034
8035 bool copy_to_clipboard = ImGui::Button(label: "Copy to clipboard");
8036 ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);
8037 ImGui::BeginChild(id: ImGui::GetID(str_id: "cfg_infos"), size: child_size, child_flags: ImGuiChildFlags_FrameStyle);
8038 if (copy_to_clipboard)
8039 {
8040 ImGui::LogToClipboard();
8041 ImGui::LogText(fmt: "```\n"); // Back quotes will make text appears without formatting when pasting on GitHub
8042 }
8043
8044 ImGui::Text(fmt: "Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8045 ImGui::Separator();
8046 ImGui::Text(fmt: "sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));
8047 ImGui::Text(fmt: "define: __cplusplus=%d", (int)__cplusplus);
8048#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8049 ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
8050#endif
8051#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
8052 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
8053#endif
8054#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
8055 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS");
8056#endif
8057#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS
8058 ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS");
8059#endif
8060#ifdef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
8061 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS");
8062#endif
8063#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
8064 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS");
8065#endif
8066#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
8067 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS");
8068#endif
8069#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
8070 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS");
8071#endif
8072#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
8073 ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS");
8074#endif
8075#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS
8076 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS");
8077#endif
8078#ifdef IMGUI_USE_BGRA_PACKED_COLOR
8079 ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR");
8080#endif
8081#ifdef _WIN32
8082 ImGui::Text("define: _WIN32");
8083#endif
8084#ifdef _WIN64
8085 ImGui::Text("define: _WIN64");
8086#endif
8087#ifdef __linux__
8088 ImGui::Text(fmt: "define: __linux__");
8089#endif
8090#ifdef __APPLE__
8091 ImGui::Text("define: __APPLE__");
8092#endif
8093#ifdef _MSC_VER
8094 ImGui::Text("define: _MSC_VER=%d", _MSC_VER);
8095#endif
8096#ifdef _MSVC_LANG
8097 ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG);
8098#endif
8099#ifdef __MINGW32__
8100 ImGui::Text("define: __MINGW32__");
8101#endif
8102#ifdef __MINGW64__
8103 ImGui::Text("define: __MINGW64__");
8104#endif
8105#ifdef __GNUC__
8106 ImGui::Text(fmt: "define: __GNUC__=%d", (int)__GNUC__);
8107#endif
8108#ifdef __clang_version__
8109 ImGui::Text(fmt: "define: __clang_version__=%s", __clang_version__);
8110#endif
8111#ifdef __EMSCRIPTEN__
8112 ImGui::Text("define: __EMSCRIPTEN__");
8113 ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
8114#endif
8115#ifdef IMGUI_HAS_VIEWPORT
8116 ImGui::Text(fmt: "define: IMGUI_HAS_VIEWPORT");
8117#endif
8118#ifdef IMGUI_HAS_DOCK
8119 ImGui::Text(fmt: "define: IMGUI_HAS_DOCK");
8120#endif
8121 ImGui::Separator();
8122 ImGui::Text(fmt: "io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
8123 ImGui::Text(fmt: "io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
8124 ImGui::Text(fmt: "io.ConfigFlags: 0x%08X", io.ConfigFlags);
8125 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(fmt: " NavEnableKeyboard");
8126 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(fmt: " NavEnableGamepad");
8127 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(fmt: " NoMouse");
8128 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(fmt: " NoMouseCursorChange");
8129 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) ImGui::Text(fmt: " NoKeyboard");
8130 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) ImGui::Text(fmt: " DockingEnable");
8131 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(fmt: " ViewportsEnable");
8132 if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) ImGui::Text(fmt: " DpiEnableScaleViewports");
8133 if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ImGui::Text(fmt: " DpiEnableScaleFonts");
8134 if (io.MouseDrawCursor) ImGui::Text(fmt: "io.MouseDrawCursor");
8135 if (io.ConfigViewportsNoAutoMerge) ImGui::Text(fmt: "io.ConfigViewportsNoAutoMerge");
8136 if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text(fmt: "io.ConfigViewportsNoTaskBarIcon");
8137 if (io.ConfigViewportsNoDecoration) ImGui::Text(fmt: "io.ConfigViewportsNoDecoration");
8138 if (io.ConfigViewportsNoDefaultParent) ImGui::Text(fmt: "io.ConfigViewportsNoDefaultParent");
8139 if (io.ConfigDockingNoSplit) ImGui::Text(fmt: "io.ConfigDockingNoSplit");
8140 if (io.ConfigDockingWithShift) ImGui::Text(fmt: "io.ConfigDockingWithShift");
8141 if (io.ConfigDockingAlwaysTabBar) ImGui::Text(fmt: "io.ConfigDockingAlwaysTabBar");
8142 if (io.ConfigDockingTransparentPayload) ImGui::Text(fmt: "io.ConfigDockingTransparentPayload");
8143 if (io.ConfigMacOSXBehaviors) ImGui::Text(fmt: "io.ConfigMacOSXBehaviors");
8144 if (io.ConfigNavMoveSetMousePos) ImGui::Text(fmt: "io.ConfigNavMoveSetMousePos");
8145 if (io.ConfigNavCaptureKeyboard) ImGui::Text(fmt: "io.ConfigNavCaptureKeyboard");
8146 if (io.ConfigInputTextCursorBlink) ImGui::Text(fmt: "io.ConfigInputTextCursorBlink");
8147 if (io.ConfigWindowsResizeFromEdges) ImGui::Text(fmt: "io.ConfigWindowsResizeFromEdges");
8148 if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text(fmt: "io.ConfigWindowsMoveFromTitleBarOnly");
8149 if (io.ConfigMemoryCompactTimer >= 0.0f) ImGui::Text(fmt: "io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer);
8150 ImGui::Text(fmt: "io.BackendFlags: 0x%08X", io.BackendFlags);
8151 if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(fmt: " HasGamepad");
8152 if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(fmt: " HasMouseCursors");
8153 if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(fmt: " HasSetMousePos");
8154 if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) ImGui::Text(fmt: " PlatformHasViewports");
8155 if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(fmt: " HasMouseHoveredViewport");
8156 if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(fmt: " RendererHasVtxOffset");
8157 if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports) ImGui::Text(fmt: " RendererHasViewports");
8158 ImGui::Separator();
8159 ImGui::Text(fmt: "io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight);
8160 ImGui::Text(fmt: "io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
8161 ImGui::Text(fmt: "io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
8162 ImGui::Separator();
8163 ImGui::Text(fmt: "style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y);
8164 ImGui::Text(fmt: "style.WindowBorderSize: %.2f", style.WindowBorderSize);
8165 ImGui::Text(fmt: "style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y);
8166 ImGui::Text(fmt: "style.FrameRounding: %.2f", style.FrameRounding);
8167 ImGui::Text(fmt: "style.FrameBorderSize: %.2f", style.FrameBorderSize);
8168 ImGui::Text(fmt: "style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y);
8169 ImGui::Text(fmt: "style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);
8170
8171 if (copy_to_clipboard)
8172 {
8173 ImGui::LogText(fmt: "\n```\n");
8174 ImGui::LogFinish();
8175 }
8176 ImGui::EndChild();
8177 }
8178 ImGui::End();
8179}
8180
8181//-----------------------------------------------------------------------------
8182// [SECTION] Style Editor / ShowStyleEditor()
8183//-----------------------------------------------------------------------------
8184// - ShowFontSelector()
8185// - ShowStyleSelector()
8186// - ShowStyleEditor()
8187//-----------------------------------------------------------------------------
8188
8189// Forward declare ShowFontAtlas() which isn't worth putting in public API yet
8190namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }
8191
8192// Demo helper function to select among loaded fonts.
8193// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.
8194void ImGui::ShowFontSelector(const char* label)
8195{
8196 ImGuiIO& io = ImGui::GetIO();
8197 ImFont* font_current = ImGui::GetFont();
8198 if (ImGui::BeginCombo(label, preview_value: font_current->GetDebugName()))
8199 {
8200 for (ImFont* font : io.Fonts->Fonts)
8201 {
8202 ImGui::PushID(ptr_id: (void*)font);
8203 if (ImGui::Selectable(label: font->GetDebugName(), selected: font == font_current))
8204 io.FontDefault = font;
8205 if (font == font_current)
8206 ImGui::SetItemDefaultFocus();
8207 ImGui::PopID();
8208 }
8209 ImGui::EndCombo();
8210 }
8211 ImGui::SameLine();
8212 HelpMarker(
8213 desc: "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n"
8214 "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
8215 "- Read FAQ and docs/FONTS.md for more details.\n"
8216 "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
8217}
8218
8219// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
8220// Here we use the simplified Combo() api that packs items into a single literal string.
8221// Useful for quick combo boxes where the choices are known locally.
8222bool ImGui::ShowStyleSelector(const char* label)
8223{
8224 static int style_idx = -1;
8225 if (ImGui::Combo(label, current_item: &style_idx, items_separated_by_zeros: "Dark\0Light\0Classic\0"))
8226 {
8227 switch (style_idx)
8228 {
8229 case 0: ImGui::StyleColorsDark(); break;
8230 case 1: ImGui::StyleColorsLight(); break;
8231 case 2: ImGui::StyleColorsClassic(); break;
8232 }
8233 return true;
8234 }
8235 return false;
8236}
8237
8238void ImGui::ShowStyleEditor(ImGuiStyle* ref)
8239{
8240 IMGUI_DEMO_MARKER("Tools/Style Editor");
8241 // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
8242 // (without a reference style pointer, we will use one compared locally as a reference)
8243 ImGuiStyle& style = ImGui::GetStyle();
8244 static ImGuiStyle ref_saved_style;
8245
8246 // Default to using internal storage as reference
8247 static bool init = true;
8248 if (init && ref == NULL)
8249 ref_saved_style = style;
8250 init = false;
8251 if (ref == NULL)
8252 ref = &ref_saved_style;
8253
8254 ImGui::PushItemWidth(item_width: ImGui::GetWindowWidth() * 0.50f);
8255
8256 if (ImGui::ShowStyleSelector(label: "Colors##Selector"))
8257 ref_saved_style = style;
8258 ImGui::ShowFontSelector(label: "Fonts##Selector");
8259
8260 // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
8261 if (ImGui::SliderFloat(label: "FrameRounding", v: &style.FrameRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f"))
8262 style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
8263 { bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox(label: "WindowBorder", v: &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
8264 ImGui::SameLine();
8265 { bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox(label: "FrameBorder", v: &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
8266 ImGui::SameLine();
8267 { bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox(label: "PopupBorder", v: &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
8268
8269 // Save/Revert button
8270 if (ImGui::Button(label: "Save Ref"))
8271 *ref = ref_saved_style = style;
8272 ImGui::SameLine();
8273 if (ImGui::Button(label: "Revert Ref"))
8274 style = *ref;
8275 ImGui::SameLine();
8276 HelpMarker(
8277 desc: "Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
8278 "Use \"Export\" below to save them somewhere.");
8279
8280 ImGui::Separator();
8281
8282 if (ImGui::BeginTabBar(str_id: "##tabs", flags: ImGuiTabBarFlags_None))
8283 {
8284 if (ImGui::BeginTabItem(label: "Sizes"))
8285 {
8286 ImGui::SeparatorText(label: "Main");
8287 ImGui::SliderFloat2(label: "WindowPadding", v: (float*)&style.WindowPadding, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8288 ImGui::SliderFloat2(label: "FramePadding", v: (float*)&style.FramePadding, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8289 ImGui::SliderFloat2(label: "ItemSpacing", v: (float*)&style.ItemSpacing, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8290 ImGui::SliderFloat2(label: "ItemInnerSpacing", v: (float*)&style.ItemInnerSpacing, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8291 ImGui::SliderFloat2(label: "TouchExtraPadding", v: (float*)&style.TouchExtraPadding, v_min: 0.0f, v_max: 10.0f, format: "%.0f");
8292 ImGui::SliderFloat(label: "IndentSpacing", v: &style.IndentSpacing, v_min: 0.0f, v_max: 30.0f, format: "%.0f");
8293 ImGui::SliderFloat(label: "ScrollbarSize", v: &style.ScrollbarSize, v_min: 1.0f, v_max: 20.0f, format: "%.0f");
8294 ImGui::SliderFloat(label: "GrabMinSize", v: &style.GrabMinSize, v_min: 1.0f, v_max: 20.0f, format: "%.0f");
8295
8296 ImGui::SeparatorText(label: "Borders");
8297 ImGui::SliderFloat(label: "WindowBorderSize", v: &style.WindowBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8298 ImGui::SliderFloat(label: "ChildBorderSize", v: &style.ChildBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8299 ImGui::SliderFloat(label: "PopupBorderSize", v: &style.PopupBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8300 ImGui::SliderFloat(label: "FrameBorderSize", v: &style.FrameBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8301
8302 ImGui::SeparatorText(label: "Rounding");
8303 ImGui::SliderFloat(label: "WindowRounding", v: &style.WindowRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8304 ImGui::SliderFloat(label: "ChildRounding", v: &style.ChildRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8305 ImGui::SliderFloat(label: "FrameRounding", v: &style.FrameRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8306 ImGui::SliderFloat(label: "PopupRounding", v: &style.PopupRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8307 ImGui::SliderFloat(label: "ScrollbarRounding", v: &style.ScrollbarRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8308 ImGui::SliderFloat(label: "GrabRounding", v: &style.GrabRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8309
8310 ImGui::SeparatorText(label: "Tabs");
8311 ImGui::SliderFloat(label: "TabBorderSize", v: &style.TabBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8312 ImGui::SliderFloat(label: "TabBarBorderSize", v: &style.TabBarBorderSize, v_min: 0.0f, v_max: 2.0f, format: "%.0f");
8313 ImGui::SliderFloat(label: "TabBarOverlineSize", v: &style.TabBarOverlineSize, v_min: 0.0f, v_max: 3.0f, format: "%.0f");
8314 ImGui::SameLine(); HelpMarker(desc: "Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
8315 ImGui::DragFloat(label: "TabCloseButtonMinWidthSelected", v: &style.TabCloseButtonMinWidthSelected, v_speed: 0.1f, v_min: -1.0f, v_max: 100.0f, format: (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
8316 ImGui::DragFloat(label: "TabCloseButtonMinWidthUnselected", v: &style.TabCloseButtonMinWidthUnselected, v_speed: 0.1f, v_min: -1.0f, v_max: 100.0f, format: (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
8317 ImGui::SliderFloat(label: "TabRounding", v: &style.TabRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8318
8319 ImGui::SeparatorText(label: "Tables");
8320 ImGui::SliderFloat2(label: "CellPadding", v: (float*)&style.CellPadding, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8321 ImGui::SliderAngle(label: "TableAngledHeadersAngle", v_rad: &style.TableAngledHeadersAngle, v_degrees_min: -50.0f, v_degrees_max: +50.0f);
8322 ImGui::SliderFloat2(label: "TableAngledHeadersTextAlign", v: (float*)&style.TableAngledHeadersTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8323
8324 ImGui::SeparatorText(label: "Windows");
8325 ImGui::SliderFloat2(label: "WindowTitleAlign", v: (float*)&style.WindowTitleAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8326 ImGui::SliderFloat(label: "WindowBorderHoverPadding", v: &style.WindowBorderHoverPadding, v_min: 1.0f, v_max: 20.0f, format: "%.0f");
8327 int window_menu_button_position = style.WindowMenuButtonPosition + 1;
8328 if (ImGui::Combo(label: "WindowMenuButtonPosition", current_item: (int*)&window_menu_button_position, items_separated_by_zeros: "None\0Left\0Right\0"))
8329 style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);
8330
8331 ImGui::SeparatorText(label: "Widgets");
8332 ImGui::Combo(label: "ColorButtonPosition", current_item: (int*)&style.ColorButtonPosition, items_separated_by_zeros: "Left\0Right\0");
8333 ImGui::SliderFloat2(label: "ButtonTextAlign", v: (float*)&style.ButtonTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8334 ImGui::SameLine(); HelpMarker(desc: "Alignment applies when a button is larger than its text content.");
8335 ImGui::SliderFloat2(label: "SelectableTextAlign", v: (float*)&style.SelectableTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8336 ImGui::SameLine(); HelpMarker(desc: "Alignment applies when a selectable is larger than its text content.");
8337 ImGui::SliderFloat(label: "SeparatorTextBorderSize", v: &style.SeparatorTextBorderSize, v_min: 0.0f, v_max: 10.0f, format: "%.0f");
8338 ImGui::SliderFloat2(label: "SeparatorTextAlign", v: (float*)&style.SeparatorTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8339 ImGui::SliderFloat2(label: "SeparatorTextPadding", v: (float*)&style.SeparatorTextPadding, v_min: 0.0f, v_max: 40.0f, format: "%.0f");
8340 ImGui::SliderFloat(label: "LogSliderDeadzone", v: &style.LogSliderDeadzone, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8341 ImGui::SliderFloat(label: "ImageBorderSize", v: &style.ImageBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8342
8343 ImGui::SeparatorText(label: "Docking");
8344 ImGui::SliderFloat(label: "DockingSplitterSize", v: &style.DockingSeparatorSize, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8345
8346 ImGui::SeparatorText(label: "Tooltips");
8347 for (int n = 0; n < 2; n++)
8348 if (ImGui::TreeNodeEx(label: n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav"))
8349 {
8350 ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;
8351 ImGui::CheckboxFlags(label: "ImGuiHoveredFlags_DelayNone", flags: p, flags_value: ImGuiHoveredFlags_DelayNone);
8352 ImGui::CheckboxFlags(label: "ImGuiHoveredFlags_DelayShort", flags: p, flags_value: ImGuiHoveredFlags_DelayShort);
8353 ImGui::CheckboxFlags(label: "ImGuiHoveredFlags_DelayNormal", flags: p, flags_value: ImGuiHoveredFlags_DelayNormal);
8354 ImGui::CheckboxFlags(label: "ImGuiHoveredFlags_Stationary", flags: p, flags_value: ImGuiHoveredFlags_Stationary);
8355 ImGui::CheckboxFlags(label: "ImGuiHoveredFlags_NoSharedDelay", flags: p, flags_value: ImGuiHoveredFlags_NoSharedDelay);
8356 ImGui::TreePop();
8357 }
8358
8359 ImGui::SeparatorText(label: "Misc");
8360 ImGui::SliderFloat2(label: "DisplayWindowPadding", v: (float*)&style.DisplayWindowPadding, v_min: 0.0f, v_max: 30.0f, format: "%.0f"); ImGui::SameLine(); HelpMarker(desc: "Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.");
8361 ImGui::SliderFloat2(label: "DisplaySafeAreaPadding", v: (float*)&style.DisplaySafeAreaPadding, v_min: 0.0f, v_max: 30.0f, format: "%.0f"); ImGui::SameLine(); HelpMarker(desc: "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).");
8362
8363 ImGui::EndTabItem();
8364 }
8365
8366 if (ImGui::BeginTabItem(label: "Colors"))
8367 {
8368 static int output_dest = 0;
8369 static bool output_only_modified = true;
8370 if (ImGui::Button(label: "Export"))
8371 {
8372 if (output_dest == 0)
8373 ImGui::LogToClipboard();
8374 else
8375 ImGui::LogToTTY();
8376 ImGui::LogText(fmt: "ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE);
8377 for (int i = 0; i < ImGuiCol_COUNT; i++)
8378 {
8379 const ImVec4& col = style.Colors[i];
8380 const char* name = ImGui::GetStyleColorName(idx: i);
8381 if (!output_only_modified || memcmp(s1: &col, s2: &ref->Colors[i], n: sizeof(ImVec4)) != 0)
8382 ImGui::LogText(fmt: "colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
8383 name, 23 - (int)strlen(s: name), "", col.x, col.y, col.z, col.w);
8384 }
8385 ImGui::LogFinish();
8386 }
8387 ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo(label: "##output_type", current_item: &output_dest, items_separated_by_zeros: "To Clipboard\0To TTY\0");
8388 ImGui::SameLine(); ImGui::Checkbox(label: "Only Modified Colors", v: &output_only_modified);
8389
8390 static ImGuiTextFilter filter;
8391 filter.Draw(label: "Filter colors", width: ImGui::GetFontSize() * 16);
8392
8393 static ImGuiColorEditFlags alpha_flags = 0;
8394 if (ImGui::RadioButton(label: "Opaque", active: alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine();
8395 if (ImGui::RadioButton(label: "Alpha", active: alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
8396 if (ImGui::RadioButton(label: "Both", active: alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();
8397 HelpMarker(
8398 desc: "In the color list:\n"
8399 "Left-click on color square to open color picker,\n"
8400 "Right-click to open edit options menu.");
8401
8402 ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), size_max: ImVec2(FLT_MAX, FLT_MAX));
8403 ImGui::BeginChild(str_id: "##colors", size: ImVec2(0, 0), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, window_flags: ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
8404 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * -12);
8405 for (int i = 0; i < ImGuiCol_COUNT; i++)
8406 {
8407 const char* name = ImGui::GetStyleColorName(idx: i);
8408 if (!filter.PassFilter(text: name))
8409 continue;
8410 ImGui::PushID(int_id: i);
8411#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8412 if (ImGui::Button(label: "?"))
8413 ImGui::DebugFlashStyleColor(idx: (ImGuiCol)i);
8414 ImGui::SetItemTooltip("Flash given color to identify places where it is used.");
8415 ImGui::SameLine();
8416#endif
8417 ImGui::ColorEdit4(label: "##color", col: (float*)&style.Colors[i], flags: ImGuiColorEditFlags_AlphaBar | alpha_flags);
8418 if (memcmp(s1: &style.Colors[i], s2: &ref->Colors[i], n: sizeof(ImVec4)) != 0)
8419 {
8420 // Tips: in a real user application, you may want to merge and use an icon font into the main font,
8421 // so instead of "Save"/"Revert" you'd use icons!
8422 // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
8423 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: style.ItemInnerSpacing.x); if (ImGui::Button(label: "Save")) { ref->Colors[i] = style.Colors[i]; }
8424 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: style.ItemInnerSpacing.x); if (ImGui::Button(label: "Revert")) { style.Colors[i] = ref->Colors[i]; }
8425 }
8426 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: style.ItemInnerSpacing.x);
8427 ImGui::TextUnformatted(text: name);
8428 ImGui::PopID();
8429 }
8430 ImGui::PopItemWidth();
8431 ImGui::EndChild();
8432
8433 ImGui::EndTabItem();
8434 }
8435
8436 if (ImGui::BeginTabItem(label: "Fonts"))
8437 {
8438 ImGuiIO& io = ImGui::GetIO();
8439 ImFontAtlas* atlas = io.Fonts;
8440 HelpMarker(desc: "Read FAQ and docs/FONTS.md for details on font loading.");
8441 ImGui::ShowFontAtlas(atlas);
8442
8443 // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
8444 // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
8445 const float MIN_SCALE = 0.3f;
8446 const float MAX_SCALE = 2.0f;
8447 HelpMarker(
8448 desc: "Those are old settings provided for convenience.\n"
8449 "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
8450 "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
8451 "Using those settings here will give you poor quality results.");
8452 static float window_scale = 1.0f;
8453 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * 8);
8454 if (ImGui::DragFloat(label: "window scale", v: &window_scale, v_speed: 0.005f, v_min: MIN_SCALE, v_max: MAX_SCALE, format: "%.2f", flags: ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
8455 ImGui::SetWindowFontScale(window_scale);
8456 ImGui::DragFloat(label: "global scale", v: &io.FontGlobalScale, v_speed: 0.005f, v_min: MIN_SCALE, v_max: MAX_SCALE, format: "%.2f", flags: ImGuiSliderFlags_AlwaysClamp); // Scale everything
8457 ImGui::PopItemWidth();
8458
8459 ImGui::EndTabItem();
8460 }
8461
8462 if (ImGui::BeginTabItem(label: "Rendering"))
8463 {
8464 ImGui::Checkbox(label: "Anti-aliased lines", v: &style.AntiAliasedLines);
8465 ImGui::SameLine();
8466 HelpMarker(desc: "When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
8467
8468 ImGui::Checkbox(label: "Anti-aliased lines use texture", v: &style.AntiAliasedLinesUseTex);
8469 ImGui::SameLine();
8470 HelpMarker(desc: "Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
8471
8472 ImGui::Checkbox(label: "Anti-aliased fill", v: &style.AntiAliasedFill);
8473 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * 8);
8474 ImGui::DragFloat(label: "Curve Tessellation Tolerance", v: &style.CurveTessellationTol, v_speed: 0.02f, v_min: 0.10f, v_max: 10.0f, format: "%.2f");
8475 if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
8476
8477 // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
8478 ImGui::DragFloat(label: "Circle Tessellation Max Error", v: &style.CircleTessellationMaxError , v_speed: 0.005f, v_min: 0.10f, v_max: 5.0f, format: "%.2f", flags: ImGuiSliderFlags_AlwaysClamp);
8479 const bool show_samples = ImGui::IsItemActive();
8480 if (show_samples)
8481 ImGui::SetNextWindowPos(pos: ImGui::GetCursorScreenPos());
8482 if (show_samples && ImGui::BeginTooltip())
8483 {
8484 ImGui::TextUnformatted(text: "(R = radius, N = approx number of segments)");
8485 ImGui::Spacing();
8486 ImDrawList* draw_list = ImGui::GetWindowDrawList();
8487 const float min_widget_width = ImGui::CalcTextSize(text: "R: MMM\nN: MMM").x;
8488 for (int n = 0; n < 8; n++)
8489 {
8490 const float RAD_MIN = 5.0f;
8491 const float RAD_MAX = 70.0f;
8492 const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
8493
8494 ImGui::BeginGroup();
8495
8496 // N is not always exact here due to how PathArcTo() function work internally
8497 ImGui::Text(fmt: "R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(radius: rad));
8498
8499 const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
8500 const float offset_x = floorf(x: canvas_width * 0.5f);
8501 const float offset_y = floorf(x: RAD_MAX);
8502
8503 const ImVec2 p1 = ImGui::GetCursorScreenPos();
8504 draw_list->AddCircle(center: ImVec2(p1.x + offset_x, p1.y + offset_y), radius: rad, col: ImGui::GetColorU32(idx: ImGuiCol_Text));
8505 ImGui::Dummy(size: ImVec2(canvas_width, RAD_MAX * 2));
8506
8507 /*
8508 const ImVec2 p2 = ImGui::GetCursorScreenPos();
8509 draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));
8510 ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8511 */
8512
8513 ImGui::EndGroup();
8514 ImGui::SameLine();
8515 }
8516 ImGui::EndTooltip();
8517 }
8518 ImGui::SameLine();
8519 HelpMarker(desc: "When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically.");
8520
8521 ImGui::DragFloat(label: "Global Alpha", v: &style.Alpha, v_speed: 0.005f, v_min: 0.20f, v_max: 1.0f, format: "%.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.
8522 ImGui::DragFloat(label: "Disabled Alpha", v: &style.DisabledAlpha, v_speed: 0.005f, v_min: 0.0f, v_max: 1.0f, format: "%.2f"); ImGui::SameLine(); HelpMarker(desc: "Additional alpha multiplier for disabled items (multiply over current value of Alpha).");
8523 ImGui::PopItemWidth();
8524
8525 ImGui::EndTabItem();
8526 }
8527
8528 ImGui::EndTabBar();
8529 }
8530
8531 ImGui::PopItemWidth();
8532}
8533
8534//-----------------------------------------------------------------------------
8535// [SECTION] User Guide / ShowUserGuide()
8536//-----------------------------------------------------------------------------
8537
8538void ImGui::ShowUserGuide()
8539{
8540 ImGuiIO& io = ImGui::GetIO();
8541 ImGui::BulletText(fmt: "Double-click on title bar to collapse window.");
8542 ImGui::BulletText(
8543 fmt: "Click and drag on lower corner to resize window\n"
8544 "(double-click to auto fit window to its contents).");
8545 ImGui::BulletText(fmt: "CTRL+Click on a slider or drag box to input value as text.");
8546 ImGui::BulletText(fmt: "TAB/SHIFT+TAB to cycle through keyboard editable fields.");
8547 ImGui::BulletText(fmt: "CTRL+Tab to select a window.");
8548 if (io.FontAllowUserScaling)
8549 ImGui::BulletText(fmt: "CTRL+Mouse Wheel to zoom window contents.");
8550 ImGui::BulletText(fmt: "While inputting text:\n");
8551 ImGui::Indent();
8552 ImGui::BulletText(fmt: "CTRL+Left/Right to word jump.");
8553 ImGui::BulletText(fmt: "CTRL+A or double-click to select all.");
8554 ImGui::BulletText(fmt: "CTRL+X/C/V to use clipboard cut/copy/paste.");
8555 ImGui::BulletText(fmt: "CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.");
8556 ImGui::BulletText(fmt: "ESCAPE to revert.");
8557 ImGui::Unindent();
8558 ImGui::BulletText(fmt: "With keyboard navigation enabled:");
8559 ImGui::Indent();
8560 ImGui::BulletText(fmt: "Arrow keys to navigate.");
8561 ImGui::BulletText(fmt: "Space to activate a widget.");
8562 ImGui::BulletText(fmt: "Return to input text into a widget.");
8563 ImGui::BulletText(fmt: "Escape to deactivate a widget, close popup, exit child window.");
8564 ImGui::BulletText(fmt: "Alt to jump to the menu layer of a window.");
8565 ImGui::Unindent();
8566}
8567
8568//-----------------------------------------------------------------------------
8569// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
8570//-----------------------------------------------------------------------------
8571// - ShowExampleAppMainMenuBar()
8572// - ShowExampleMenuFile()
8573//-----------------------------------------------------------------------------
8574
8575// Demonstrate creating a "main" fullscreen menu bar and populating it.
8576// Note the difference between BeginMainMenuBar() and BeginMenuBar():
8577// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)
8578// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.
8579static void ShowExampleAppMainMenuBar()
8580{
8581 if (ImGui::BeginMainMenuBar())
8582 {
8583 if (ImGui::BeginMenu(label: "File"))
8584 {
8585 ShowExampleMenuFile();
8586 ImGui::EndMenu();
8587 }
8588 if (ImGui::BeginMenu(label: "Edit"))
8589 {
8590 if (ImGui::MenuItem(label: "Undo", shortcut: "CTRL+Z")) {}
8591 if (ImGui::MenuItem(label: "Redo", shortcut: "CTRL+Y", selected: false, enabled: false)) {} // Disabled item
8592 ImGui::Separator();
8593 if (ImGui::MenuItem(label: "Cut", shortcut: "CTRL+X")) {}
8594 if (ImGui::MenuItem(label: "Copy", shortcut: "CTRL+C")) {}
8595 if (ImGui::MenuItem(label: "Paste", shortcut: "CTRL+V")) {}
8596 ImGui::EndMenu();
8597 }
8598 ImGui::EndMainMenuBar();
8599 }
8600}
8601
8602// Note that shortcuts are currently provided for display only
8603// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
8604static void ShowExampleMenuFile()
8605{
8606 IMGUI_DEMO_MARKER("Examples/Menu");
8607 ImGui::MenuItem(label: "(demo menu)", NULL, selected: false, enabled: false);
8608 if (ImGui::MenuItem(label: "New")) {}
8609 if (ImGui::MenuItem(label: "Open", shortcut: "Ctrl+O")) {}
8610 if (ImGui::BeginMenu(label: "Open Recent"))
8611 {
8612 ImGui::MenuItem(label: "fish_hat.c");
8613 ImGui::MenuItem(label: "fish_hat.inl");
8614 ImGui::MenuItem(label: "fish_hat.h");
8615 if (ImGui::BeginMenu(label: "More.."))
8616 {
8617 ImGui::MenuItem(label: "Hello");
8618 ImGui::MenuItem(label: "Sailor");
8619 if (ImGui::BeginMenu(label: "Recurse.."))
8620 {
8621 ShowExampleMenuFile();
8622 ImGui::EndMenu();
8623 }
8624 ImGui::EndMenu();
8625 }
8626 ImGui::EndMenu();
8627 }
8628 if (ImGui::MenuItem(label: "Save", shortcut: "Ctrl+S")) {}
8629 if (ImGui::MenuItem(label: "Save As..")) {}
8630
8631 ImGui::Separator();
8632 IMGUI_DEMO_MARKER("Examples/Menu/Options");
8633 if (ImGui::BeginMenu(label: "Options"))
8634 {
8635 static bool enabled = true;
8636 ImGui::MenuItem(label: "Enabled", shortcut: "", p_selected: &enabled);
8637 ImGui::BeginChild(str_id: "child", size: ImVec2(0, 60), child_flags: ImGuiChildFlags_Borders);
8638 for (int i = 0; i < 10; i++)
8639 ImGui::Text(fmt: "Scrolling Text %d", i);
8640 ImGui::EndChild();
8641 static float f = 0.5f;
8642 static int n = 0;
8643 ImGui::SliderFloat(label: "Value", v: &f, v_min: 0.0f, v_max: 1.0f);
8644 ImGui::InputFloat(label: "Input", v: &f, step: 0.1f);
8645 ImGui::Combo(label: "Combo", current_item: &n, items_separated_by_zeros: "Yes\0No\0Maybe\0\0");
8646 ImGui::EndMenu();
8647 }
8648
8649 IMGUI_DEMO_MARKER("Examples/Menu/Colors");
8650 if (ImGui::BeginMenu(label: "Colors"))
8651 {
8652 float sz = ImGui::GetTextLineHeight();
8653 for (int i = 0; i < ImGuiCol_COUNT; i++)
8654 {
8655 const char* name = ImGui::GetStyleColorName(idx: (ImGuiCol)i);
8656 ImVec2 p = ImGui::GetCursorScreenPos();
8657 ImGui::GetWindowDrawList()->AddRectFilled(p_min: p, p_max: ImVec2(p.x + sz, p.y + sz), col: ImGui::GetColorU32(idx: (ImGuiCol)i));
8658 ImGui::Dummy(size: ImVec2(sz, sz));
8659 ImGui::SameLine();
8660 ImGui::MenuItem(label: name);
8661 }
8662 ImGui::EndMenu();
8663 }
8664
8665 // Here we demonstrate appending again to the "Options" menu (which we already created above)
8666 // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice.
8667 // In a real code-base using it would make senses to use this feature from very different code locations.
8668 if (ImGui::BeginMenu(label: "Options")) // <-- Append!
8669 {
8670 IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu");
8671 static bool b = true;
8672 ImGui::Checkbox(label: "SomeOption", v: &b);
8673 ImGui::EndMenu();
8674 }
8675
8676 if (ImGui::BeginMenu(label: "Disabled", enabled: false)) // Disabled
8677 {
8678 IM_ASSERT(0);
8679 }
8680 if (ImGui::MenuItem(label: "Checked", NULL, selected: true)) {}
8681 ImGui::Separator();
8682 if (ImGui::MenuItem(label: "Quit", shortcut: "Alt+F4")) {}
8683}
8684
8685//-----------------------------------------------------------------------------
8686// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
8687//-----------------------------------------------------------------------------
8688
8689// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
8690// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
8691struct ExampleAppConsole
8692{
8693 char InputBuf[256];
8694 ImVector<char*> Items;
8695 ImVector<const char*> Commands;
8696 ImVector<char*> History;
8697 int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
8698 ImGuiTextFilter Filter;
8699 bool AutoScroll;
8700 bool ScrollToBottom;
8701
8702 ExampleAppConsole()
8703 {
8704 IMGUI_DEMO_MARKER("Examples/Console");
8705 ClearLog();
8706 memset(s: InputBuf, c: 0, n: sizeof(InputBuf));
8707 HistoryPos = -1;
8708
8709 // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
8710 Commands.push_back(v: "HELP");
8711 Commands.push_back(v: "HISTORY");
8712 Commands.push_back(v: "CLEAR");
8713 Commands.push_back(v: "CLASSIFY");
8714 AutoScroll = true;
8715 ScrollToBottom = false;
8716 AddLog(fmt: "Welcome to Dear ImGui!");
8717 }
8718 ~ExampleAppConsole()
8719 {
8720 ClearLog();
8721 for (int i = 0; i < History.Size; i++)
8722 ImGui::MemFree(ptr: History[i]);
8723 }
8724
8725 // Portable helpers
8726 static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(c: *s2) - toupper(c: *s1)) == 0 && *s1) { s1++; s2++; } return d; }
8727 static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(c: *s2) - toupper(c: *s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }
8728 static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s: s) + 1; void* buf = ImGui::MemAlloc(size: len); IM_ASSERT(buf); return (char*)memcpy(dest: buf, src: (const void*)s, n: len); }
8729 static void Strtrim(char* s) { char* str_end = s + strlen(s: s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
8730
8731 void ClearLog()
8732 {
8733 for (int i = 0; i < Items.Size; i++)
8734 ImGui::MemFree(ptr: Items[i]);
8735 Items.clear();
8736 }
8737
8738 void AddLog(const char* fmt, ...) IM_FMTARGS(2)
8739 {
8740 // FIXME-OPT
8741 char buf[1024];
8742 va_list args;
8743 va_start(args, fmt);
8744 vsnprintf(s: buf, IM_ARRAYSIZE(buf), format: fmt, arg: args);
8745 buf[IM_ARRAYSIZE(buf)-1] = 0;
8746 va_end(args);
8747 Items.push_back(v: Strdup(s: buf));
8748 }
8749
8750 void Draw(const char* title, bool* p_open)
8751 {
8752 ImGui::SetNextWindowSize(size: ImVec2(520, 600), cond: ImGuiCond_FirstUseEver);
8753 if (!ImGui::Begin(name: title, p_open))
8754 {
8755 ImGui::End();
8756 return;
8757 }
8758
8759 // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
8760 // So e.g. IsItemHovered() will return true when hovering the title bar.
8761 // Here we create a context menu only available from the title bar.
8762 if (ImGui::BeginPopupContextItem())
8763 {
8764 if (ImGui::MenuItem(label: "Close Console"))
8765 *p_open = false;
8766 ImGui::EndPopup();
8767 }
8768
8769 ImGui::TextWrapped(
8770 fmt: "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate "
8771 "implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
8772 ImGui::TextWrapped(fmt: "Enter 'HELP' for help.");
8773
8774 // TODO: display items starting from the bottom
8775
8776 if (ImGui::SmallButton(label: "Add Debug Text")) { AddLog(fmt: "%d some text", Items.Size); AddLog(fmt: "some more text"); AddLog(fmt: "display very important message here!"); }
8777 ImGui::SameLine();
8778 if (ImGui::SmallButton(label: "Add Debug Error")) { AddLog(fmt: "[error] something went wrong"); }
8779 ImGui::SameLine();
8780 if (ImGui::SmallButton(label: "Clear")) { ClearLog(); }
8781 ImGui::SameLine();
8782 bool copy_to_clipboard = ImGui::SmallButton(label: "Copy");
8783 //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
8784
8785 ImGui::Separator();
8786
8787 // Options menu
8788 if (ImGui::BeginPopup(str_id: "Options"))
8789 {
8790 ImGui::Checkbox(label: "Auto-scroll", v: &AutoScroll);
8791 ImGui::EndPopup();
8792 }
8793
8794 // Options, Filter
8795 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_O, flags: ImGuiInputFlags_Tooltip);
8796 if (ImGui::Button(label: "Options"))
8797 ImGui::OpenPopup(str_id: "Options");
8798 ImGui::SameLine();
8799 Filter.Draw(label: "Filter (\"incl,-excl\") (\"error\")", width: 180);
8800 ImGui::Separator();
8801
8802 // Reserve enough left-over height for 1 separator + 1 input text
8803 const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
8804 if (ImGui::BeginChild(str_id: "ScrollingRegion", size: ImVec2(0, -footer_height_to_reserve), child_flags: ImGuiChildFlags_NavFlattened, window_flags: ImGuiWindowFlags_HorizontalScrollbar))
8805 {
8806 if (ImGui::BeginPopupContextWindow())
8807 {
8808 if (ImGui::Selectable(label: "Clear")) ClearLog();
8809 ImGui::EndPopup();
8810 }
8811
8812 // Display every line as a separate entry so we can change their color or add custom widgets.
8813 // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
8814 // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
8815 // to only process visible items. The clipper will automatically measure the height of your first item and then
8816 // "seek" to display only items in the visible area.
8817 // To use the clipper we can replace your standard loop:
8818 // for (int i = 0; i < Items.Size; i++)
8819 // With:
8820 // ImGuiListClipper clipper;
8821 // clipper.Begin(Items.Size);
8822 // while (clipper.Step())
8823 // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8824 // - That your items are evenly spaced (same height)
8825 // - That you have cheap random access to your elements (you can access them given their index,
8826 // without processing all the ones before)
8827 // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
8828 // We would need random-access on the post-filtered list.
8829 // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
8830 // or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
8831 // and appending newly elements as they are inserted. This is left as a task to the user until we can manage
8832 // to improve this example code!
8833 // If your items are of variable height:
8834 // - Split them into same height items would be simpler and facilitate random-seeking into your list.
8835 // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
8836 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(4, 1)); // Tighten spacing
8837 if (copy_to_clipboard)
8838 ImGui::LogToClipboard();
8839 for (const char* item : Items)
8840 {
8841 if (!Filter.PassFilter(text: item))
8842 continue;
8843
8844 // Normally you would store more information in your item than just a string.
8845 // (e.g. make Items[] an array of structure, store color/type etc.)
8846 ImVec4 color;
8847 bool has_color = false;
8848 if (strstr(haystack: item, needle: "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
8849 else if (strncmp(s1: item, s2: "# ", n: 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
8850 if (has_color)
8851 ImGui::PushStyleColor(idx: ImGuiCol_Text, col: color);
8852 ImGui::TextUnformatted(text: item);
8853 if (has_color)
8854 ImGui::PopStyleColor();
8855 }
8856 if (copy_to_clipboard)
8857 ImGui::LogFinish();
8858
8859 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
8860 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
8861 if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
8862 ImGui::SetScrollHereY(1.0f);
8863 ScrollToBottom = false;
8864
8865 ImGui::PopStyleVar();
8866 }
8867 ImGui::EndChild();
8868 ImGui::Separator();
8869
8870 // Command-line
8871 bool reclaim_focus = false;
8872 ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
8873 if (ImGui::InputText(label: "Input", buf: InputBuf, IM_ARRAYSIZE(InputBuf), flags: input_text_flags, callback: &TextEditCallbackStub, user_data: (void*)this))
8874 {
8875 char* s = InputBuf;
8876 Strtrim(s);
8877 if (s[0])
8878 ExecCommand(command_line: s);
8879 strcpy(dest: s, src: "");
8880 reclaim_focus = true;
8881 }
8882
8883 // Auto-focus on window apparition
8884 ImGui::SetItemDefaultFocus();
8885 if (reclaim_focus)
8886 ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
8887
8888 ImGui::End();
8889 }
8890
8891 void ExecCommand(const char* command_line)
8892 {
8893 AddLog(fmt: "# %s\n", command_line);
8894
8895 // Insert into history. First find match and delete it so it can be pushed to the back.
8896 // This isn't trying to be smart or optimal.
8897 HistoryPos = -1;
8898 for (int i = History.Size - 1; i >= 0; i--)
8899 if (Stricmp(s1: History[i], s2: command_line) == 0)
8900 {
8901 ImGui::MemFree(ptr: History[i]);
8902 History.erase(it: History.begin() + i);
8903 break;
8904 }
8905 History.push_back(v: Strdup(s: command_line));
8906
8907 // Process command
8908 if (Stricmp(s1: command_line, s2: "CLEAR") == 0)
8909 {
8910 ClearLog();
8911 }
8912 else if (Stricmp(s1: command_line, s2: "HELP") == 0)
8913 {
8914 AddLog(fmt: "Commands:");
8915 for (int i = 0; i < Commands.Size; i++)
8916 AddLog(fmt: "- %s", Commands[i]);
8917 }
8918 else if (Stricmp(s1: command_line, s2: "HISTORY") == 0)
8919 {
8920 int first = History.Size - 10;
8921 for (int i = first > 0 ? first : 0; i < History.Size; i++)
8922 AddLog(fmt: "%3d: %s\n", i, History[i]);
8923 }
8924 else
8925 {
8926 AddLog(fmt: "Unknown command: '%s'\n", command_line);
8927 }
8928
8929 // On command input, we scroll to bottom even if AutoScroll==false
8930 ScrollToBottom = true;
8931 }
8932
8933 // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
8934 static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)
8935 {
8936 ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;
8937 return console->TextEditCallback(data);
8938 }
8939
8940 int TextEditCallback(ImGuiInputTextCallbackData* data)
8941 {
8942 //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
8943 switch (data->EventFlag)
8944 {
8945 case ImGuiInputTextFlags_CallbackCompletion:
8946 {
8947 // Example of TEXT COMPLETION
8948
8949 // Locate beginning of current word
8950 const char* word_end = data->Buf + data->CursorPos;
8951 const char* word_start = word_end;
8952 while (word_start > data->Buf)
8953 {
8954 const char c = word_start[-1];
8955 if (c == ' ' || c == '\t' || c == ',' || c == ';')
8956 break;
8957 word_start--;
8958 }
8959
8960 // Build a list of candidates
8961 ImVector<const char*> candidates;
8962 for (int i = 0; i < Commands.Size; i++)
8963 if (Strnicmp(s1: Commands[i], s2: word_start, n: (int)(word_end - word_start)) == 0)
8964 candidates.push_back(v: Commands[i]);
8965
8966 if (candidates.Size == 0)
8967 {
8968 // No match
8969 AddLog(fmt: "No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
8970 }
8971 else if (candidates.Size == 1)
8972 {
8973 // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
8974 data->DeleteChars(pos: (int)(word_start - data->Buf), bytes_count: (int)(word_end - word_start));
8975 data->InsertChars(pos: data->CursorPos, text: candidates[0]);
8976 data->InsertChars(pos: data->CursorPos, text: " ");
8977 }
8978 else
8979 {
8980 // Multiple matches. Complete as much as we can..
8981 // So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
8982 int match_len = (int)(word_end - word_start);
8983 for (;;)
8984 {
8985 int c = 0;
8986 bool all_candidates_matches = true;
8987 for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
8988 if (i == 0)
8989 c = toupper(c: candidates[i][match_len]);
8990 else if (c == 0 || c != toupper(c: candidates[i][match_len]))
8991 all_candidates_matches = false;
8992 if (!all_candidates_matches)
8993 break;
8994 match_len++;
8995 }
8996
8997 if (match_len > 0)
8998 {
8999 data->DeleteChars(pos: (int)(word_start - data->Buf), bytes_count: (int)(word_end - word_start));
9000 data->InsertChars(pos: data->CursorPos, text: candidates[0], text_end: candidates[0] + match_len);
9001 }
9002
9003 // List matches
9004 AddLog(fmt: "Possible matches:\n");
9005 for (int i = 0; i < candidates.Size; i++)
9006 AddLog(fmt: "- %s\n", candidates[i]);
9007 }
9008
9009 break;
9010 }
9011 case ImGuiInputTextFlags_CallbackHistory:
9012 {
9013 // Example of HISTORY
9014 const int prev_history_pos = HistoryPos;
9015 if (data->EventKey == ImGuiKey_UpArrow)
9016 {
9017 if (HistoryPos == -1)
9018 HistoryPos = History.Size - 1;
9019 else if (HistoryPos > 0)
9020 HistoryPos--;
9021 }
9022 else if (data->EventKey == ImGuiKey_DownArrow)
9023 {
9024 if (HistoryPos != -1)
9025 if (++HistoryPos >= History.Size)
9026 HistoryPos = -1;
9027 }
9028
9029 // A better implementation would preserve the data on the current input line along with cursor position.
9030 if (prev_history_pos != HistoryPos)
9031 {
9032 const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
9033 data->DeleteChars(pos: 0, bytes_count: data->BufTextLen);
9034 data->InsertChars(pos: 0, text: history_str);
9035 }
9036 }
9037 }
9038 return 0;
9039 }
9040};
9041
9042static void ShowExampleAppConsole(bool* p_open)
9043{
9044 static ExampleAppConsole console;
9045 console.Draw(title: "Example: Console", p_open);
9046}
9047
9048//-----------------------------------------------------------------------------
9049// [SECTION] Example App: Debug Log / ShowExampleAppLog()
9050//-----------------------------------------------------------------------------
9051
9052// Usage:
9053// static ExampleAppLog my_log;
9054// my_log.AddLog("Hello %d world\n", 123);
9055// my_log.Draw("title");
9056struct ExampleAppLog
9057{
9058 ImGuiTextBuffer Buf;
9059 ImGuiTextFilter Filter;
9060 ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
9061 bool AutoScroll; // Keep scrolling if already at the bottom.
9062
9063 ExampleAppLog()
9064 {
9065 AutoScroll = true;
9066 Clear();
9067 }
9068
9069 void Clear()
9070 {
9071 Buf.clear();
9072 LineOffsets.clear();
9073 LineOffsets.push_back(v: 0);
9074 }
9075
9076 void AddLog(const char* fmt, ...) IM_FMTARGS(2)
9077 {
9078 int old_size = Buf.size();
9079 va_list args;
9080 va_start(args, fmt);
9081 Buf.appendfv(fmt, args);
9082 va_end(args);
9083 for (int new_size = Buf.size(); old_size < new_size; old_size++)
9084 if (Buf[old_size] == '\n')
9085 LineOffsets.push_back(v: old_size + 1);
9086 }
9087
9088 void Draw(const char* title, bool* p_open = NULL)
9089 {
9090 if (!ImGui::Begin(name: title, p_open))
9091 {
9092 ImGui::End();
9093 return;
9094 }
9095
9096 // Options menu
9097 if (ImGui::BeginPopup(str_id: "Options"))
9098 {
9099 ImGui::Checkbox(label: "Auto-scroll", v: &AutoScroll);
9100 ImGui::EndPopup();
9101 }
9102
9103 // Main window
9104 if (ImGui::Button(label: "Options"))
9105 ImGui::OpenPopup(str_id: "Options");
9106 ImGui::SameLine();
9107 bool clear = ImGui::Button(label: "Clear");
9108 ImGui::SameLine();
9109 bool copy = ImGui::Button(label: "Copy");
9110 ImGui::SameLine();
9111 Filter.Draw(label: "Filter", width: -100.0f);
9112
9113 ImGui::Separator();
9114
9115 if (ImGui::BeginChild(str_id: "scrolling", size: ImVec2(0, 0), child_flags: ImGuiChildFlags_None, window_flags: ImGuiWindowFlags_HorizontalScrollbar))
9116 {
9117 if (clear)
9118 Clear();
9119 if (copy)
9120 ImGui::LogToClipboard();
9121
9122 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
9123 const char* buf = Buf.begin();
9124 const char* buf_end = Buf.end();
9125 if (Filter.IsActive())
9126 {
9127 // In this example we don't use the clipper when Filter is enabled.
9128 // This is because we don't have random access to the result of our filter.
9129 // A real application processing logs with ten of thousands of entries may want to store the result of
9130 // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
9131 for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
9132 {
9133 const char* line_start = buf + LineOffsets[line_no];
9134 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9135 if (Filter.PassFilter(text: line_start, text_end: line_end))
9136 ImGui::TextUnformatted(text: line_start, text_end: line_end);
9137 }
9138 }
9139 else
9140 {
9141 // The simplest and easy way to display the entire buffer:
9142 // ImGui::TextUnformatted(buf_begin, buf_end);
9143 // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
9144 // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
9145 // within the visible area.
9146 // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
9147 // on your side is recommended. Using ImGuiListClipper requires
9148 // - A) random access into your data
9149 // - B) items all being the same height,
9150 // both of which we can handle since we have an array pointing to the beginning of each line of text.
9151 // When using the filter (in the block of code above) we don't have random access into the data to display
9152 // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
9153 // it possible (and would be recommended if you want to search through tens of thousands of entries).
9154 ImGuiListClipper clipper;
9155 clipper.Begin(items_count: LineOffsets.Size);
9156 while (clipper.Step())
9157 {
9158 for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
9159 {
9160 const char* line_start = buf + LineOffsets[line_no];
9161 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9162 ImGui::TextUnformatted(text: line_start, text_end: line_end);
9163 }
9164 }
9165 clipper.End();
9166 }
9167 ImGui::PopStyleVar();
9168
9169 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
9170 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
9171 if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
9172 ImGui::SetScrollHereY(1.0f);
9173 }
9174 ImGui::EndChild();
9175 ImGui::End();
9176 }
9177};
9178
9179// Demonstrate creating a simple log window with basic filtering.
9180static void ShowExampleAppLog(bool* p_open)
9181{
9182 static ExampleAppLog log;
9183
9184 // For the demo: add a debug button _BEFORE_ the normal log window contents
9185 // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.
9186 // Most of the contents of the window will be added by the log.Draw() call.
9187 ImGui::SetNextWindowSize(size: ImVec2(500, 400), cond: ImGuiCond_FirstUseEver);
9188 ImGui::Begin(name: "Example: Log", p_open);
9189 IMGUI_DEMO_MARKER("Examples/Log");
9190 if (ImGui::SmallButton(label: "[Debug] Add 5 entries"))
9191 {
9192 static int counter = 0;
9193 const char* categories[3] = { "info", "warn", "error" };
9194 const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
9195 for (int n = 0; n < 5; n++)
9196 {
9197 const char* category = categories[counter % IM_ARRAYSIZE(categories)];
9198 const char* word = words[counter % IM_ARRAYSIZE(words)];
9199 log.AddLog(fmt: "[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
9200 ImGui::GetFrameCount(), category, ImGui::GetTime(), word);
9201 counter++;
9202 }
9203 }
9204 ImGui::End();
9205
9206 // Actually call in the regular Log helper (which will Begin() into the same window as we just did)
9207 log.Draw(title: "Example: Log", p_open);
9208}
9209
9210//-----------------------------------------------------------------------------
9211// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
9212//-----------------------------------------------------------------------------
9213
9214// Demonstrate create a window with multiple child windows.
9215static void ShowExampleAppLayout(bool* p_open)
9216{
9217 ImGui::SetNextWindowSize(size: ImVec2(500, 440), cond: ImGuiCond_FirstUseEver);
9218 if (ImGui::Begin(name: "Example: Simple layout", p_open, flags: ImGuiWindowFlags_MenuBar))
9219 {
9220 IMGUI_DEMO_MARKER("Examples/Simple layout");
9221 if (ImGui::BeginMenuBar())
9222 {
9223 if (ImGui::BeginMenu(label: "File"))
9224 {
9225 if (ImGui::MenuItem(label: "Close", shortcut: "Ctrl+W")) { *p_open = false; }
9226 ImGui::EndMenu();
9227 }
9228 ImGui::EndMenuBar();
9229 }
9230
9231 // Left
9232 static int selected = 0;
9233 {
9234 ImGui::BeginChild(str_id: "left pane", size: ImVec2(150, 0), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
9235 for (int i = 0; i < 100; i++)
9236 {
9237 // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav
9238 char label[128];
9239 sprintf(s: label, format: "MyObject %d", i);
9240 if (ImGui::Selectable(label, selected: selected == i))
9241 selected = i;
9242 }
9243 ImGui::EndChild();
9244 }
9245 ImGui::SameLine();
9246
9247 // Right
9248 {
9249 ImGui::BeginGroup();
9250 ImGui::BeginChild(str_id: "item view", size: ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
9251 ImGui::Text(fmt: "MyObject: %d", selected);
9252 ImGui::Separator();
9253 if (ImGui::BeginTabBar(str_id: "##Tabs", flags: ImGuiTabBarFlags_None))
9254 {
9255 if (ImGui::BeginTabItem(label: "Description"))
9256 {
9257 ImGui::TextWrapped(fmt: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ");
9258 ImGui::EndTabItem();
9259 }
9260 if (ImGui::BeginTabItem(label: "Details"))
9261 {
9262 ImGui::Text(fmt: "ID: 0123456789");
9263 ImGui::EndTabItem();
9264 }
9265 ImGui::EndTabBar();
9266 }
9267 ImGui::EndChild();
9268 if (ImGui::Button(label: "Revert")) {}
9269 ImGui::SameLine();
9270 if (ImGui::Button(label: "Save")) {}
9271 ImGui::EndGroup();
9272 }
9273 }
9274 ImGui::End();
9275}
9276
9277//-----------------------------------------------------------------------------
9278// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
9279//-----------------------------------------------------------------------------
9280// Some of the interactions are a bit lack-luster:
9281// - We would want pressing validating or leaving the filter to somehow restore focus.
9282// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.
9283// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.
9284//-----------------------------------------------------------------------------
9285
9286struct ExampleAppPropertyEditor
9287{
9288 ImGuiTextFilter Filter;
9289 ExampleTreeNode* VisibleNode = NULL;
9290
9291 void Draw(ExampleTreeNode* root_node)
9292 {
9293 // Left side: draw tree
9294 // - Currently using a table to benefit from RowBg feature
9295 if (ImGui::BeginChild(str_id: "##tree", size: ImVec2(300, 0), child_flags: ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
9296 {
9297 ImGui::SetNextItemWidth(-FLT_MIN);
9298 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_F, flags: ImGuiInputFlags_Tooltip);
9299 ImGui::PushItemFlag(option: ImGuiItemFlags_NoNavDefaultFocus, enabled: true);
9300 if (ImGui::InputTextWithHint(label: "##Filter", hint: "incl,-excl", buf: Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), flags: ImGuiInputTextFlags_EscapeClearsAll))
9301 Filter.Build();
9302 ImGui::PopItemFlag();
9303
9304 if (ImGui::BeginTable(str_id: "##bg", columns: 1, flags: ImGuiTableFlags_RowBg))
9305 {
9306 for (ExampleTreeNode* node : root_node->Childs)
9307 if (Filter.PassFilter(text: node->Name)) // Filter root node
9308 DrawTreeNode(node);
9309 ImGui::EndTable();
9310 }
9311 }
9312 ImGui::EndChild();
9313
9314 // Right side: draw properties
9315 ImGui::SameLine();
9316
9317 ImGui::BeginGroup(); // Lock X position
9318 if (ExampleTreeNode* node = VisibleNode)
9319 {
9320 ImGui::Text(fmt: "%s", node->Name);
9321 ImGui::TextDisabled(fmt: "UID: 0x%08X", node->UID);
9322 ImGui::Separator();
9323 if (ImGui::BeginTable(str_id: "##properties", columns: 2, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))
9324 {
9325 // Push object ID after we entered the table, so table is shared for all objects
9326 ImGui::PushID(int_id: (int)node->UID);
9327 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed);
9328 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 2.0f); // Default twice larger
9329 if (node->HasData)
9330 {
9331 // In a typical application, the structure description would be derived from a data-driven system.
9332 // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.
9333 // - Limits and some details are hard-coded to simplify the demo.
9334 for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)
9335 {
9336 ImGui::TableNextRow();
9337 ImGui::PushID(str_id: field_desc.Name);
9338 ImGui::TableNextColumn();
9339 ImGui::AlignTextToFramePadding();
9340 ImGui::TextUnformatted(text: field_desc.Name);
9341 ImGui::TableNextColumn();
9342 void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);
9343 switch (field_desc.DataType)
9344 {
9345 case ImGuiDataType_Bool:
9346 {
9347 IM_ASSERT(field_desc.DataCount == 1);
9348 ImGui::Checkbox(label: "##Editor", v: (bool*)field_ptr);
9349 break;
9350 }
9351 case ImGuiDataType_S32:
9352 {
9353 int v_min = INT_MIN, v_max = INT_MAX;
9354 ImGui::SetNextItemWidth(-FLT_MIN);
9355 ImGui::DragScalarN(label: "##Editor", data_type: field_desc.DataType, p_data: field_ptr, components: field_desc.DataCount, v_speed: 1.0f, p_min: &v_min, p_max: &v_max);
9356 break;
9357 }
9358 case ImGuiDataType_Float:
9359 {
9360 float v_min = 0.0f, v_max = 1.0f;
9361 ImGui::SetNextItemWidth(-FLT_MIN);
9362 ImGui::SliderScalarN(label: "##Editor", data_type: field_desc.DataType, p_data: field_ptr, components: field_desc.DataCount, p_min: &v_min, p_max: &v_max);
9363 break;
9364 }
9365 case ImGuiDataType_String:
9366 {
9367 ImGui::InputText(label: "##Editor", buf: reinterpret_cast<char*>(field_ptr), buf_size: 28);
9368 break;
9369 }
9370 }
9371 ImGui::PopID();
9372 }
9373 }
9374 ImGui::PopID();
9375 ImGui::EndTable();
9376 }
9377 }
9378 ImGui::EndGroup();
9379 }
9380
9381 void DrawTreeNode(ExampleTreeNode* node)
9382 {
9383 ImGui::TableNextRow();
9384 ImGui::TableNextColumn();
9385 ImGui::PushID(int_id: node->UID);
9386 ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
9387 tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards
9388 tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support
9389 if (node == VisibleNode)
9390 tree_flags |= ImGuiTreeNodeFlags_Selected;
9391 if (node->Childs.Size == 0)
9392 tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
9393 if (node->DataMyBool == false)
9394 ImGui::PushStyleColor(idx: ImGuiCol_Text, col: ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
9395 bool node_open = ImGui::TreeNodeEx(str_id: "", flags: tree_flags, fmt: "%s", node->Name);
9396 if (node->DataMyBool == false)
9397 ImGui::PopStyleColor();
9398 if (ImGui::IsItemFocused())
9399 VisibleNode = node;
9400 if (node_open)
9401 {
9402 for (ExampleTreeNode* child : node->Childs)
9403 DrawTreeNode(node: child);
9404 ImGui::TreePop();
9405 }
9406 ImGui::PopID();
9407 }
9408};
9409
9410// Demonstrate creating a simple property editor.
9411static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data)
9412{
9413 ImGui::SetNextWindowSize(size: ImVec2(430, 450), cond: ImGuiCond_FirstUseEver);
9414 if (!ImGui::Begin(name: "Example: Property editor", p_open))
9415 {
9416 ImGui::End();
9417 return;
9418 }
9419
9420 IMGUI_DEMO_MARKER("Examples/Property Editor");
9421 static ExampleAppPropertyEditor property_editor;
9422 if (demo_data->DemoTree == NULL)
9423 demo_data->DemoTree = ExampleTree_CreateDemoTree();
9424 property_editor.Draw(root_node: demo_data->DemoTree);
9425
9426 ImGui::End();
9427}
9428
9429//-----------------------------------------------------------------------------
9430// [SECTION] Example App: Long Text / ShowExampleAppLongText()
9431//-----------------------------------------------------------------------------
9432
9433// Demonstrate/test rendering huge amount of text, and the incidence of clipping.
9434static void ShowExampleAppLongText(bool* p_open)
9435{
9436 ImGui::SetNextWindowSize(size: ImVec2(520, 600), cond: ImGuiCond_FirstUseEver);
9437 if (!ImGui::Begin(name: "Example: Long text display", p_open))
9438 {
9439 ImGui::End();
9440 return;
9441 }
9442 IMGUI_DEMO_MARKER("Examples/Long text display");
9443
9444 static int test_type = 0;
9445 static ImGuiTextBuffer log;
9446 static int lines = 0;
9447 ImGui::Text(fmt: "Printing unusually long amount of text.");
9448 ImGui::Combo(label: "Test type", current_item: &test_type,
9449 items_separated_by_zeros: "Single call to TextUnformatted()\0"
9450 "Multiple calls to Text(), clipped\0"
9451 "Multiple calls to Text(), not clipped (slow)\0");
9452 ImGui::Text(fmt: "Buffer contents: %d lines, %d bytes", lines, log.size());
9453 if (ImGui::Button(label: "Clear")) { log.clear(); lines = 0; }
9454 ImGui::SameLine();
9455 if (ImGui::Button(label: "Add 1000 lines"))
9456 {
9457 for (int i = 0; i < 1000; i++)
9458 log.appendf(fmt: "%i The quick brown fox jumps over the lazy dog\n", lines + i);
9459 lines += 1000;
9460 }
9461 ImGui::BeginChild(str_id: "Log");
9462 switch (test_type)
9463 {
9464 case 0:
9465 // Single call to TextUnformatted() with a big buffer
9466 ImGui::TextUnformatted(text: log.begin(), text_end: log.end());
9467 break;
9468 case 1:
9469 {
9470 // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
9471 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
9472 ImGuiListClipper clipper;
9473 clipper.Begin(items_count: lines);
9474 while (clipper.Step())
9475 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9476 ImGui::Text(fmt: "%i The quick brown fox jumps over the lazy dog", i);
9477 ImGui::PopStyleVar();
9478 break;
9479 }
9480 case 2:
9481 // Multiple calls to Text(), not clipped (slow)
9482 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
9483 for (int i = 0; i < lines; i++)
9484 ImGui::Text(fmt: "%i The quick brown fox jumps over the lazy dog", i);
9485 ImGui::PopStyleVar();
9486 break;
9487 }
9488 ImGui::EndChild();
9489 ImGui::End();
9490}
9491
9492//-----------------------------------------------------------------------------
9493// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
9494//-----------------------------------------------------------------------------
9495
9496// Demonstrate creating a window which gets auto-resized according to its content.
9497static void ShowExampleAppAutoResize(bool* p_open)
9498{
9499 if (!ImGui::Begin(name: "Example: Auto-resizing window", p_open, flags: ImGuiWindowFlags_AlwaysAutoResize))
9500 {
9501 ImGui::End();
9502 return;
9503 }
9504 IMGUI_DEMO_MARKER("Examples/Auto-resizing window");
9505
9506 static int lines = 10;
9507 ImGui::TextUnformatted(
9508 text: "Window will resize every-frame to the size of its content.\n"
9509 "Note that you probably don't want to query the window size to\n"
9510 "output your content because that would create a feedback loop.");
9511 ImGui::SliderInt(label: "Number of lines", v: &lines, v_min: 1, v_max: 20);
9512 for (int i = 0; i < lines; i++)
9513 ImGui::Text(fmt: "%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally
9514 ImGui::End();
9515}
9516
9517//-----------------------------------------------------------------------------
9518// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
9519//-----------------------------------------------------------------------------
9520
9521// Demonstrate creating a window with custom resize constraints.
9522// Note that size constraints currently don't work on a docked window (when in 'docking' branch)
9523static void ShowExampleAppConstrainedResize(bool* p_open)
9524{
9525 struct CustomConstraints
9526 {
9527 // Helper functions to demonstrate programmatic constraints
9528 // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.
9529 // FIXME: None of the three demos works consistently when resizing from borders.
9530 static void AspectRatio(ImGuiSizeCallbackData* data)
9531 {
9532 float aspect_ratio = *(float*)data->UserData;
9533 data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio);
9534 }
9535 static void Square(ImGuiSizeCallbackData* data)
9536 {
9537 data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);
9538 }
9539 static void Step(ImGuiSizeCallbackData* data)
9540 {
9541 float step = *(float*)data->UserData;
9542 data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step);
9543 }
9544 };
9545
9546 const char* test_desc[] =
9547 {
9548 "Between 100x100 and 500x500",
9549 "At least 100x100",
9550 "Resize vertical + lock current width",
9551 "Resize horizontal + lock current height",
9552 "Width Between 400 and 500",
9553 "Height at least 400",
9554 "Custom: Aspect Ratio 16:9",
9555 "Custom: Always Square",
9556 "Custom: Fixed Steps (100)",
9557 };
9558
9559 // Options
9560 static bool auto_resize = false;
9561 static bool window_padding = true;
9562 static int type = 6; // Aspect Ratio
9563 static int display_lines = 10;
9564
9565 // Submit constraint
9566 float aspect_ratio = 16.0f / 9.0f;
9567 float fixed_step = 100.0f;
9568 if (type == 0) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(100, 100), size_max: ImVec2(500, 500)); // Between 100x100 and 500x500
9569 if (type == 1) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(100, 100), size_max: ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100
9570 if (type == 2) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(-1, 0), size_max: ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width
9571 if (type == 3) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0, -1), size_max: ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height
9572 if (type == 4) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(400, -1), size_max: ImVec2(500, -1)); // Width Between and 400 and 500
9573 if (type == 5) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(-1, 400), size_max: ImVec2(-1, FLT_MAX)); // Height at least 400
9574 if (type == 6) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0, 0), size_max: ImVec2(FLT_MAX, FLT_MAX), custom_callback: CustomConstraints::AspectRatio, custom_callback_data: (void*)&aspect_ratio); // Aspect ratio
9575 if (type == 7) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0, 0), size_max: ImVec2(FLT_MAX, FLT_MAX), custom_callback: CustomConstraints::Square); // Always Square
9576 if (type == 8) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0, 0), size_max: ImVec2(FLT_MAX, FLT_MAX), custom_callback: CustomConstraints::Step, custom_callback_data: (void*)&fixed_step); // Fixed Step
9577
9578 // Submit window
9579 if (!window_padding)
9580 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: ImVec2(0.0f, 0.0f));
9581 const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;
9582 const bool window_open = ImGui::Begin(name: "Example: Constrained Resize", p_open, flags: window_flags);
9583 if (!window_padding)
9584 ImGui::PopStyleVar();
9585 if (window_open)
9586 {
9587 IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
9588 if (ImGui::GetIO().KeyShift)
9589 {
9590 // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture.
9591 ImVec2 avail_size = ImGui::GetContentRegionAvail();
9592 ImVec2 pos = ImGui::GetCursorScreenPos();
9593 ImGui::ColorButton(desc_id: "viewport", col: ImVec4(0.5f, 0.2f, 0.5f, 1.0f), flags: ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, size: avail_size);
9594 ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10));
9595 ImGui::Text(fmt: "%.2f x %.2f", avail_size.x, avail_size.y);
9596 }
9597 else
9598 {
9599 ImGui::Text(fmt: "(Hold SHIFT to display a dummy viewport)");
9600 if (ImGui::IsWindowDocked())
9601 ImGui::Text(fmt: "Warning: Sizing Constraints won't work if the window is docked!");
9602 if (ImGui::Button(label: "Set 200x200")) { ImGui::SetWindowSize(size: ImVec2(200, 200)); } ImGui::SameLine();
9603 if (ImGui::Button(label: "Set 500x500")) { ImGui::SetWindowSize(size: ImVec2(500, 500)); } ImGui::SameLine();
9604 if (ImGui::Button(label: "Set 800x200")) { ImGui::SetWindowSize(size: ImVec2(800, 200)); }
9605 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9606 ImGui::Combo(label: "Constraint", current_item: &type, items: test_desc, IM_ARRAYSIZE(test_desc));
9607 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9608 ImGui::DragInt(label: "Lines", v: &display_lines, v_speed: 0.2f, v_min: 1, v_max: 100);
9609 ImGui::Checkbox(label: "Auto-resize", v: &auto_resize);
9610 ImGui::Checkbox(label: "Window padding", v: &window_padding);
9611 for (int i = 0; i < display_lines; i++)
9612 ImGui::Text(fmt: "%*sHello, sailor! Making this line long enough for the example.", i * 4, "");
9613 }
9614 }
9615 ImGui::End();
9616}
9617
9618//-----------------------------------------------------------------------------
9619// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
9620//-----------------------------------------------------------------------------
9621
9622// Demonstrate creating a simple static window with no decoration
9623// + a context-menu to choose which corner of the screen to use.
9624static void ShowExampleAppSimpleOverlay(bool* p_open)
9625{
9626 static int location = 0;
9627 ImGuiIO& io = ImGui::GetIO();
9628 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
9629 if (location >= 0)
9630 {
9631 const float PAD = 10.0f;
9632 const ImGuiViewport* viewport = ImGui::GetMainViewport();
9633 ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
9634 ImVec2 work_size = viewport->WorkSize;
9635 ImVec2 window_pos, window_pos_pivot;
9636 window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);
9637 window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);
9638 window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f;
9639 window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f;
9640 ImGui::SetNextWindowPos(pos: window_pos, cond: ImGuiCond_Always, pivot: window_pos_pivot);
9641 ImGui::SetNextWindowViewport(viewport->ID);
9642 window_flags |= ImGuiWindowFlags_NoMove;
9643 }
9644 else if (location == -2)
9645 {
9646 // Center window
9647 ImGui::SetNextWindowPos(pos: ImGui::GetMainViewport()->GetCenter(), cond: ImGuiCond_Always, pivot: ImVec2(0.5f, 0.5f));
9648 window_flags |= ImGuiWindowFlags_NoMove;
9649 }
9650 ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
9651 if (ImGui::Begin(name: "Example: Simple overlay", p_open, flags: window_flags))
9652 {
9653 IMGUI_DEMO_MARKER("Examples/Simple Overlay");
9654 ImGui::Text(fmt: "Simple overlay\n" "(right-click to change position)");
9655 ImGui::Separator();
9656 if (ImGui::IsMousePosValid())
9657 ImGui::Text(fmt: "Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
9658 else
9659 ImGui::Text(fmt: "Mouse Position: <invalid>");
9660 if (ImGui::BeginPopupContextWindow())
9661 {
9662 if (ImGui::MenuItem(label: "Custom", NULL, selected: location == -1)) location = -1;
9663 if (ImGui::MenuItem(label: "Center", NULL, selected: location == -2)) location = -2;
9664 if (ImGui::MenuItem(label: "Top-left", NULL, selected: location == 0)) location = 0;
9665 if (ImGui::MenuItem(label: "Top-right", NULL, selected: location == 1)) location = 1;
9666 if (ImGui::MenuItem(label: "Bottom-left", NULL, selected: location == 2)) location = 2;
9667 if (ImGui::MenuItem(label: "Bottom-right", NULL, selected: location == 3)) location = 3;
9668 if (p_open && ImGui::MenuItem(label: "Close")) *p_open = false;
9669 ImGui::EndPopup();
9670 }
9671 }
9672 ImGui::End();
9673}
9674
9675//-----------------------------------------------------------------------------
9676// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
9677//-----------------------------------------------------------------------------
9678
9679// Demonstrate creating a window covering the entire screen/viewport
9680static void ShowExampleAppFullscreen(bool* p_open)
9681{
9682 static bool use_work_area = true;
9683 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
9684
9685 // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
9686 // Based on your use case you may want one or the other.
9687 const ImGuiViewport* viewport = ImGui::GetMainViewport();
9688 ImGui::SetNextWindowPos(pos: use_work_area ? viewport->WorkPos : viewport->Pos);
9689 ImGui::SetNextWindowSize(size: use_work_area ? viewport->WorkSize : viewport->Size);
9690
9691 if (ImGui::Begin(name: "Example: Fullscreen window", p_open, flags))
9692 {
9693 ImGui::Checkbox(label: "Use work area instead of main area", v: &use_work_area);
9694 ImGui::SameLine();
9695 HelpMarker(desc: "Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference.");
9696
9697 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoBackground", flags: &flags, flags_value: ImGuiWindowFlags_NoBackground);
9698 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoDecoration", flags: &flags, flags_value: ImGuiWindowFlags_NoDecoration);
9699 ImGui::Indent();
9700 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoTitleBar", flags: &flags, flags_value: ImGuiWindowFlags_NoTitleBar);
9701 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoCollapse", flags: &flags, flags_value: ImGuiWindowFlags_NoCollapse);
9702 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoScrollbar", flags: &flags, flags_value: ImGuiWindowFlags_NoScrollbar);
9703 ImGui::Unindent();
9704
9705 if (p_open && ImGui::Button(label: "Close this window"))
9706 *p_open = false;
9707 }
9708 ImGui::End();
9709}
9710
9711//-----------------------------------------------------------------------------
9712// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()
9713//-----------------------------------------------------------------------------
9714
9715// Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation.
9716// This applies to all regular items as well.
9717// Read FAQ section "How can I have multiple widgets with the same label?" for details.
9718static void ShowExampleAppWindowTitles(bool*)
9719{
9720 const ImGuiViewport* viewport = ImGui::GetMainViewport();
9721 const ImVec2 base_pos = viewport->Pos;
9722
9723 // By default, Windows are uniquely identified by their title.
9724 // You can use the "##" and "###" markers to manipulate the display/ID.
9725
9726 // Using "##" to display same title but have unique identifier.
9727 ImGui::SetNextWindowPos(pos: ImVec2(base_pos.x + 100, base_pos.y + 100), cond: ImGuiCond_FirstUseEver);
9728 ImGui::Begin(name: "Same title as another window##1");
9729 IMGUI_DEMO_MARKER("Examples/Manipulating window titles");
9730 ImGui::Text(fmt: "This is window 1.\nMy title is the same as window 2, but my identifier is unique.");
9731 ImGui::End();
9732
9733 ImGui::SetNextWindowPos(pos: ImVec2(base_pos.x + 100, base_pos.y + 200), cond: ImGuiCond_FirstUseEver);
9734 ImGui::Begin(name: "Same title as another window##2");
9735 ImGui::Text(fmt: "This is window 2.\nMy title is the same as window 1, but my identifier is unique.");
9736 ImGui::End();
9737
9738 // Using "###" to display a changing title but keep a static identifier "AnimatedTitle"
9739 char buf[128];
9740 sprintf(s: buf, format: "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount());
9741 ImGui::SetNextWindowPos(pos: ImVec2(base_pos.x + 100, base_pos.y + 300), cond: ImGuiCond_FirstUseEver);
9742 ImGui::Begin(name: buf);
9743 ImGui::Text(fmt: "This window has a changing title.");
9744 ImGui::End();
9745}
9746
9747//-----------------------------------------------------------------------------
9748// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
9749//-----------------------------------------------------------------------------
9750
9751// Add a |_| looking shape
9752static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz)
9753{
9754 const ImVec2 pos_norms[] = { { 0.0f, 0.0f }, { 0.3f, 0.0f }, { 0.3f, 0.7f }, { 0.7f, 0.7f }, { 0.7f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } };
9755 for (const ImVec2& p : pos_norms)
9756 draw_list->PathLineTo(pos: ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y)));
9757}
9758
9759// Demonstrate using the low-level ImDrawList to draw custom shapes.
9760static void ShowExampleAppCustomRendering(bool* p_open)
9761{
9762 if (!ImGui::Begin(name: "Example: Custom rendering", p_open))
9763 {
9764 ImGui::End();
9765 return;
9766 }
9767 IMGUI_DEMO_MARKER("Examples/Custom Rendering");
9768
9769 // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
9770 // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
9771 // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
9772 // exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
9773
9774 if (ImGui::BeginTabBar(str_id: "##TabBar"))
9775 {
9776 if (ImGui::BeginTabItem(label: "Primitives"))
9777 {
9778 ImGui::PushItemWidth(item_width: -ImGui::GetFontSize() * 15);
9779 ImDrawList* draw_list = ImGui::GetWindowDrawList();
9780
9781 // Draw gradients
9782 // (note that those are currently exacerbating our sRGB/Linear issues)
9783 // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..
9784 ImGui::Text(fmt: "Gradients");
9785 ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
9786 {
9787 ImVec2 p0 = ImGui::GetCursorScreenPos();
9788 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9789 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));
9790 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));
9791 draw_list->AddRectFilledMultiColor(p_min: p0, p_max: p1, col_upr_left: col_a, col_upr_right: col_b, col_bot_right: col_b, col_bot_left: col_a);
9792 ImGui::InvisibleButton(str_id: "##gradient1", size: gradient_size);
9793 }
9794 {
9795 ImVec2 p0 = ImGui::GetCursorScreenPos();
9796 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9797 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));
9798 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));
9799 draw_list->AddRectFilledMultiColor(p_min: p0, p_max: p1, col_upr_left: col_a, col_upr_right: col_b, col_bot_right: col_b, col_bot_left: col_a);
9800 ImGui::InvisibleButton(str_id: "##gradient2", size: gradient_size);
9801 }
9802
9803 // Draw a bunch of primitives
9804 ImGui::Text(fmt: "All primitives");
9805 static float sz = 36.0f;
9806 static float thickness = 3.0f;
9807 static int ngon_sides = 6;
9808 static bool circle_segments_override = false;
9809 static int circle_segments_override_v = 12;
9810 static bool curve_segments_override = false;
9811 static int curve_segments_override_v = 8;
9812 static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
9813 ImGui::DragFloat(label: "Size", v: &sz, v_speed: 0.2f, v_min: 2.0f, v_max: 100.0f, format: "%.0f");
9814 ImGui::DragFloat(label: "Thickness", v: &thickness, v_speed: 0.05f, v_min: 1.0f, v_max: 8.0f, format: "%.02f");
9815 ImGui::SliderInt(label: "N-gon sides", v: &ngon_sides, v_min: 3, v_max: 12);
9816 ImGui::Checkbox(label: "##circlesegmentoverride", v: &circle_segments_override);
9817 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
9818 circle_segments_override |= ImGui::SliderInt(label: "Circle segments override", v: &circle_segments_override_v, v_min: 3, v_max: 40);
9819 ImGui::Checkbox(label: "##curvessegmentoverride", v: &curve_segments_override);
9820 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
9821 curve_segments_override |= ImGui::SliderInt(label: "Curves segments override", v: &curve_segments_override_v, v_min: 3, v_max: 40);
9822 ImGui::ColorEdit4(label: "Color", col: &colf.x);
9823
9824 const ImVec2 p = ImGui::GetCursorScreenPos();
9825 const ImU32 col = ImColor(colf);
9826 const float spacing = 10.0f;
9827 const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;
9828 const float rounding = sz / 5.0f;
9829 const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;
9830 const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;
9831 const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves
9832 const ImVec2 cp4[4] = { ImVec2(0.0f, 0.0f), ImVec2(sz * 1.3f, sz * 0.3f), ImVec2(sz - sz * 1.3f, sz - sz * 0.3f), ImVec2(sz, sz) };
9833
9834 float x = p.x + 4.0f;
9835 float y = p.y + 4.0f;
9836 for (int n = 0; n < 2; n++)
9837 {
9838 // First line uses a thickness of 1.0f, second line uses the configurable thickness
9839 float th = (n == 0) ? 1.0f : thickness;
9840 draw_list->AddNgon(center: ImVec2(x + sz*0.5f, y + sz*0.5f), radius: sz*0.5f, col, num_segments: ngon_sides, thickness: th); x += sz + spacing; // N-gon
9841 draw_list->AddCircle(center: ImVec2(x + sz*0.5f, y + sz*0.5f), radius: sz*0.5f, col, num_segments: circle_segments, thickness: th); x += sz + spacing; // Circle
9842 draw_list->AddEllipse(center: ImVec2(x + sz*0.5f, y + sz*0.5f), radius: ImVec2(sz*0.5f, sz*0.3f), col, rot: -0.3f, num_segments: circle_segments, thickness: th); x += sz + spacing; // Ellipse
9843 draw_list->AddRect(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), col, rounding: 0.0f, flags: ImDrawFlags_None, thickness: th); x += sz + spacing; // Square
9844 draw_list->AddRect(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), col, rounding, flags: ImDrawFlags_None, thickness: th); x += sz + spacing; // Square with all rounded corners
9845 draw_list->AddRect(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), col, rounding, flags: corners_tl_br, thickness: th); x += sz + spacing; // Square with two rounded corners
9846 draw_list->AddTriangle(p1: ImVec2(x+sz*0.5f,y), p2: ImVec2(x+sz, y+sz-0.5f), p3: ImVec2(x, y+sz-0.5f), col, thickness: th);x += sz + spacing; // Triangle
9847 //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle
9848 PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, flags: ImDrawFlags_Closed, thickness: th); x += sz + spacing; // Concave Shape
9849 //draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th);
9850 draw_list->AddLine(p1: ImVec2(x, y), p2: ImVec2(x + sz, y), col, thickness: th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
9851 draw_list->AddLine(p1: ImVec2(x, y), p2: ImVec2(x, y + sz), col, thickness: th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
9852 draw_list->AddLine(p1: ImVec2(x, y), p2: ImVec2(x + sz, y + sz), col, thickness: th); x += sz + spacing; // Diagonal line
9853
9854 // Path
9855 draw_list->PathArcTo(center: ImVec2(x + sz*0.5f, y + sz*0.5f), radius: sz*0.5f, a_min: 3.141592f, a_max: 3.141592f * -0.5f);
9856 draw_list->PathStroke(col, flags: ImDrawFlags_None, thickness: th);
9857 x += sz + spacing;
9858
9859 // Quadratic Bezier Curve (3 control points)
9860 draw_list->AddBezierQuadratic(p1: ImVec2(x + cp3[0].x, y + cp3[0].y), p2: ImVec2(x + cp3[1].x, y + cp3[1].y), p3: ImVec2(x + cp3[2].x, y + cp3[2].y), col, thickness: th, num_segments: curve_segments);
9861 x += sz + spacing;
9862
9863 // Cubic Bezier Curve (4 control points)
9864 draw_list->AddBezierCubic(p1: ImVec2(x + cp4[0].x, y + cp4[0].y), p2: ImVec2(x + cp4[1].x, y + cp4[1].y), p3: ImVec2(x + cp4[2].x, y + cp4[2].y), p4: ImVec2(x + cp4[3].x, y + cp4[3].y), col, thickness: th, num_segments: curve_segments);
9865
9866 x = p.x + 4;
9867 y += sz + spacing;
9868 }
9869
9870 // Filled shapes
9871 draw_list->AddNgonFilled(center: ImVec2(x + sz * 0.5f, y + sz * 0.5f), radius: sz * 0.5f, col, num_segments: ngon_sides); x += sz + spacing; // N-gon
9872 draw_list->AddCircleFilled(center: ImVec2(x + sz * 0.5f, y + sz * 0.5f), radius: sz * 0.5f, col, num_segments: circle_segments); x += sz + spacing; // Circle
9873 draw_list->AddEllipseFilled(center: ImVec2(x + sz * 0.5f, y + sz * 0.5f), radius: ImVec2(sz * 0.5f, sz * 0.3f), col, rot: -0.3f, num_segments: circle_segments); x += sz + spacing;// Ellipse
9874 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square
9875 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), col, rounding: 10.0f); x += sz + spacing; // Square with all rounded corners
9876 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), col, rounding: 10.0f, flags: corners_tl_br); x += sz + spacing; // Square with two rounded corners
9877 draw_list->AddTriangleFilled(p1: ImVec2(x+sz*0.5f,y), p2: ImVec2(x+sz, y+sz-0.5f), p3: ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle
9878 //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle
9879 PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col); x += sz + spacing; // Concave shape
9880 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness)
9881 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness)
9882 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine)
9883
9884 // Path
9885 draw_list->PathArcTo(center: ImVec2(x + sz * 0.5f, y + sz * 0.5f), radius: sz * 0.5f, a_min: 3.141592f * -0.5f, a_max: 3.141592f);
9886 draw_list->PathFillConvex(col);
9887 x += sz + spacing;
9888
9889 // Quadratic Bezier Curve (3 control points)
9890 draw_list->PathLineTo(pos: ImVec2(x + cp3[0].x, y + cp3[0].y));
9891 draw_list->PathBezierQuadraticCurveTo(p2: ImVec2(x + cp3[1].x, y + cp3[1].y), p3: ImVec2(x + cp3[2].x, y + cp3[2].y), num_segments: curve_segments);
9892 draw_list->PathFillConvex(col);
9893 x += sz + spacing;
9894
9895 draw_list->AddRectFilledMultiColor(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255));
9896 x += sz + spacing;
9897
9898 ImGui::Dummy(size: ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f));
9899 ImGui::PopItemWidth();
9900 ImGui::EndTabItem();
9901 }
9902
9903 if (ImGui::BeginTabItem(label: "Canvas"))
9904 {
9905 static ImVector<ImVec2> points;
9906 static ImVec2 scrolling(0.0f, 0.0f);
9907 static bool opt_enable_grid = true;
9908 static bool opt_enable_context_menu = true;
9909 static bool adding_line = false;
9910
9911 ImGui::Checkbox(label: "Enable grid", v: &opt_enable_grid);
9912 ImGui::Checkbox(label: "Enable context menu", v: &opt_enable_context_menu);
9913 ImGui::Text(fmt: "Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
9914
9915 // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
9916 // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
9917 // To use a child window instead we could use, e.g:
9918 // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
9919 // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
9920 // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);
9921 // ImGui::PopStyleColor();
9922 // ImGui::PopStyleVar();
9923 // [...]
9924 // ImGui::EndChild();
9925
9926 // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
9927 ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
9928 ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
9929 if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
9930 if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
9931 ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
9932
9933 // Draw border and background color
9934 ImGuiIO& io = ImGui::GetIO();
9935 ImDrawList* draw_list = ImGui::GetWindowDrawList();
9936 draw_list->AddRectFilled(p_min: canvas_p0, p_max: canvas_p1, IM_COL32(50, 50, 50, 255));
9937 draw_list->AddRect(p_min: canvas_p0, p_max: canvas_p1, IM_COL32(255, 255, 255, 255));
9938
9939 // This will catch our interactions
9940 ImGui::InvisibleButton(str_id: "canvas", size: canvas_sz, flags: ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
9941 const bool is_hovered = ImGui::IsItemHovered(); // Hovered
9942 const bool is_active = ImGui::IsItemActive(); // Held
9943 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
9944 const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
9945
9946 // Add first and second point
9947 if (is_hovered && !adding_line && ImGui::IsMouseClicked(button: ImGuiMouseButton_Left))
9948 {
9949 points.push_back(v: mouse_pos_in_canvas);
9950 points.push_back(v: mouse_pos_in_canvas);
9951 adding_line = true;
9952 }
9953 if (adding_line)
9954 {
9955 points.back() = mouse_pos_in_canvas;
9956 if (!ImGui::IsMouseDown(button: ImGuiMouseButton_Left))
9957 adding_line = false;
9958 }
9959
9960 // Pan (we use a zero mouse threshold when there's no context menu)
9961 // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
9962 const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;
9963 if (is_active && ImGui::IsMouseDragging(button: ImGuiMouseButton_Right, lock_threshold: mouse_threshold_for_pan))
9964 {
9965 scrolling.x += io.MouseDelta.x;
9966 scrolling.y += io.MouseDelta.y;
9967 }
9968
9969 // Context menu (under default mouse threshold)
9970 ImVec2 drag_delta = ImGui::GetMouseDragDelta(button: ImGuiMouseButton_Right);
9971 if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
9972 ImGui::OpenPopupOnItemClick(str_id: "context", popup_flags: ImGuiPopupFlags_MouseButtonRight);
9973 if (ImGui::BeginPopup(str_id: "context"))
9974 {
9975 if (adding_line)
9976 points.resize(new_size: points.size() - 2);
9977 adding_line = false;
9978 if (ImGui::MenuItem(label: "Remove one", NULL, selected: false, enabled: points.Size > 0)) { points.resize(new_size: points.size() - 2); }
9979 if (ImGui::MenuItem(label: "Remove all", NULL, selected: false, enabled: points.Size > 0)) { points.clear(); }
9980 ImGui::EndPopup();
9981 }
9982
9983 // Draw grid + all lines in the canvas
9984 draw_list->PushClipRect(clip_rect_min: canvas_p0, clip_rect_max: canvas_p1, intersect_with_current_clip_rect: true);
9985 if (opt_enable_grid)
9986 {
9987 const float GRID_STEP = 64.0f;
9988 for (float x = fmodf(x: scrolling.x, y: GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
9989 draw_list->AddLine(p1: ImVec2(canvas_p0.x + x, canvas_p0.y), p2: ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));
9990 for (float y = fmodf(x: scrolling.y, y: GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
9991 draw_list->AddLine(p1: ImVec2(canvas_p0.x, canvas_p0.y + y), p2: ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));
9992 }
9993 for (int n = 0; n < points.Size; n += 2)
9994 draw_list->AddLine(p1: ImVec2(origin.x + points[n].x, origin.y + points[n].y), p2: ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), thickness: 2.0f);
9995 draw_list->PopClipRect();
9996
9997 ImGui::EndTabItem();
9998 }
9999
10000 if (ImGui::BeginTabItem(label: "BG/FG draw lists"))
10001 {
10002 static bool draw_bg = true;
10003 static bool draw_fg = true;
10004 ImGui::Checkbox(label: "Draw in Background draw list", v: &draw_bg);
10005 ImGui::SameLine(); HelpMarker(desc: "The Background draw list will be rendered below every Dear ImGui windows.");
10006 ImGui::Checkbox(label: "Draw in Foreground draw list", v: &draw_fg);
10007 ImGui::SameLine(); HelpMarker(desc: "The Foreground draw list will be rendered over every Dear ImGui windows.");
10008 ImVec2 window_pos = ImGui::GetWindowPos();
10009 ImVec2 window_size = ImGui::GetWindowSize();
10010 ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);
10011 if (draw_bg)
10012 ImGui::GetBackgroundDrawList()->AddCircle(center: window_center, radius: window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), num_segments: 0, thickness: 10 + 4);
10013 if (draw_fg)
10014 ImGui::GetForegroundDrawList()->AddCircle(center: window_center, radius: window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), num_segments: 0, thickness: 10);
10015 ImGui::EndTabItem();
10016 }
10017
10018 // Demonstrate out-of-order rendering via channels splitting
10019 // We use functions in ImDrawList as each draw list contains a convenience splitter,
10020 // but you can also instantiate your own ImDrawListSplitter if you need to nest them.
10021 if (ImGui::BeginTabItem(label: "Draw Channels"))
10022 {
10023 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10024 {
10025 ImGui::Text(fmt: "Blue shape is drawn first: appears in back");
10026 ImGui::Text(fmt: "Red shape is drawn after: appears in front");
10027 ImVec2 p0 = ImGui::GetCursorScreenPos();
10028 draw_list->AddRectFilled(p_min: ImVec2(p0.x, p0.y), p_max: ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10029 draw_list->AddRectFilled(p_min: ImVec2(p0.x + 25, p0.y + 25), p_max: ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10030 ImGui::Dummy(size: ImVec2(75, 75));
10031 }
10032 ImGui::Separator();
10033 {
10034 ImGui::Text(fmt: "Blue shape is drawn first, into channel 1: appears in front");
10035 ImGui::Text(fmt: "Red shape is drawn after, into channel 0: appears in back");
10036 ImVec2 p1 = ImGui::GetCursorScreenPos();
10037
10038 // Create 2 channels and draw a Blue shape THEN a Red shape.
10039 // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.
10040 draw_list->ChannelsSplit(count: 2);
10041 draw_list->ChannelsSetCurrent(n: 1);
10042 draw_list->AddRectFilled(p_min: ImVec2(p1.x, p1.y), p_max: ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10043 draw_list->ChannelsSetCurrent(n: 0);
10044 draw_list->AddRectFilled(p_min: ImVec2(p1.x + 25, p1.y + 25), p_max: ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10045
10046 // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.
10047 // This works by copying draw indices only (vertices are not copied).
10048 draw_list->ChannelsMerge();
10049 ImGui::Dummy(size: ImVec2(75, 75));
10050 ImGui::Text(fmt: "After reordering, contents of channel 0 appears below channel 1.");
10051 }
10052 ImGui::EndTabItem();
10053 }
10054
10055 ImGui::EndTabBar();
10056 }
10057
10058 ImGui::End();
10059}
10060
10061//-----------------------------------------------------------------------------
10062// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
10063//-----------------------------------------------------------------------------
10064
10065// Demonstrate using DockSpace() to create an explicit docking node within an existing window.
10066// Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking!
10067// - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.
10068// - Drag from window menu button (upper-left button) to undock an entire node (all windows).
10069// - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to enable docking.
10070// About dockspaces:
10071// - Use DockSpace() to create an explicit dock node _within_ an existing window.
10072// - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport.
10073// This is often used with ImGuiDockNodeFlags_PassthruCentralNode.
10074// - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! (*)
10075// - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked.
10076// e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly.
10077// (*) because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node,
10078// because that window is submitted as part of the part of the NewFrame() call. An easy workaround is that you can create
10079// your own implicit "Debug##2" window after calling DockSpace() and leave it in the window stack for anyone to use.
10080void ShowExampleAppDockSpace(bool* p_open)
10081{
10082 // READ THIS !!!
10083 // TL;DR; this demo is more complicated than what most users you would normally use.
10084 // If we remove all options we are showcasing, this demo would become:
10085 // void ShowExampleAppDockSpace()
10086 // {
10087 // ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
10088 // }
10089 // In most cases you should be able to just call DockSpaceOverViewport() and ignore all the code below!
10090 // In this specific demo, we are not using DockSpaceOverViewport() because:
10091 // - (1) we allow the host window to be floating/moveable instead of filling the viewport (when opt_fullscreen == false)
10092 // - (2) we allow the host window to have padding (when opt_padding == true)
10093 // - (3) we expose many flags and need a way to have them visible.
10094 // - (4) we have a local menu bar in the host window (vs. you could use BeginMainMenuBar() + DockSpaceOverViewport()
10095 // in your code, but we don't here because we allow the window to be floating)
10096
10097 static bool opt_fullscreen = true;
10098 static bool opt_padding = false;
10099 static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
10100
10101 // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
10102 // because it would be confusing to have two docking targets within each others.
10103 ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
10104 if (opt_fullscreen)
10105 {
10106 const ImGuiViewport* viewport = ImGui::GetMainViewport();
10107 ImGui::SetNextWindowPos(pos: viewport->WorkPos);
10108 ImGui::SetNextWindowSize(size: viewport->WorkSize);
10109 ImGui::SetNextWindowViewport(viewport->ID);
10110 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowRounding, val: 0.0f);
10111 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowBorderSize, val: 0.0f);
10112 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
10113 window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
10114 }
10115 else
10116 {
10117 dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
10118 }
10119
10120 // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background
10121 // and handle the pass-thru hole, so we ask Begin() to not render a background.
10122 if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
10123 window_flags |= ImGuiWindowFlags_NoBackground;
10124
10125 // Important: note that we proceed even if Begin() returns false (aka window is collapsed).
10126 // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
10127 // all active windows docked into it will lose their parent and become undocked.
10128 // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
10129 // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
10130 if (!opt_padding)
10131 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: ImVec2(0.0f, 0.0f));
10132 ImGui::Begin(name: "DockSpace Demo", p_open, flags: window_flags);
10133 if (!opt_padding)
10134 ImGui::PopStyleVar();
10135
10136 if (opt_fullscreen)
10137 ImGui::PopStyleVar(count: 2);
10138
10139 // Submit the DockSpace
10140 ImGuiIO& io = ImGui::GetIO();
10141 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
10142 {
10143 ImGuiID dockspace_id = ImGui::GetID(str_id: "MyDockSpace");
10144 ImGui::DockSpace(dockspace_id, size: ImVec2(0.0f, 0.0f), flags: dockspace_flags);
10145 }
10146 else
10147 {
10148 ShowDockingDisabledMessage();
10149 }
10150
10151 if (ImGui::BeginMenuBar())
10152 {
10153 if (ImGui::BeginMenu(label: "Options"))
10154 {
10155 // Disabling fullscreen would allow the window to be moved to the front of other windows,
10156 // which we can't undo at the moment without finer window depth/z control.
10157 ImGui::MenuItem(label: "Fullscreen", NULL, p_selected: &opt_fullscreen);
10158 ImGui::MenuItem(label: "Padding", NULL, p_selected: &opt_padding);
10159 ImGui::Separator();
10160
10161 if (ImGui::MenuItem(label: "Flag: NoDockingOverCentralNode", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingOverCentralNode; }
10162 if (ImGui::MenuItem(label: "Flag: NoDockingSplit", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoDockingSplit) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingSplit; }
10163 if (ImGui::MenuItem(label: "Flag: NoUndocking", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoUndocking) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoUndocking; }
10164 if (ImGui::MenuItem(label: "Flag: NoResize", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoResize; }
10165 if (ImGui::MenuItem(label: "Flag: AutoHideTabBar", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; }
10166 if (ImGui::MenuItem(label: "Flag: PassthruCentralNode", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, enabled: opt_fullscreen)) { dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; }
10167 ImGui::Separator();
10168
10169 if (ImGui::MenuItem(label: "Close", NULL, selected: false, enabled: p_open != NULL))
10170 *p_open = false;
10171 ImGui::EndMenu();
10172 }
10173 HelpMarker(
10174 desc: "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n"
10175 "- Drag from window title bar or their tab to dock/undock." "\n"
10176 "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n"
10177 "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n"
10178 "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)" "\n"
10179 "This demo app has nothing to do with enabling docking!" "\n\n"
10180 "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window." "\n\n"
10181 "Read comments in ShowExampleAppDockSpace() for more details.");
10182
10183 ImGui::EndMenuBar();
10184 }
10185
10186 ImGui::End();
10187}
10188
10189//-----------------------------------------------------------------------------
10190// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
10191//-----------------------------------------------------------------------------
10192
10193// Simplified structure to mimic a Document model
10194struct MyDocument
10195{
10196 char Name[32]; // Document title
10197 int UID; // Unique ID (necessary as we can change title)
10198 bool Open; // Set when open (we keep an array of all available documents to simplify demo code!)
10199 bool OpenPrev; // Copy of Open from last update.
10200 bool Dirty; // Set when the document has been modified
10201 ImVec4 Color; // An arbitrary variable associated to the document
10202
10203 MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
10204 {
10205 UID = uid;
10206 snprintf(s: Name, maxlen: sizeof(Name), format: "%s", name);
10207 Open = OpenPrev = open;
10208 Dirty = false;
10209 Color = color;
10210 }
10211 void DoOpen() { Open = true; }
10212 void DoForceClose() { Open = false; Dirty = false; }
10213 void DoSave() { Dirty = false; }
10214};
10215
10216struct ExampleAppDocuments
10217{
10218 ImVector<MyDocument> Documents;
10219 ImVector<MyDocument*> CloseQueue;
10220 MyDocument* RenamingDoc = NULL;
10221 bool RenamingStarted = false;
10222
10223 ExampleAppDocuments()
10224 {
10225 Documents.push_back(v: MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
10226 Documents.push_back(v: MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
10227 Documents.push_back(v: MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
10228 Documents.push_back(v: MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));
10229 Documents.push_back(v: MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f)));
10230 Documents.push_back(v: MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f)));
10231 }
10232
10233 // As we allow to change document name, we append a never-changing document ID so tabs are stable
10234 void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size)
10235 {
10236 snprintf(s: out_buf, maxlen: out_buf_size, format: "%s###doc%d", doc->Name, doc->UID);
10237 }
10238
10239 // Display placeholder contents for the Document
10240 void DisplayDocContents(MyDocument* doc)
10241 {
10242 ImGui::PushID(ptr_id: doc);
10243 ImGui::Text(fmt: "Document \"%s\"", doc->Name);
10244 ImGui::PushStyleColor(idx: ImGuiCol_Text, col: doc->Color);
10245 ImGui::TextWrapped(fmt: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
10246 ImGui::PopStyleColor();
10247
10248 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_R, flags: ImGuiInputFlags_Tooltip);
10249 if (ImGui::Button(label: "Rename.."))
10250 {
10251 RenamingDoc = doc;
10252 RenamingStarted = true;
10253 }
10254 ImGui::SameLine();
10255
10256 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_M, flags: ImGuiInputFlags_Tooltip);
10257 if (ImGui::Button(label: "Modify"))
10258 doc->Dirty = true;
10259
10260 ImGui::SameLine();
10261 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_S, flags: ImGuiInputFlags_Tooltip);
10262 if (ImGui::Button(label: "Save"))
10263 doc->DoSave();
10264
10265 ImGui::SameLine();
10266 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_W, flags: ImGuiInputFlags_Tooltip);
10267 if (ImGui::Button(label: "Close"))
10268 CloseQueue.push_back(v: doc);
10269 ImGui::ColorEdit3(label: "color", col: &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior.
10270 ImGui::PopID();
10271 }
10272
10273 // Display context menu for the Document
10274 void DisplayDocContextMenu(MyDocument* doc)
10275 {
10276 if (!ImGui::BeginPopupContextItem())
10277 return;
10278
10279 char buf[256];
10280 sprintf(s: buf, format: "Save %s", doc->Name);
10281 if (ImGui::MenuItem(label: buf, shortcut: "Ctrl+S", selected: false, enabled: doc->Open))
10282 doc->DoSave();
10283 if (ImGui::MenuItem(label: "Rename...", shortcut: "Ctrl+R", selected: false, enabled: doc->Open))
10284 RenamingDoc = doc;
10285 if (ImGui::MenuItem(label: "Close", shortcut: "Ctrl+W", selected: false, enabled: doc->Open))
10286 CloseQueue.push_back(v: doc);
10287 ImGui::EndPopup();
10288 }
10289
10290 // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
10291 // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
10292 // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
10293 // 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
10294 // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
10295 // give the impression of a flicker for one frame.
10296 // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
10297 // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
10298 void NotifyOfDocumentsClosedElsewhere()
10299 {
10300 for (MyDocument& doc : Documents)
10301 {
10302 if (!doc.Open && doc.OpenPrev)
10303 ImGui::SetTabItemClosed(doc.Name);
10304 doc.OpenPrev = doc.Open;
10305 }
10306 }
10307};
10308
10309void ShowExampleAppDocuments(bool* p_open)
10310{
10311 static ExampleAppDocuments app;
10312
10313 // Options
10314 enum Target
10315 {
10316 Target_None,
10317 Target_Tab, // Create documents as local tab into a local tab bar
10318 Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace
10319 };
10320 static Target opt_target = Target_Tab;
10321 static bool opt_reorderable = true;
10322 static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;
10323
10324 // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant")
10325 // that we emit gets docked into the same spot as the parent window ("Example: Documents").
10326 // This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab
10327 // not visible, which in turn would stop submitting the "Eggplant" window.
10328 // We avoid this problem by submitting our documents window even if our parent window is not currently visible.
10329 // Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking.
10330
10331 bool window_contents_visible = ImGui::Begin(name: "Example: Documents", p_open, flags: ImGuiWindowFlags_MenuBar);
10332 if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow)
10333 {
10334 ImGui::End();
10335 return;
10336 }
10337
10338 // Menu
10339 if (ImGui::BeginMenuBar())
10340 {
10341 if (ImGui::BeginMenu(label: "File"))
10342 {
10343 int open_count = 0;
10344 for (MyDocument& doc : app.Documents)
10345 open_count += doc.Open ? 1 : 0;
10346
10347 if (ImGui::BeginMenu(label: "Open", enabled: open_count < app.Documents.Size))
10348 {
10349 for (MyDocument& doc : app.Documents)
10350 if (!doc.Open && ImGui::MenuItem(label: doc.Name))
10351 doc.DoOpen();
10352 ImGui::EndMenu();
10353 }
10354 if (ImGui::MenuItem(label: "Close All Documents", NULL, selected: false, enabled: open_count > 0))
10355 for (MyDocument& doc : app.Documents)
10356 app.CloseQueue.push_back(v: &doc);
10357 if (ImGui::MenuItem(label: "Exit") && p_open)
10358 *p_open = false;
10359 ImGui::EndMenu();
10360 }
10361 ImGui::EndMenuBar();
10362 }
10363
10364 // [Debug] List documents with one checkbox for each
10365 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
10366 {
10367 MyDocument& doc = app.Documents[doc_n];
10368 if (doc_n > 0)
10369 ImGui::SameLine();
10370 ImGui::PushID(ptr_id: &doc);
10371 if (ImGui::Checkbox(label: doc.Name, v: &doc.Open))
10372 if (!doc.Open)
10373 doc.DoForceClose();
10374 ImGui::PopID();
10375 }
10376 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * 12);
10377 ImGui::Combo(label: "Output", current_item: (int*)&opt_target, items_separated_by_zeros: "None\0TabBar+Tabs\0DockSpace+Window\0");
10378 ImGui::PopItemWidth();
10379 bool redock_all = false;
10380 if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox(label: "Reorderable Tabs", v: &opt_reorderable); }
10381 if (opt_target == Target_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button(label: "Redock all"); }
10382
10383 ImGui::Separator();
10384
10385 // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.
10386 // They have multiple effects:
10387 // - Display a dot next to the title.
10388 // - Tab is selected when clicking the X close button.
10389 // - Closure is not assumed (will wait for user to stop submitting the tab).
10390 // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
10391 // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty
10392 // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.
10393 // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
10394
10395 // Tabs
10396 if (opt_target == Target_Tab)
10397 {
10398 ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);
10399 tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;
10400 if (ImGui::BeginTabBar(str_id: "##tabs", flags: tab_bar_flags))
10401 {
10402 if (opt_reorderable)
10403 app.NotifyOfDocumentsClosedElsewhere();
10404
10405 // [DEBUG] Stress tests
10406 //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.
10407 //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..
10408
10409 // Submit Tabs
10410 for (MyDocument& doc : app.Documents)
10411 {
10412 if (!doc.Open)
10413 continue;
10414
10415 // As we allow to change document name, we append a never-changing document id so tabs are stable
10416 char doc_name_buf[64];
10417 app.GetTabName(doc: &doc, out_buf: doc_name_buf, out_buf_size: sizeof(doc_name_buf));
10418 ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);
10419 bool visible = ImGui::BeginTabItem(label: doc_name_buf, p_open: &doc.Open, flags: tab_flags);
10420
10421 // Cancel attempt to close when unsaved add to save queue so we can display a popup.
10422 if (!doc.Open && doc.Dirty)
10423 {
10424 doc.Open = true;
10425 app.CloseQueue.push_back(v: &doc);
10426 }
10427
10428 app.DisplayDocContextMenu(doc: &doc);
10429 if (visible)
10430 {
10431 app.DisplayDocContents(doc: &doc);
10432 ImGui::EndTabItem();
10433 }
10434 }
10435
10436 ImGui::EndTabBar();
10437 }
10438 }
10439 else if (opt_target == Target_DockSpaceAndWindow)
10440 {
10441 if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)
10442 {
10443 app.NotifyOfDocumentsClosedElsewhere();
10444
10445 // Create a DockSpace node where any window can be docked
10446 ImGuiID dockspace_id = ImGui::GetID(str_id: "MyDockSpace");
10447 ImGui::DockSpace(dockspace_id);
10448
10449 // Create Windows
10450 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
10451 {
10452 MyDocument* doc = &app.Documents[doc_n];
10453 if (!doc->Open)
10454 continue;
10455
10456 ImGui::SetNextWindowDockID(dock_id: dockspace_id, cond: redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver);
10457 ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0);
10458 bool visible = ImGui::Begin(name: doc->Name, p_open: &doc->Open, flags: window_flags);
10459
10460 // Cancel attempt to close when unsaved add to save queue so we can display a popup.
10461 if (!doc->Open && doc->Dirty)
10462 {
10463 doc->Open = true;
10464 app.CloseQueue.push_back(v: doc);
10465 }
10466
10467 app.DisplayDocContextMenu(doc);
10468 if (visible)
10469 app.DisplayDocContents(doc);
10470
10471 ImGui::End();
10472 }
10473 }
10474 else
10475 {
10476 ShowDockingDisabledMessage();
10477 }
10478 }
10479
10480 // Early out other contents
10481 if (!window_contents_visible)
10482 {
10483 ImGui::End();
10484 return;
10485 }
10486
10487 // Display renaming UI
10488 if (app.RenamingDoc != NULL)
10489 {
10490 if (app.RenamingStarted)
10491 ImGui::OpenPopup(str_id: "Rename");
10492 if (ImGui::BeginPopup(str_id: "Rename"))
10493 {
10494 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30);
10495 if (ImGui::InputText(label: "###Name", buf: app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), flags: ImGuiInputTextFlags_EnterReturnsTrue))
10496 {
10497 ImGui::CloseCurrentPopup();
10498 app.RenamingDoc = NULL;
10499 }
10500 if (app.RenamingStarted)
10501 ImGui::SetKeyboardFocusHere(-1);
10502 ImGui::EndPopup();
10503 }
10504 else
10505 {
10506 app.RenamingDoc = NULL;
10507 }
10508 app.RenamingStarted = false;
10509 }
10510
10511 // Display closing confirmation UI
10512 if (!app.CloseQueue.empty())
10513 {
10514 int close_queue_unsaved_documents = 0;
10515 for (int n = 0; n < app.CloseQueue.Size; n++)
10516 if (app.CloseQueue[n]->Dirty)
10517 close_queue_unsaved_documents++;
10518
10519 if (close_queue_unsaved_documents == 0)
10520 {
10521 // Close documents when all are unsaved
10522 for (int n = 0; n < app.CloseQueue.Size; n++)
10523 app.CloseQueue[n]->DoForceClose();
10524 app.CloseQueue.clear();
10525 }
10526 else
10527 {
10528 if (!ImGui::IsPopupOpen(str_id: "Save?"))
10529 ImGui::OpenPopup(str_id: "Save?");
10530 if (ImGui::BeginPopupModal(name: "Save?", NULL, flags: ImGuiWindowFlags_AlwaysAutoResize))
10531 {
10532 ImGui::Text(fmt: "Save change to the following items?");
10533 float item_height = ImGui::GetTextLineHeightWithSpacing();
10534 if (ImGui::BeginChild(id: ImGui::GetID(str_id: "frame"), size: ImVec2(-FLT_MIN, 6.25f * item_height), child_flags: ImGuiChildFlags_FrameStyle))
10535 for (MyDocument* doc : app.CloseQueue)
10536 if (doc->Dirty)
10537 ImGui::Text(fmt: "%s", doc->Name);
10538 ImGui::EndChild();
10539
10540 ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
10541 if (ImGui::Button(label: "Yes", size: button_size))
10542 {
10543 for (MyDocument* doc : app.CloseQueue)
10544 {
10545 if (doc->Dirty)
10546 doc->DoSave();
10547 doc->DoForceClose();
10548 }
10549 app.CloseQueue.clear();
10550 ImGui::CloseCurrentPopup();
10551 }
10552 ImGui::SameLine();
10553 if (ImGui::Button(label: "No", size: button_size))
10554 {
10555 for (MyDocument* doc : app.CloseQueue)
10556 doc->DoForceClose();
10557 app.CloseQueue.clear();
10558 ImGui::CloseCurrentPopup();
10559 }
10560 ImGui::SameLine();
10561 if (ImGui::Button(label: "Cancel", size: button_size))
10562 {
10563 app.CloseQueue.clear();
10564 ImGui::CloseCurrentPopup();
10565 }
10566 ImGui::EndPopup();
10567 }
10568 }
10569 }
10570
10571 ImGui::End();
10572}
10573
10574//-----------------------------------------------------------------------------
10575// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
10576//-----------------------------------------------------------------------------
10577
10578//#include "imgui_internal.h" // NavMoveRequestTryWrapping()
10579
10580struct ExampleAsset
10581{
10582 ImGuiID ID;
10583 int Type;
10584
10585 ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; }
10586
10587 static const ImGuiTableSortSpecs* s_current_sort_specs;
10588
10589 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count)
10590 {
10591 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
10592 if (items_count > 1)
10593 qsort(base: items, nmemb: (size_t)items_count, size: sizeof(items[0]), compar: ExampleAsset::CompareWithSortSpecs);
10594 s_current_sort_specs = NULL;
10595 }
10596
10597 // Compare function to be used by qsort()
10598 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
10599 {
10600 const ExampleAsset* a = (const ExampleAsset*)lhs;
10601 const ExampleAsset* b = (const ExampleAsset*)rhs;
10602 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
10603 {
10604 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
10605 int delta = 0;
10606 if (sort_spec->ColumnIndex == 0)
10607 delta = ((int)a->ID - (int)b->ID);
10608 else if (sort_spec->ColumnIndex == 1)
10609 delta = (a->Type - b->Type);
10610 if (delta > 0)
10611 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
10612 if (delta < 0)
10613 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
10614 }
10615 return ((int)a->ID - (int)b->ID);
10616 }
10617};
10618const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL;
10619
10620struct ExampleAssetsBrowser
10621{
10622 // Options
10623 bool ShowTypeOverlay = true;
10624 bool AllowSorting = true;
10625 bool AllowDragUnselected = false;
10626 bool AllowBoxSelect = true;
10627 float IconSize = 32.0f;
10628 int IconSpacing = 10;
10629 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.
10630 bool StretchSpacing = true;
10631
10632 // State
10633 ImVector<ExampleAsset> Items; // Our items
10634 ExampleSelectionWithDeletion Selection; // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion)
10635 ImGuiID NextItemId = 0; // Unique identifier when creating new items
10636 bool RequestDelete = false; // Deferred deletion request
10637 bool RequestSort = false; // Deferred sort request
10638 float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better
10639
10640 // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way.
10641 ImVec2 LayoutItemSize;
10642 ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing
10643 float LayoutItemSpacing = 0.0f;
10644 float LayoutSelectableSpacing = 0.0f;
10645 float LayoutOuterPadding = 0.0f;
10646 int LayoutColumnCount = 0;
10647 int LayoutLineCount = 0;
10648
10649 // Functions
10650 ExampleAssetsBrowser()
10651 {
10652 AddItems(count: 10000);
10653 }
10654 void AddItems(int count)
10655 {
10656 if (Items.Size == 0)
10657 NextItemId = 0;
10658 Items.reserve(new_capacity: Items.Size + count);
10659 for (int n = 0; n < count; n++, NextItemId++)
10660 Items.push_back(v: ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2));
10661 RequestSort = true;
10662 }
10663 void ClearItems()
10664 {
10665 Items.clear();
10666 Selection.Clear();
10667 }
10668
10669 // Logic would be written in the main code BeginChild() and outputing to local variables.
10670 // We extracted it into a function so we can call it easily from multiple places.
10671 void UpdateLayoutSizes(float avail_width)
10672 {
10673 // Layout: when not stretching: allow extending into right-most spacing.
10674 LayoutItemSpacing = (float)IconSpacing;
10675 if (StretchSpacing == false)
10676 avail_width += floorf(x: LayoutItemSpacing * 0.5f);
10677
10678 // Layout: calculate number of icon per line and number of lines
10679 LayoutItemSize = ImVec2(floorf(x: IconSize), floorf(x: IconSize));
10680 LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);
10681 LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;
10682
10683 // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.
10684 if (StretchSpacing && LayoutColumnCount > 1)
10685 LayoutItemSpacing = floorf(x: avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount;
10686
10687 LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing);
10688 LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);
10689 LayoutOuterPadding = floorf(x: LayoutItemSpacing * 0.5f);
10690 }
10691
10692 void Draw(const char* title, bool* p_open)
10693 {
10694 ImGui::SetNextWindowSize(size: ImVec2(IconSize * 25, IconSize * 15), cond: ImGuiCond_FirstUseEver);
10695 if (!ImGui::Begin(name: title, p_open, flags: ImGuiWindowFlags_MenuBar))
10696 {
10697 ImGui::End();
10698 return;
10699 }
10700
10701 // Menu bar
10702 if (ImGui::BeginMenuBar())
10703 {
10704 if (ImGui::BeginMenu(label: "File"))
10705 {
10706 if (ImGui::MenuItem(label: "Add 10000 items"))
10707 AddItems(count: 10000);
10708 if (ImGui::MenuItem(label: "Clear items"))
10709 ClearItems();
10710 ImGui::Separator();
10711 if (ImGui::MenuItem(label: "Close", NULL, selected: false, enabled: p_open != NULL))
10712 *p_open = false;
10713 ImGui::EndMenu();
10714 }
10715 if (ImGui::BeginMenu(label: "Edit"))
10716 {
10717 if (ImGui::MenuItem(label: "Delete", shortcut: "Del", selected: false, enabled: Selection.Size > 0))
10718 RequestDelete = true;
10719 ImGui::EndMenu();
10720 }
10721 if (ImGui::BeginMenu(label: "Options"))
10722 {
10723 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * 10);
10724
10725 ImGui::SeparatorText(label: "Contents");
10726 ImGui::Checkbox(label: "Show Type Overlay", v: &ShowTypeOverlay);
10727 ImGui::Checkbox(label: "Allow Sorting", v: &AllowSorting);
10728
10729 ImGui::SeparatorText(label: "Selection Behavior");
10730 ImGui::Checkbox(label: "Allow dragging unselected item", v: &AllowDragUnselected);
10731 ImGui::Checkbox(label: "Allow box-selection", v: &AllowBoxSelect);
10732
10733 ImGui::SeparatorText(label: "Layout");
10734 ImGui::SliderFloat(label: "Icon Size", v: &IconSize, v_min: 16.0f, v_max: 128.0f, format: "%.0f");
10735 ImGui::SameLine(); HelpMarker(desc: "Use CTRL+Wheel to zoom");
10736 ImGui::SliderInt(label: "Icon Spacing", v: &IconSpacing, v_min: 0, v_max: 32);
10737 ImGui::SliderInt(label: "Icon Hit Spacing", v: &IconHitSpacing, v_min: 0, v_max: 32);
10738 ImGui::Checkbox(label: "Stretch Spacing", v: &StretchSpacing);
10739 ImGui::PopItemWidth();
10740 ImGui::EndMenu();
10741 }
10742 ImGui::EndMenuBar();
10743 }
10744
10745 // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI
10746 if (AllowSorting)
10747 {
10748 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
10749 ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders;
10750 if (ImGui::BeginTable(str_id: "for_sort_specs_only", columns: 2, flags: table_flags_for_sort_specs, outer_size: ImVec2(0.0f, ImGui::GetFrameHeight())))
10751 {
10752 ImGui::TableSetupColumn(label: "Index");
10753 ImGui::TableSetupColumn(label: "Type");
10754 ImGui::TableHeadersRow();
10755 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
10756 if (sort_specs->SpecsDirty || RequestSort)
10757 {
10758 ExampleAsset::SortWithSortSpecs(sort_specs, items: Items.Data, items_count: Items.Size);
10759 sort_specs->SpecsDirty = RequestSort = false;
10760 }
10761 ImGui::EndTable();
10762 }
10763 ImGui::PopStyleVar();
10764 }
10765
10766 ImGuiIO& io = ImGui::GetIO();
10767 ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));
10768 if (ImGui::BeginChild(str_id: "Assets", size: ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), child_flags: ImGuiChildFlags_Borders, window_flags: ImGuiWindowFlags_NoMove))
10769 {
10770 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10771
10772 const float avail_width = ImGui::GetContentRegionAvail().x;
10773 UpdateLayoutSizes(avail_width);
10774
10775 // Calculate and store start position.
10776 ImVec2 start_pos = ImGui::GetCursorScreenPos();
10777 start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding);
10778 ImGui::SetCursorScreenPos(start_pos);
10779
10780 // Multi-select
10781 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid;
10782
10783 // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items)
10784 if (AllowBoxSelect)
10785 ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d;
10786
10787 // - This feature allows dragging an unselected item without selecting it (rarely used)
10788 if (AllowDragUnselected)
10789 ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease;
10790
10791 // - Enable keyboard wrapping on X axis
10792 // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing:
10793 // ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
10794 // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system)
10795 ms_flags |= ImGuiMultiSelectFlags_NavWrapX;
10796
10797 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags: ms_flags, selection_size: Selection.Size, items_count: Items.Size);
10798
10799 // Use custom selection adapter: store ID in selection (recommended)
10800 Selection.UserData = this;
10801 Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; };
10802 Selection.ApplyRequests(ms_io);
10803
10804 const bool want_delete = (ImGui::Shortcut(key_chord: ImGuiKey_Delete, flags: ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete;
10805 const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, items_count: Items.Size) : -1;
10806 RequestDelete = false;
10807
10808 // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items)
10809 // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...
10810 // But it is necessary for two reasons:
10811 // - Selectables uses it by default to visually fill the space between two items.
10812 // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do).
10813 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing));
10814
10815 // Rendering parameters
10816 const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) };
10817 const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220));
10818 const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f);
10819 const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize(text: "999").x);
10820
10821 const int column_count = LayoutColumnCount;
10822 ImGuiListClipper clipper;
10823 clipper.Begin(items_count: LayoutLineCount, items_height: LayoutItemStep.y);
10824 if (item_curr_idx_to_focus != -1)
10825 clipper.IncludeItemByIndex(item_index: item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped.
10826 if (ms_io->RangeSrcItem != -1)
10827 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped.
10828 while (clipper.Step())
10829 {
10830 for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++)
10831 {
10832 const int item_min_idx_for_current_line = line_idx * column_count;
10833 const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size);
10834 for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx)
10835 {
10836 ExampleAsset* item_data = &Items[item_idx];
10837 ImGui::PushID(int_id: (int)item_data->ID);
10838
10839 // Position item
10840 ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y);
10841 ImGui::SetCursorScreenPos(pos);
10842
10843 ImGui::SetNextItemSelectionUserData(item_idx);
10844 bool item_is_selected = Selection.Contains(id: (ImGuiID)item_data->ID);
10845 bool item_is_visible = ImGui::IsRectVisible(size: LayoutItemSize);
10846 ImGui::Selectable(label: "", selected: item_is_selected, flags: ImGuiSelectableFlags_None, size: LayoutItemSize);
10847
10848 // Update our selection state immediately (without waiting for EndMultiSelect() requests)
10849 // because we use this to alter the color of our text/icon.
10850 if (ImGui::IsItemToggledSelection())
10851 item_is_selected = !item_is_selected;
10852
10853 // Focus (for after deletion)
10854 if (item_curr_idx_to_focus == item_idx)
10855 ImGui::SetKeyboardFocusHere(-1);
10856
10857 // Drag and drop
10858 if (ImGui::BeginDragDropSource())
10859 {
10860 // Create payload with full selection OR single unselected item.
10861 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
10862 if (ImGui::GetDragDropPayload() == NULL)
10863 {
10864 ImVector<ImGuiID> payload_items;
10865 void* it = NULL;
10866 ImGuiID id = 0;
10867 if (!item_is_selected)
10868 payload_items.push_back(v: item_data->ID);
10869 else
10870 while (Selection.GetNextSelectedItem(opaque_it: &it, out_id: &id))
10871 payload_items.push_back(v: id);
10872 ImGui::SetDragDropPayload(type: "ASSETS_BROWSER_ITEMS", data: payload_items.Data, sz: (size_t)payload_items.size_in_bytes());
10873 }
10874
10875 // Display payload content in tooltip, by extracting it from the payload data
10876 // (we could read from selection, but it is more correct and reusable to read from payload)
10877 const ImGuiPayload* payload = ImGui::GetDragDropPayload();
10878 const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID);
10879 ImGui::Text(fmt: "%d assets", payload_count);
10880
10881 ImGui::EndDragDropSource();
10882 }
10883
10884 // Render icon (a real app would likely display an image/thumbnail here)
10885 // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well.
10886 if (item_is_visible)
10887 {
10888 ImVec2 box_min(pos.x - 1, pos.y - 1);
10889 ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious
10890 draw_list->AddRectFilled(p_min: box_min, p_max: box_max, col: icon_bg_color); // Background color
10891 if (ShowTypeOverlay && item_data->Type != 0)
10892 {
10893 ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)];
10894 draw_list->AddRectFilled(p_min: ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), p_max: ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), col: type_col);
10895 }
10896 if (display_label)
10897 {
10898 ImU32 label_col = ImGui::GetColorU32(idx: item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled);
10899 char label[32];
10900 sprintf(s: label, format: "%d", item_data->ID);
10901 draw_list->AddText(pos: ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), col: label_col, text_begin: label);
10902 }
10903 }
10904
10905 ImGui::PopID();
10906 }
10907 }
10908 }
10909 clipper.End();
10910 ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
10911
10912 // Context menu
10913 if (ImGui::BeginPopupContextWindow())
10914 {
10915 ImGui::Text(fmt: "Selection: %d items", Selection.Size);
10916 ImGui::Separator();
10917 if (ImGui::MenuItem(label: "Delete", shortcut: "Del", selected: false, enabled: Selection.Size > 0))
10918 RequestDelete = true;
10919 ImGui::EndPopup();
10920 }
10921
10922 ms_io = ImGui::EndMultiSelect();
10923 Selection.ApplyRequests(ms_io);
10924 if (want_delete)
10925 Selection.ApplyDeletionPostLoop(ms_io, items&: Items, item_curr_idx_to_select: item_curr_idx_to_focus);
10926
10927 // Zooming with CTRL+Wheel
10928 if (ImGui::IsWindowAppearing())
10929 ZoomWheelAccum = 0.0f;
10930 if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(key: ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false)
10931 {
10932 ZoomWheelAccum += io.MouseWheel;
10933 if (fabsf(x: ZoomWheelAccum) >= 1.0f)
10934 {
10935 // Calculate hovered item index from mouse location
10936 // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it.
10937 const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x;
10938 const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y;
10939 const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx;
10940 //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
10941
10942 // Zoom
10943 IconSize *= powf(x: 1.1f, y: (float)(int)ZoomWheelAccum);
10944 IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f);
10945 ZoomWheelAccum -= (int)ZoomWheelAccum;
10946 UpdateLayoutSizes(avail_width);
10947
10948 // Manipulate scroll to that we will land at the same Y location of currently hovered item.
10949 // - Calculate next frame position of item under mouse
10950 // - Set new scroll position to be used in next ImGui::BeginChild() call.
10951 float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(x: hovered_item_ny, y: 1.0f)) * LayoutItemStep.y;
10952 hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y;
10953 float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y;
10954 ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y);
10955 }
10956 }
10957 }
10958 ImGui::EndChild();
10959
10960 ImGui::Text(fmt: "Selected: %d/%d items", Selection.Size, Items.Size);
10961 ImGui::End();
10962 }
10963};
10964
10965void ShowExampleAppAssetsBrowser(bool* p_open)
10966{
10967 IMGUI_DEMO_MARKER("Examples/Assets Browser");
10968 static ExampleAssetsBrowser assets_browser;
10969 assets_browser.Draw(title: "Example: Assets Browser", p_open);
10970}
10971
10972// End of Demo code
10973#else
10974
10975void ImGui::ShowAboutWindow(bool*) {}
10976void ImGui::ShowDemoWindow(bool*) {}
10977void ImGui::ShowUserGuide() {}
10978void ImGui::ShowStyleEditor(ImGuiStyle*) {}
10979bool ImGui::ShowStyleSelector(const char* label) { return false; }
10980void ImGui::ShowFontSelector(const char* label) {}
10981
10982#endif
10983
10984#endif // #ifndef IMGUI_DISABLE
10985

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of imgui/imgui_demo.cpp