1// dear imgui, v1.92.2b
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] DemoWindowWidgetsFonts()
86// [SECTION] DemoWindowWidgetsImages()
87// [SECTION] DemoWindowWidgetsListBoxes()
88// [SECTION] DemoWindowWidgetsMultiComponents()
89// [SECTION] DemoWindowWidgetsPlotting()
90// [SECTION] DemoWindowWidgetsProgressBars()
91// [SECTION] DemoWindowWidgetsQueryingStatuses()
92// [SECTION] DemoWindowWidgetsSelectables()
93// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
94// [SECTION] DemoWindowWidgetsTabs()
95// [SECTION] DemoWindowWidgetsText()
96// [SECTION] DemoWindowWidgetsTextFilter()
97// [SECTION] DemoWindowWidgetsTextInput()
98// [SECTION] DemoWindowWidgetsTooltips()
99// [SECTION] DemoWindowWidgetsTreeNodes()
100// [SECTION] DemoWindowWidgetsVerticalSliders()
101// [SECTION] DemoWindowWidgets()
102// [SECTION] DemoWindowLayout()
103// [SECTION] DemoWindowPopups()
104// [SECTION] DemoWindowTables()
105// [SECTION] DemoWindowInputs()
106// [SECTION] About Window / ShowAboutWindow()
107// [SECTION] Style Editor / ShowStyleEditor()
108// [SECTION] User Guide / ShowUserGuide()
109// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
110// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
111// [SECTION] Example App: Debug Log / ShowExampleAppLog()
112// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
113// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
114// [SECTION] Example App: Long Text / ShowExampleAppLongText()
115// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
116// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
117// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
118// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
119// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()
120// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
121// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
122// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
123// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
124
125*/
126
127#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
128#define _CRT_SECURE_NO_WARNINGS
129#endif
130
131#include "imgui.h"
132#ifndef IMGUI_DISABLE
133
134// System includes
135#include <ctype.h> // toupper
136#include <limits.h> // INT_MIN, INT_MAX
137#include <math.h> // sqrtf, powf, cosf, sinf, floorf, ceilf
138#include <stdio.h> // vsnprintf, sscanf, printf
139#include <stdlib.h> // NULL, malloc, free, atoi
140#include <stdint.h> // intptr_t
141#if !defined(_MSC_VER) || _MSC_VER >= 1800
142#include <inttypes.h> // PRId64/PRIu64, not avail in some MinGW headers.
143#endif
144#ifdef __EMSCRIPTEN__
145#include <emscripten/version.h> // __EMSCRIPTEN_major__ etc.
146#endif
147
148// Visual Studio warnings
149#ifdef _MSC_VER
150#pragma warning (disable: 4127) // condition expression is constant
151#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
152#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).
153#endif
154
155// Clang/GCC warnings with -Weverything
156#if defined(__clang__)
157#if __has_warning("-Wunknown-warning-option")
158#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!
159#endif
160#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
161#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
162#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)
163#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type
164#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
165#pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal
166#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.
167#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.
168#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
169#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.
170#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
171#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
172#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
173#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
174#elif defined(__GNUC__)
175#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
176#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
177#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
178#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
179#pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure)
180#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
181#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
182#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.
183#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
184#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
185#endif
186
187// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
188#ifdef _WIN32
189#define IM_NEWLINE "\r\n"
190#else
191#define IM_NEWLINE "\n"
192#endif
193
194// Helpers
195#if defined(_MSC_VER) && !defined(snprintf)
196#define snprintf _snprintf
197#endif
198#if defined(_MSC_VER) && !defined(vsnprintf)
199#define vsnprintf _vsnprintf
200#endif
201
202// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013)
203#if !defined(PRId64) && defined(_MSC_VER)
204#define PRId64 "I64d"
205#define PRIu64 "I64u"
206#elif !defined(PRId64)
207#define PRId64 "lld"
208#define PRIu64 "llu"
209#endif
210
211// Helpers macros
212// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
213// but making an exception here as those are largely simplifying code...
214// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
215#define IM_MIN(A, B) (((A) < (B)) ? (A) : (B))
216#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
217#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
218
219// Enforce cdecl calling convention for functions called by the standard library,
220// in case compilation settings changed the default to e.g. __vectorcall
221#ifndef IMGUI_CDECL
222#ifdef _MSC_VER
223#define IMGUI_CDECL __cdecl
224#else
225#define IMGUI_CDECL
226#endif
227#endif
228
229//-----------------------------------------------------------------------------
230// [SECTION] Forward Declarations
231//-----------------------------------------------------------------------------
232
233#if !defined(IMGUI_DISABLE_DEMO_WINDOWS)
234
235// Forward Declarations
236struct ImGuiDemoWindowData;
237static void ShowExampleAppMainMenuBar();
238static void ShowExampleAppAssetsBrowser(bool* p_open);
239static void ShowExampleAppConsole(bool* p_open);
240static void ShowExampleAppCustomRendering(bool* p_open);
241static void ShowExampleAppDockSpace(bool* p_open);
242static void ShowExampleAppDocuments(bool* p_open);
243static void ShowExampleAppLog(bool* p_open);
244static void ShowExampleAppLayout(bool* p_open);
245static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);
246static void ShowExampleAppSimpleOverlay(bool* p_open);
247static void ShowExampleAppAutoResize(bool* p_open);
248static void ShowExampleAppConstrainedResize(bool* p_open);
249static void ShowExampleAppFullscreen(bool* p_open);
250static void ShowExampleAppLongText(bool* p_open);
251static void ShowExampleAppWindowTitles(bool* p_open);
252static void ShowExampleMenuFile();
253
254// We split the contents of the big ShowDemoWindow() function into smaller functions
255// (because the link time of very large functions tends to grow non-linearly)
256static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data);
257static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data);
258static void DemoWindowLayout();
259static void DemoWindowPopups();
260static void DemoWindowTables();
261static void DemoWindowColumns();
262static void DemoWindowInputs();
263
264// Helper tree functions used by Property Editor & Multi-Select demos
265struct ExampleTreeNode;
266static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent);
267static void ExampleTree_DestroyNode(ExampleTreeNode* node);
268
269//-----------------------------------------------------------------------------
270// [SECTION] Helpers
271//-----------------------------------------------------------------------------
272
273// Helper to display a little (?) mark which shows a tooltip when hovered.
274// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
275static void HelpMarker(const char* desc)
276{
277 ImGui::TextDisabled(fmt: "(?)");
278 if (ImGui::BeginItemTooltip())
279 {
280 ImGui::PushTextWrapPos(wrap_local_pos_x: ImGui::GetFontSize() * 35.0f);
281 ImGui::TextUnformatted(text: desc);
282 ImGui::PopTextWrapPos();
283 ImGui::EndTooltip();
284 }
285}
286
287static void ShowDockingDisabledMessage()
288{
289 ImGuiIO& io = ImGui::GetIO();
290 ImGui::Text(fmt: "ERROR: Docking is not enabled! See Demo > Configuration.");
291 ImGui::Text(fmt: "Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or ");
292 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: 0.0f);
293 if (ImGui::SmallButton(label: "click here"))
294 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
295}
296
297// Helper to wire demo markers located in code to an interactive browser
298typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data);
299extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback;
300extern void* GImGuiDemoMarkerCallbackUserData;
301ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL;
302void* GImGuiDemoMarkerCallbackUserData = NULL;
303#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
304
305//-----------------------------------------------------------------------------
306// [SECTION] Demo Window / ShowDemoWindow()
307//-----------------------------------------------------------------------------
308
309// Data to be shared across different functions of the demo.
310struct ImGuiDemoWindowData
311{
312 // Examples Apps (accessible from the "Examples" menu)
313 bool ShowMainMenuBar = false;
314 bool ShowAppAssetsBrowser = false;
315 bool ShowAppConsole = false;
316 bool ShowAppCustomRendering = false;
317 bool ShowAppDocuments = false;
318 bool ShowAppDockSpace = false;
319 bool ShowAppLog = false;
320 bool ShowAppLayout = false;
321 bool ShowAppPropertyEditor = false;
322 bool ShowAppSimpleOverlay = false;
323 bool ShowAppAutoResize = false;
324 bool ShowAppConstrainedResize = false;
325 bool ShowAppFullscreen = false;
326 bool ShowAppLongText = false;
327 bool ShowAppWindowTitles = false;
328
329 // Dear ImGui Tools (accessible from the "Tools" menu)
330 bool ShowMetrics = false;
331 bool ShowDebugLog = false;
332 bool ShowIDStackTool = false;
333 bool ShowStyleEditor = false;
334 bool ShowAbout = false;
335
336 // Other data
337 bool DisableSections = false;
338 ExampleTreeNode* DemoTree = NULL;
339
340 ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(node: DemoTree); }
341};
342
343// Demonstrate most Dear ImGui features (this is big function!)
344// You may execute this function to experiment with the UI and understand what it does.
345// You may then search for keywords in the code when you are interested by a specific feature.
346void ImGui::ShowDemoWindow(bool* p_open)
347{
348 // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
349 // Most functions would normally just assert/crash if the context is missing.
350 IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!");
351
352 // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
353 IMGUI_CHECKVERSION();
354
355 // Stored data
356 static ImGuiDemoWindowData demo_data;
357
358 // Examples Apps (accessible from the "Examples" menu)
359 if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); }
360 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)
361 if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(p_open: &demo_data.ShowAppDocuments); } // ...process the Document app next, as it may also use a DockSpace()
362 if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(p_open: &demo_data.ShowAppAssetsBrowser); }
363 if (demo_data.ShowAppConsole) { ShowExampleAppConsole(p_open: &demo_data.ShowAppConsole); }
364 if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(p_open: &demo_data.ShowAppCustomRendering); }
365 if (demo_data.ShowAppLog) { ShowExampleAppLog(p_open: &demo_data.ShowAppLog); }
366 if (demo_data.ShowAppLayout) { ShowExampleAppLayout(p_open: &demo_data.ShowAppLayout); }
367 if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(p_open: &demo_data.ShowAppPropertyEditor, demo_data: &demo_data); }
368 if (demo_data.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(p_open: &demo_data.ShowAppSimpleOverlay); }
369 if (demo_data.ShowAppAutoResize) { ShowExampleAppAutoResize(p_open: &demo_data.ShowAppAutoResize); }
370 if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(p_open: &demo_data.ShowAppConstrainedResize); }
371 if (demo_data.ShowAppFullscreen) { ShowExampleAppFullscreen(p_open: &demo_data.ShowAppFullscreen); }
372 if (demo_data.ShowAppLongText) { ShowExampleAppLongText(p_open: &demo_data.ShowAppLongText); }
373 if (demo_data.ShowAppWindowTitles) { ShowExampleAppWindowTitles(p_open: &demo_data.ShowAppWindowTitles); }
374
375 // Dear ImGui Tools (accessible from the "Tools" menu)
376 if (demo_data.ShowMetrics) { ImGui::ShowMetricsWindow(p_open: &demo_data.ShowMetrics); }
377 if (demo_data.ShowDebugLog) { ImGui::ShowDebugLogWindow(p_open: &demo_data.ShowDebugLog); }
378 if (demo_data.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(p_open: &demo_data.ShowIDStackTool); }
379 if (demo_data.ShowAbout) { ImGui::ShowAboutWindow(p_open: &demo_data.ShowAbout); }
380 if (demo_data.ShowStyleEditor)
381 {
382 ImGui::Begin(name: "Dear ImGui Style Editor", p_open: &demo_data.ShowStyleEditor);
383 ImGui::ShowStyleEditor();
384 ImGui::End();
385 }
386
387 // Demonstrate the various window flags. Typically you would just use the default!
388 static bool no_titlebar = false;
389 static bool no_scrollbar = false;
390 static bool no_menu = false;
391 static bool no_move = false;
392 static bool no_resize = false;
393 static bool no_collapse = false;
394 static bool no_close = false;
395 static bool no_nav = false;
396 static bool no_background = false;
397 static bool no_bring_to_front = false;
398 static bool no_docking = false;
399 static bool unsaved_document = false;
400
401 ImGuiWindowFlags window_flags = 0;
402 if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar;
403 if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar;
404 if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar;
405 if (no_move) window_flags |= ImGuiWindowFlags_NoMove;
406 if (no_resize) window_flags |= ImGuiWindowFlags_NoResize;
407 if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse;
408 if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
409 if (no_background) window_flags |= ImGuiWindowFlags_NoBackground;
410 if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
411 if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking;
412 if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument;
413 if (no_close) p_open = NULL; // Don't pass our bool* to Begin
414
415 // We specify a default position/size in case there's no data in the .ini file.
416 // We only do it to make the demo applications a little more welcoming, but typically this isn't required.
417 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
418 ImGui::SetNextWindowPos(pos: ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), cond: ImGuiCond_FirstUseEver);
419 ImGui::SetNextWindowSize(size: ImVec2(550, 680), cond: ImGuiCond_FirstUseEver);
420
421 // Main body of the Demo window starts here.
422 if (!ImGui::Begin(name: "Dear ImGui Demo", p_open, flags: window_flags))
423 {
424 // Early out if the window is collapsed, as an optimization.
425 ImGui::End();
426 return;
427 }
428
429 // Most framed widgets share a common width settings. Remaining width is used for the label.
430 // The width of the frame may be changed with PushItemWidth() or SetNextItemWidth().
431 // - Positive value for absolute size, negative value for right-alignment.
432 // - The default value is about GetWindowWidth() * 0.65f.
433 // - See 'Demo->Layout->Widgets Width' for details.
434 // Here we change the frame width based on how much width we want to give to the label.
435 const float label_width_base = ImGui::GetFontSize() * 12; // Some amount of width for label, based on font size.
436 const float label_width_max = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets.
437 const float label_width = IM_MIN(label_width_base, label_width_max);
438 ImGui::PushItemWidth(item_width: -label_width); // Right-align: framed items will leave 'label_width' available for the label.
439 //ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for framed widgets, leaving 60% width for labels.
440 //ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.40f); // e.g. Use 40% width for labels, leaving 60% width for framed widgets.
441 //ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Use XXX width for labels, leaving the rest for framed widgets.
442
443 // Menu Bar
444 DemoWindowMenuBar(demo_data: &demo_data);
445
446 ImGui::Text(fmt: "dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
447 ImGui::Spacing();
448
449 IMGUI_DEMO_MARKER("Help");
450 if (ImGui::CollapsingHeader(label: "Help"))
451 {
452 ImGui::SeparatorText(label: "ABOUT THIS DEMO:");
453 ImGui::BulletText(fmt: "Sections below are demonstrating many aspects of the library.");
454 ImGui::BulletText(fmt: "The \"Examples\" menu above leads to more demo contents.");
455 ImGui::BulletText(fmt: "The \"Tools\" menu above gives access to: About Box, Style Editor,\n"
456 "and Metrics/Debugger (general purpose Dear ImGui debugging tool).");
457
458 ImGui::SeparatorText(label: "PROGRAMMER GUIDE:");
459 ImGui::BulletText(fmt: "See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!");
460 ImGui::BulletText(fmt: "See comments in imgui.cpp.");
461 ImGui::BulletText(fmt: "See example applications in the examples/ folder.");
462 ImGui::BulletText(fmt: "Read the FAQ at ");
463 ImGui::SameLine(offset_from_start_x: 0, spacing: 0);
464 ImGui::TextLinkOpenURL(label: "https://www.dearimgui.com/faq/");
465 ImGui::BulletText(fmt: "Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.");
466 ImGui::BulletText(fmt: "Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.");
467
468 ImGui::SeparatorText(label: "USER GUIDE:");
469 ImGui::ShowUserGuide();
470 }
471
472 IMGUI_DEMO_MARKER("Configuration");
473 if (ImGui::CollapsingHeader(label: "Configuration"))
474 {
475 ImGuiIO& io = ImGui::GetIO();
476
477 if (ImGui::TreeNode(label: "Configuration##2"))
478 {
479 ImGui::SeparatorText(label: "General");
480 ImGui::CheckboxFlags(label: "io.ConfigFlags: NavEnableKeyboard", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NavEnableKeyboard);
481 ImGui::SameLine(); HelpMarker(desc: "Enable keyboard controls.");
482 ImGui::CheckboxFlags(label: "io.ConfigFlags: NavEnableGamepad", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NavEnableGamepad);
483 ImGui::SameLine(); HelpMarker(desc: "Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
484 ImGui::CheckboxFlags(label: "io.ConfigFlags: NoMouse", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NoMouse);
485 ImGui::SameLine(); HelpMarker(desc: "Instruct dear imgui to disable mouse inputs and interactions.");
486
487 // The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:
488 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
489 {
490 if (fmodf(x: (float)ImGui::GetTime(), y: 0.40f) < 0.20f)
491 {
492 ImGui::SameLine();
493 ImGui::Text(fmt: "<<PRESS SPACE TO DISABLE>>");
494 }
495 // Prevent both being checked
496 if (ImGui::IsKeyPressed(key: ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard))
497 io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
498 }
499
500 ImGui::CheckboxFlags(label: "io.ConfigFlags: NoMouseCursorChange", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NoMouseCursorChange);
501 ImGui::SameLine(); HelpMarker(desc: "Instruct backend to not alter mouse cursor shape and visibility.");
502 ImGui::CheckboxFlags(label: "io.ConfigFlags: NoKeyboard", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_NoKeyboard);
503 ImGui::SameLine(); HelpMarker(desc: "Instruct dear imgui to disable keyboard inputs and interactions.");
504
505 ImGui::Checkbox(label: "io.ConfigInputTrickleEventQueue", v: &io.ConfigInputTrickleEventQueue);
506 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.");
507 ImGui::Checkbox(label: "io.MouseDrawCursor", v: &io.MouseDrawCursor);
508 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).");
509
510 ImGui::SeparatorText(label: "Keyboard/Gamepad Navigation");
511 ImGui::Checkbox(label: "io.ConfigNavSwapGamepadButtons", v: &io.ConfigNavSwapGamepadButtons);
512 ImGui::Checkbox(label: "io.ConfigNavMoveSetMousePos", v: &io.ConfigNavMoveSetMousePos);
513 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");
514 ImGui::Checkbox(label: "io.ConfigNavCaptureKeyboard", v: &io.ConfigNavCaptureKeyboard);
515 ImGui::Checkbox(label: "io.ConfigNavEscapeClearFocusItem", v: &io.ConfigNavEscapeClearFocusItem);
516 ImGui::SameLine(); HelpMarker(desc: "Pressing Escape clears focused item.");
517 ImGui::Checkbox(label: "io.ConfigNavEscapeClearFocusWindow", v: &io.ConfigNavEscapeClearFocusWindow);
518 ImGui::SameLine(); HelpMarker(desc: "Pressing Escape clears focused window.");
519 ImGui::Checkbox(label: "io.ConfigNavCursorVisibleAuto", v: &io.ConfigNavCursorVisibleAuto);
520 ImGui::SameLine(); HelpMarker(desc: "Using directional navigation key makes the cursor visible. Mouse click hides the cursor.");
521 ImGui::Checkbox(label: "io.ConfigNavCursorVisibleAlways", v: &io.ConfigNavCursorVisibleAlways);
522 ImGui::SameLine(); HelpMarker(desc: "Navigation cursor is always visible.");
523
524 ImGui::SeparatorText(label: "Docking");
525 ImGui::CheckboxFlags(label: "io.ConfigFlags: DockingEnable", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_DockingEnable);
526 ImGui::SameLine();
527 if (io.ConfigDockingWithShift)
528 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).");
529 else
530 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).");
531 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
532 {
533 ImGui::Indent();
534 ImGui::Checkbox(label: "io.ConfigDockingNoSplit", v: &io.ConfigDockingNoSplit);
535 ImGui::SameLine(); HelpMarker(desc: "Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.");
536 ImGui::Checkbox(label: "io.ConfigDockingWithShift", v: &io.ConfigDockingWithShift);
537 ImGui::SameLine(); HelpMarker(desc: "Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)");
538 ImGui::Checkbox(label: "io.ConfigDockingAlwaysTabBar", v: &io.ConfigDockingAlwaysTabBar);
539 ImGui::SameLine(); HelpMarker(desc: "Create a docking node and tab-bar on single floating windows.");
540 ImGui::Checkbox(label: "io.ConfigDockingTransparentPayload", v: &io.ConfigDockingTransparentPayload);
541 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.");
542 ImGui::Unindent();
543 }
544
545 ImGui::SeparatorText(label: "Multi-viewports");
546 ImGui::CheckboxFlags(label: "io.ConfigFlags: ViewportsEnable", flags: &io.ConfigFlags, flags_value: ImGuiConfigFlags_ViewportsEnable);
547 ImGui::SameLine(); HelpMarker(desc: "[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details.");
548 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
549 {
550 ImGui::Indent();
551 ImGui::Checkbox(label: "io.ConfigViewportsNoAutoMerge", v: &io.ConfigViewportsNoAutoMerge);
552 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.");
553 ImGui::Checkbox(label: "io.ConfigViewportsNoTaskBarIcon", v: &io.ConfigViewportsNoTaskBarIcon);
554 ImGui::SameLine(); HelpMarker(desc: "Toggling this at runtime is normally unsupported (most platform backends won't refresh the task bar icon state right away).");
555 ImGui::Checkbox(label: "io.ConfigViewportsNoDecoration", v: &io.ConfigViewportsNoDecoration);
556 ImGui::SameLine(); HelpMarker(desc: "Toggling this at runtime is normally unsupported (most platform backends won't refresh the decoration right away).");
557 ImGui::Checkbox(label: "io.ConfigViewportsNoDefaultParent", v: &io.ConfigViewportsNoDefaultParent);
558 ImGui::SameLine(); HelpMarker(desc: "Toggling this at runtime is normally unsupported (most platform backends won't refresh the parenting right away).");
559 ImGui::Checkbox(label: "io.ConfigViewportPlatformFocusSetsImGuiFocus", v: &io.ConfigViewportPlatformFocusSetsImGuiFocus);
560 ImGui::SameLine(); HelpMarker(desc: "When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change).");
561 ImGui::Unindent();
562 }
563
564 //ImGui::SeparatorText("DPI/Scaling");
565 //ImGui::Checkbox("io.ConfigDpiScaleFonts", &io.ConfigDpiScaleFonts);
566 //ImGui::SameLine(); HelpMarker("Experimental: Automatically update style.FontScaleDpi when Monitor DPI changes. This will scale fonts but NOT style sizes/padding for now.");
567 //ImGui::Checkbox("io.ConfigDpiScaleViewports", &io.ConfigDpiScaleViewports);
568 //ImGui::SameLine(); HelpMarker("Experimental: Scale Dear ImGui and Platform Windows when Monitor DPI changes.");
569
570 ImGui::SeparatorText(label: "Windows");
571 ImGui::Checkbox(label: "io.ConfigWindowsResizeFromEdges", v: &io.ConfigWindowsResizeFromEdges);
572 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.");
573 ImGui::Checkbox(label: "io.ConfigWindowsMoveFromTitleBarOnly", v: &io.ConfigWindowsMoveFromTitleBarOnly);
574 ImGui::Checkbox(label: "io.ConfigWindowsCopyContentsWithCtrlC", v: &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]
575 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.");
576 ImGui::Checkbox(label: "io.ConfigScrollbarScrollByPage", v: &io.ConfigScrollbarScrollByPage);
577 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.");
578
579 ImGui::SeparatorText(label: "Widgets");
580 ImGui::Checkbox(label: "io.ConfigInputTextCursorBlink", v: &io.ConfigInputTextCursorBlink);
581 ImGui::SameLine(); HelpMarker(desc: "Enable blinking cursor (optional as some users consider it to be distracting).");
582 ImGui::Checkbox(label: "io.ConfigInputTextEnterKeepActive", v: &io.ConfigInputTextEnterKeepActive);
583 ImGui::SameLine(); HelpMarker(desc: "Pressing Enter will keep item active and select contents (single-line only).");
584 ImGui::Checkbox(label: "io.ConfigDragClickToInputText", v: &io.ConfigDragClickToInputText);
585 ImGui::SameLine(); HelpMarker(desc: "Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
586 ImGui::Checkbox(label: "io.ConfigMacOSXBehaviors", v: &io.ConfigMacOSXBehaviors);
587 ImGui::SameLine(); HelpMarker(desc: "Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
588 ImGui::Text(fmt: "Also see Style->Rendering for rendering options.");
589
590 // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
591 ImGui::SeparatorText(label: "Error Handling");
592
593 ImGui::Checkbox(label: "io.ConfigErrorRecovery", v: &io.ConfigErrorRecovery);
594 ImGui::SameLine(); HelpMarker(
595 desc: "Options to configure how we handle recoverable errors.\n"
596 "- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
597 "- You not are not supposed to rely on it in the course of a normal application run.\n"
598 "- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
599 "- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! "
600 "Otherwise it would severely hinder your ability to catch and correct mistakes!");
601 ImGui::Checkbox(label: "io.ConfigErrorRecoveryEnableAssert", v: &io.ConfigErrorRecoveryEnableAssert);
602 ImGui::Checkbox(label: "io.ConfigErrorRecoveryEnableDebugLog", v: &io.ConfigErrorRecoveryEnableDebugLog);
603 ImGui::Checkbox(label: "io.ConfigErrorRecoveryEnableTooltip", v: &io.ConfigErrorRecoveryEnableTooltip);
604 if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
605 io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
606
607 // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
608 ImGui::SeparatorText(label: "Debug");
609 ImGui::Checkbox(label: "io.ConfigDebugIsDebuggerPresent", v: &io.ConfigDebugIsDebuggerPresent);
610 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.");
611 ImGui::Checkbox(label: "io.ConfigDebugHighlightIdConflicts", v: &io.ConfigDebugHighlightIdConflicts);
612 ImGui::SameLine(); HelpMarker(desc: "Highlight and show an error message when multiple items have conflicting identifiers.");
613 ImGui::BeginDisabled();
614 ImGui::Checkbox(label: "io.ConfigDebugBeginReturnValueOnce", v: &io.ConfigDebugBeginReturnValueOnce);
615 ImGui::EndDisabled();
616 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.");
617 ImGui::Checkbox(label: "io.ConfigDebugBeginReturnValueLoop", v: &io.ConfigDebugBeginReturnValueLoop);
618 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.");
619 ImGui::Checkbox(label: "io.ConfigDebugIgnoreFocusLoss", v: &io.ConfigDebugIgnoreFocusLoss);
620 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.");
621 ImGui::Checkbox(label: "io.ConfigDebugIniSettings", v: &io.ConfigDebugIniSettings);
622 ImGui::SameLine(); HelpMarker(desc: "Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).");
623
624 ImGui::TreePop();
625 ImGui::Spacing();
626 }
627
628 IMGUI_DEMO_MARKER("Configuration/Backend Flags");
629 if (ImGui::TreeNode(label: "Backend Flags"))
630 {
631 HelpMarker(
632 desc: "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n"
633 "Here we expose them as read-only fields to avoid breaking interactions with your backend.");
634
635 // Make a local copy to avoid modifying actual backend flags.
636 // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?
637 ImGui::BeginDisabled();
638 ImGui::CheckboxFlags(label: "io.BackendFlags: HasGamepad", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasGamepad);
639 ImGui::CheckboxFlags(label: "io.BackendFlags: HasMouseCursors", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasMouseCursors);
640 ImGui::CheckboxFlags(label: "io.BackendFlags: HasSetMousePos", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasSetMousePos);
641 ImGui::CheckboxFlags(label: "io.BackendFlags: PlatformHasViewports", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_PlatformHasViewports);
642 ImGui::CheckboxFlags(label: "io.BackendFlags: HasMouseHoveredViewport",flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasMouseHoveredViewport);
643 ImGui::CheckboxFlags(label: "io.BackendFlags: RendererHasVtxOffset", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_RendererHasVtxOffset);
644 ImGui::CheckboxFlags(label: "io.BackendFlags: RendererHasTextures", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_RendererHasTextures);
645 ImGui::CheckboxFlags(label: "io.BackendFlags: RendererHasViewports", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_RendererHasViewports);
646 ImGui::EndDisabled();
647
648 ImGui::TreePop();
649 ImGui::Spacing();
650 }
651
652 IMGUI_DEMO_MARKER("Configuration/Style, Fonts");
653 if (ImGui::TreeNode(label: "Style, Fonts"))
654 {
655 ImGui::Checkbox(label: "Style Editor", v: &demo_data.ShowStyleEditor);
656 ImGui::SameLine();
657 HelpMarker(desc: "The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.");
658 ImGui::TreePop();
659 ImGui::Spacing();
660 }
661
662 IMGUI_DEMO_MARKER("Configuration/Capture, Logging");
663 if (ImGui::TreeNode(label: "Capture/Logging"))
664 {
665 HelpMarker(
666 desc: "The logging API redirects all text output so you can easily capture the content of "
667 "a window or a block. Tree nodes can be automatically expanded.\n"
668 "Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
669 ImGui::LogButtons();
670
671 HelpMarker(desc: "You can also call ImGui::LogText() to output directly to the log without a visual output.");
672 if (ImGui::Button(label: "Copy \"Hello, world!\" to clipboard"))
673 {
674 ImGui::LogToClipboard();
675 ImGui::LogText(fmt: "Hello, world!");
676 ImGui::LogFinish();
677 }
678 ImGui::TreePop();
679 }
680 }
681
682 IMGUI_DEMO_MARKER("Window options");
683 if (ImGui::CollapsingHeader(label: "Window options"))
684 {
685 if (ImGui::BeginTable(str_id: "split", columns: 3))
686 {
687 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No titlebar", v: &no_titlebar);
688 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No scrollbar", v: &no_scrollbar);
689 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No menu", v: &no_menu);
690 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No move", v: &no_move);
691 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No resize", v: &no_resize);
692 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No collapse", v: &no_collapse);
693 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No close", v: &no_close);
694 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No nav", v: &no_nav);
695 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No background", v: &no_background);
696 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No bring to front", v: &no_bring_to_front);
697 ImGui::TableNextColumn(); ImGui::Checkbox(label: "No docking", v: &no_docking);
698 ImGui::TableNextColumn(); ImGui::Checkbox(label: "Unsaved document", v: &unsaved_document);
699 ImGui::EndTable();
700 }
701 }
702
703 // All demo contents
704 DemoWindowWidgets(demo_data: &demo_data);
705 DemoWindowLayout();
706 DemoWindowPopups();
707 DemoWindowTables();
708 DemoWindowInputs();
709
710 // End of ShowDemoWindow()
711 ImGui::PopItemWidth();
712 ImGui::End();
713}
714
715//-----------------------------------------------------------------------------
716// [SECTION] DemoWindowMenuBar()
717//-----------------------------------------------------------------------------
718
719static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
720{
721 IMGUI_DEMO_MARKER("Menu");
722 if (ImGui::BeginMenuBar())
723 {
724 if (ImGui::BeginMenu(label: "Menu"))
725 {
726 IMGUI_DEMO_MARKER("Menu/File");
727 ShowExampleMenuFile();
728 ImGui::EndMenu();
729 }
730 if (ImGui::BeginMenu(label: "Examples"))
731 {
732 IMGUI_DEMO_MARKER("Menu/Examples");
733 ImGui::MenuItem(label: "Main menu bar", NULL, p_selected: &demo_data->ShowMainMenuBar);
734
735 ImGui::SeparatorText(label: "Mini apps");
736 ImGui::MenuItem(label: "Assets Browser", NULL, p_selected: &demo_data->ShowAppAssetsBrowser);
737 ImGui::MenuItem(label: "Console", NULL, p_selected: &demo_data->ShowAppConsole);
738 ImGui::MenuItem(label: "Custom rendering", NULL, p_selected: &demo_data->ShowAppCustomRendering);
739 ImGui::MenuItem(label: "Documents", NULL, p_selected: &demo_data->ShowAppDocuments);
740 ImGui::MenuItem(label: "Dockspace", NULL, p_selected: &demo_data->ShowAppDockSpace);
741 ImGui::MenuItem(label: "Log", NULL, p_selected: &demo_data->ShowAppLog);
742 ImGui::MenuItem(label: "Property editor", NULL, p_selected: &demo_data->ShowAppPropertyEditor);
743 ImGui::MenuItem(label: "Simple layout", NULL, p_selected: &demo_data->ShowAppLayout);
744 ImGui::MenuItem(label: "Simple overlay", NULL, p_selected: &demo_data->ShowAppSimpleOverlay);
745
746 ImGui::SeparatorText(label: "Concepts");
747 ImGui::MenuItem(label: "Auto-resizing window", NULL, p_selected: &demo_data->ShowAppAutoResize);
748 ImGui::MenuItem(label: "Constrained-resizing window", NULL, p_selected: &demo_data->ShowAppConstrainedResize);
749 ImGui::MenuItem(label: "Fullscreen window", NULL, p_selected: &demo_data->ShowAppFullscreen);
750 ImGui::MenuItem(label: "Long text display", NULL, p_selected: &demo_data->ShowAppLongText);
751 ImGui::MenuItem(label: "Manipulating window titles", NULL, p_selected: &demo_data->ShowAppWindowTitles);
752
753 ImGui::EndMenu();
754 }
755 //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar!
756 if (ImGui::BeginMenu(label: "Tools"))
757 {
758 IMGUI_DEMO_MARKER("Menu/Tools");
759 ImGuiIO& io = ImGui::GetIO();
760#ifndef IMGUI_DISABLE_DEBUG_TOOLS
761 const bool has_debug_tools = true;
762#else
763 const bool has_debug_tools = false;
764#endif
765 ImGui::MenuItem(label: "Metrics/Debugger", NULL, p_selected: &demo_data->ShowMetrics, enabled: has_debug_tools);
766 if (ImGui::BeginMenu(label: "Debug Options"))
767 {
768 ImGui::BeginDisabled(disabled: !has_debug_tools);
769 ImGui::Checkbox(label: "Highlight ID Conflicts", v: &io.ConfigDebugHighlightIdConflicts);
770 ImGui::EndDisabled();
771 ImGui::Checkbox(label: "Assert on error recovery", v: &io.ConfigErrorRecoveryEnableAssert);
772 ImGui::TextDisabled(fmt: "(see Demo->Configuration for details & more)");
773 ImGui::EndMenu();
774 }
775 ImGui::MenuItem(label: "Debug Log", NULL, p_selected: &demo_data->ShowDebugLog, enabled: has_debug_tools);
776 ImGui::MenuItem(label: "ID Stack Tool", NULL, p_selected: &demo_data->ShowIDStackTool, enabled: has_debug_tools);
777 bool is_debugger_present = io.ConfigDebugIsDebuggerPresent;
778 if (ImGui::MenuItem(label: "Item Picker", NULL, selected: false, enabled: has_debug_tools))// && is_debugger_present))
779 ImGui::DebugStartItemPicker();
780 if (!is_debugger_present)
781 ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable some extra features to avoid casual users crashing the application.");
782 ImGui::MenuItem(label: "Style Editor", NULL, p_selected: &demo_data->ShowStyleEditor);
783 ImGui::MenuItem(label: "About Dear ImGui", NULL, p_selected: &demo_data->ShowAbout);
784
785 ImGui::EndMenu();
786 }
787 ImGui::EndMenuBar();
788 }
789}
790
791//-----------------------------------------------------------------------------
792// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
793//-----------------------------------------------------------------------------
794
795// Simple representation for a tree
796// (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.)
797struct ExampleTreeNode
798{
799 // Tree structure
800 char Name[28] = "";
801 int UID = 0;
802 ExampleTreeNode* Parent = NULL;
803 ImVector<ExampleTreeNode*> Childs;
804 unsigned short IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily
805
806 // Leaf Data
807 bool HasData = false; // All leaves have data
808 bool DataMyBool = true;
809 int DataMyInt = 128;
810 ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f);
811};
812
813// Simple representation of struct metadata/serialization data.
814// (this is a minimal version of what a typical advanced application may provide)
815struct ExampleMemberInfo
816{
817 const char* Name; // Member name
818 ImGuiDataType DataType; // Member type
819 int DataCount; // Member count (1 when scalar)
820 int Offset; // Offset inside parent structure
821};
822
823// Metadata description of ExampleTreeNode struct.
824static const ExampleMemberInfo ExampleTreeNodeMemberInfos[]
825{
826 { .Name: "MyName", .DataType: ImGuiDataType_String, .DataCount: 1, offsetof(ExampleTreeNode, Name) },
827 { .Name: "MyBool", .DataType: ImGuiDataType_Bool, .DataCount: 1, offsetof(ExampleTreeNode, DataMyBool) },
828 { .Name: "MyInt", .DataType: ImGuiDataType_S32, .DataCount: 1, offsetof(ExampleTreeNode, DataMyInt) },
829 { .Name: "MyVec2", .DataType: ImGuiDataType_Float, .DataCount: 2, offsetof(ExampleTreeNode, DataMyVec2) },
830};
831
832static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent)
833{
834 ExampleTreeNode* node = IM_NEW(ExampleTreeNode);
835 snprintf(s: node->Name, IM_ARRAYSIZE(node->Name), format: "%s", name);
836 node->UID = uid;
837 node->Parent = parent;
838 node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0;
839 if (parent)
840 parent->Childs.push_back(v: node);
841 return node;
842}
843
844static void ExampleTree_DestroyNode(ExampleTreeNode* node)
845{
846 for (ExampleTreeNode* child_node : node->Childs)
847 ExampleTree_DestroyNode(node: child_node);
848 IM_DELETE(p: node);
849}
850
851// Create example tree data
852// (this allocates _many_ more times than most other code in either Dear ImGui or others demo)
853static ExampleTreeNode* ExampleTree_CreateDemoTree()
854{
855 static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" };
856 const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name);
857 char name_buf[NAME_MAX_LEN];
858 int uid = 0;
859 ExampleTreeNode* node_L0 = ExampleTree_CreateNode(name: "<ROOT>", uid: ++uid, NULL);
860 const int root_items_multiplier = 2;
861 for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++)
862 {
863 snprintf(s: name_buf, IM_ARRAYSIZE(name_buf), format: "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier);
864 ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name: name_buf, uid: ++uid, parent: node_L0);
865 const int number_of_childs = (int)strlen(s: node_L1->Name);
866 for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
867 {
868 snprintf(s: name_buf, IM_ARRAYSIZE(name_buf), format: "Child %d", idx_L1);
869 ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name: name_buf, uid: ++uid, parent: node_L1);
870 node_L2->HasData = true;
871 if (idx_L1 == 0)
872 {
873 snprintf(s: name_buf, IM_ARRAYSIZE(name_buf), format: "Sub-child %d", 0);
874 ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name: name_buf, uid: ++uid, parent: node_L2);
875 node_L3->HasData = true;
876 }
877 }
878 }
879 return node_L0;
880}
881
882//-----------------------------------------------------------------------------
883// [SECTION] DemoWindowWidgetsBasic()
884//-----------------------------------------------------------------------------
885
886static void DemoWindowWidgetsBasic()
887{
888 IMGUI_DEMO_MARKER("Widgets/Basic");
889 if (ImGui::TreeNode(label: "Basic"))
890 {
891 ImGui::SeparatorText(label: "General");
892
893 IMGUI_DEMO_MARKER("Widgets/Basic/Button");
894 static int clicked = 0;
895 if (ImGui::Button(label: "Button"))
896 clicked++;
897 if (clicked & 1)
898 {
899 ImGui::SameLine();
900 ImGui::Text(fmt: "Thanks for clicking me!");
901 }
902
903 IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox");
904 static bool check = true;
905 ImGui::Checkbox(label: "checkbox", v: &check);
906
907 IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton");
908 static int e = 0;
909 ImGui::RadioButton(label: "radio a", v: &e, v_button: 0); ImGui::SameLine();
910 ImGui::RadioButton(label: "radio b", v: &e, v_button: 1); ImGui::SameLine();
911 ImGui::RadioButton(label: "radio c", v: &e, v_button: 2);
912
913 ImGui::AlignTextToFramePadding();
914 ImGui::TextLinkOpenURL(label: "Hyperlink", url: "https://github.com/ocornut/imgui/wiki/Error-Handling");
915
916 // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
917 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)");
918 for (int i = 0; i < 7; i++)
919 {
920 if (i > 0)
921 ImGui::SameLine();
922 ImGui::PushID(int_id: i);
923 ImGui::PushStyleColor(idx: ImGuiCol_Button, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.6f, v: 0.6f));
924 ImGui::PushStyleColor(idx: ImGuiCol_ButtonHovered, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.7f, v: 0.7f));
925 ImGui::PushStyleColor(idx: ImGuiCol_ButtonActive, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.8f, v: 0.8f));
926 ImGui::Button(label: "Click");
927 ImGui::PopStyleColor(count: 3);
928 ImGui::PopID();
929 }
930
931 // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
932 // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
933 // See 'Demo->Layout->Text Baseline Alignment' for details.
934 ImGui::AlignTextToFramePadding();
935 ImGui::Text(fmt: "Hold to repeat:");
936 ImGui::SameLine();
937
938 // Arrow buttons with Repeater
939 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)");
940 static int counter = 0;
941 float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
942 ImGui::PushItemFlag(option: ImGuiItemFlags_ButtonRepeat, enabled: true);
943 if (ImGui::ArrowButton(str_id: "##left", dir: ImGuiDir_Left)) { counter--; }
944 ImGui::SameLine(offset_from_start_x: 0.0f, spacing);
945 if (ImGui::ArrowButton(str_id: "##right", dir: ImGuiDir_Right)) { counter++; }
946 ImGui::PopItemFlag();
947 ImGui::SameLine();
948 ImGui::Text(fmt: "%d", counter);
949
950 ImGui::Button(label: "Tooltip");
951 ImGui::SetItemTooltip("I am a tooltip");
952
953 ImGui::LabelText(label: "label", fmt: "Value");
954
955 ImGui::SeparatorText(label: "Inputs");
956
957 {
958 // To wire InputText() with std::string or any other custom string type,
959 // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
960 IMGUI_DEMO_MARKER("Widgets/Basic/InputText");
961 static char str0[128] = "Hello, world!";
962 ImGui::InputText(label: "input text", buf: str0, IM_ARRAYSIZE(str0));
963 ImGui::SameLine(); HelpMarker(
964 desc: "USER:\n"
965 "Hold SHIFT or use mouse to select text.\n"
966 "CTRL+Left/Right to word jump.\n"
967 "CTRL+A or Double-Click to select all.\n"
968 "CTRL+X,CTRL+C,CTRL+V for clipboard.\n"
969 "CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.\n"
970 "ESCAPE to revert.\n\n"
971 "PROGRAMMER:\n"
972 "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
973 "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
974 "in imgui_demo.cpp).");
975
976 static char str1[128] = "";
977 ImGui::InputTextWithHint(label: "input text (w/ hint)", hint: "enter text here", buf: str1, IM_ARRAYSIZE(str1));
978
979 IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat");
980 static int i0 = 123;
981 ImGui::InputInt(label: "input int", v: &i0);
982
983 static float f0 = 0.001f;
984 ImGui::InputFloat(label: "input float", v: &f0, step: 0.01f, step_fast: 1.0f, format: "%.3f");
985
986 static double d0 = 999999.00000001;
987 ImGui::InputDouble(label: "input double", v: &d0, step: 0.01f, step_fast: 1.0f, format: "%.8f");
988
989 static float f1 = 1.e10f;
990 ImGui::InputFloat(label: "input scientific", v: &f1, step: 0.0f, step_fast: 0.0f, format: "%e");
991 ImGui::SameLine(); HelpMarker(
992 desc: "You can input value using the scientific notation,\n"
993 " e.g. \"1e+8\" becomes \"100000000\".");
994
995 static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
996 ImGui::InputFloat3(label: "input float3", v: vec4a);
997 }
998
999 ImGui::SeparatorText(label: "Drags");
1000
1001 {
1002 IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat");
1003 static int i1 = 50, i2 = 42, i3 = 128;
1004 ImGui::DragInt(label: "drag int", v: &i1, v_speed: 1);
1005 ImGui::SameLine(); HelpMarker(
1006 desc: "Click and drag to edit value.\n"
1007 "Hold SHIFT/ALT for faster/slower edit.\n"
1008 "Double-click or CTRL+click to input value.");
1009 ImGui::DragInt(label: "drag int 0..100", v: &i2, v_speed: 1, v_min: 0, v_max: 100, format: "%d%%", flags: ImGuiSliderFlags_AlwaysClamp);
1010 ImGui::DragInt(label: "drag int wrap 100..200", v: &i3, v_speed: 1, v_min: 100, v_max: 200, format: "%d", flags: ImGuiSliderFlags_WrapAround);
1011
1012 static float f1 = 1.00f, f2 = 0.0067f;
1013 ImGui::DragFloat(label: "drag float", v: &f1, v_speed: 0.005f);
1014 ImGui::DragFloat(label: "drag small float", v: &f2, v_speed: 0.0001f, v_min: 0.0f, v_max: 0.0f, format: "%.06f ns");
1015 //ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround);
1016 }
1017
1018 ImGui::SeparatorText(label: "Sliders");
1019
1020 {
1021 IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat");
1022 static int i1 = 0;
1023 ImGui::SliderInt(label: "slider int", v: &i1, v_min: -1, v_max: 3);
1024 ImGui::SameLine(); HelpMarker(desc: "CTRL+click to input value.");
1025
1026 static float f1 = 0.123f, f2 = 0.0f;
1027 ImGui::SliderFloat(label: "slider float", v: &f1, v_min: 0.0f, v_max: 1.0f, format: "ratio = %.3f");
1028 ImGui::SliderFloat(label: "slider float (log)", v: &f2, v_min: -10.0f, v_max: 10.0f, format: "%.4f", flags: ImGuiSliderFlags_Logarithmic);
1029
1030 IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle");
1031 static float angle = 0.0f;
1032 ImGui::SliderAngle(label: "slider angle", v_rad: &angle);
1033
1034 // Using the format string to display a name instead of an integer.
1035 // Here we completely omit '%d' from the format string, so it'll only display a name.
1036 // This technique can also be used with DragInt().
1037 IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)");
1038 enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };
1039 static int elem = Element_Fire;
1040 const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
1041 const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
1042 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.
1043 ImGui::SameLine(); HelpMarker(desc: "Using the format string parameter to display a name instead of the underlying integer.");
1044 }
1045
1046 ImGui::SeparatorText(label: "Selectors/Pickers");
1047
1048 {
1049 IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4");
1050 static float col1[3] = { 1.0f, 0.0f, 0.2f };
1051 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1052 ImGui::ColorEdit3(label: "color 1", col: col1);
1053 ImGui::SameLine(); HelpMarker(
1054 desc: "Click on the color square to open a color picker.\n"
1055 "Click and hold to use drag and drop.\n"
1056 "Right-click on the color square to show options.\n"
1057 "CTRL+click on individual component to input value.\n");
1058
1059 ImGui::ColorEdit4(label: "color 2", col: col2);
1060 }
1061
1062 {
1063 // Using the _simplified_ one-liner Combo() api here
1064 // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.
1065 IMGUI_DEMO_MARKER("Widgets/Basic/Combo");
1066 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" };
1067 static int item_current = 0;
1068 ImGui::Combo(label: "combo", current_item: &item_current, items, IM_ARRAYSIZE(items));
1069 ImGui::SameLine(); HelpMarker(
1070 desc: "Using the simplified one-liner Combo API here.\n"
1071 "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.");
1072 }
1073
1074 {
1075 // Using the _simplified_ one-liner ListBox() api here
1076 // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.
1077 IMGUI_DEMO_MARKER("Widgets/Basic/ListBox");
1078 const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
1079 static int item_current = 1;
1080 ImGui::ListBox(label: "listbox", current_item: &item_current, items, IM_ARRAYSIZE(items), height_in_items: 4);
1081 ImGui::SameLine(); HelpMarker(
1082 desc: "Using the simplified one-liner ListBox API here.\n"
1083 "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.");
1084 }
1085
1086 // Testing ImGuiOnceUponAFrame helper.
1087 //static ImGuiOnceUponAFrame once;
1088 //for (int i = 0; i < 5; i++)
1089 // if (once)
1090 // ImGui::Text("This will be displayed only once.");
1091
1092 ImGui::TreePop();
1093 }
1094}
1095
1096//-----------------------------------------------------------------------------
1097// [SECTION] DemoWindowWidgetsBullets()
1098//-----------------------------------------------------------------------------
1099
1100static void DemoWindowWidgetsBullets()
1101{
1102 IMGUI_DEMO_MARKER("Widgets/Bullets");
1103 if (ImGui::TreeNode(label: "Bullets"))
1104 {
1105 ImGui::BulletText(fmt: "Bullet point 1");
1106 ImGui::BulletText(fmt: "Bullet point 2\nOn multiple lines");
1107 if (ImGui::TreeNode(label: "Tree node"))
1108 {
1109 ImGui::BulletText(fmt: "Another bullet point");
1110 ImGui::TreePop();
1111 }
1112 ImGui::Bullet(); ImGui::Text(fmt: "Bullet point 3 (two calls)");
1113 ImGui::Bullet(); ImGui::SmallButton(label: "Button");
1114 ImGui::TreePop();
1115 }
1116}
1117
1118//-----------------------------------------------------------------------------
1119// [SECTION] DemoWindowWidgetsCollapsingHeaders()
1120//-----------------------------------------------------------------------------
1121
1122static void DemoWindowWidgetsCollapsingHeaders()
1123{
1124 IMGUI_DEMO_MARKER("Widgets/Collapsing Headers");
1125 if (ImGui::TreeNode(label: "Collapsing Headers"))
1126 {
1127 static bool closable_group = true;
1128 ImGui::Checkbox(label: "Show 2nd header", v: &closable_group);
1129 if (ImGui::CollapsingHeader(label: "Header", flags: ImGuiTreeNodeFlags_None))
1130 {
1131 ImGui::Text(fmt: "IsItemHovered: %d", ImGui::IsItemHovered());
1132 for (int i = 0; i < 5; i++)
1133 ImGui::Text(fmt: "Some content %d", i);
1134 }
1135 if (ImGui::CollapsingHeader(label: "Header with a close button", p_visible: &closable_group))
1136 {
1137 ImGui::Text(fmt: "IsItemHovered: %d", ImGui::IsItemHovered());
1138 for (int i = 0; i < 5; i++)
1139 ImGui::Text(fmt: "More content %d", i);
1140 }
1141 /*
1142 if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet))
1143 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1144 */
1145 ImGui::TreePop();
1146 }
1147}
1148
1149//-----------------------------------------------------------------------------
1150// [SECTION] DemoWindowWidgetsColorAndPickers()
1151//-----------------------------------------------------------------------------
1152
1153static void DemoWindowWidgetsColorAndPickers()
1154{
1155 IMGUI_DEMO_MARKER("Widgets/Color");
1156 if (ImGui::TreeNode(label: "Color/Picker Widgets"))
1157 {
1158 static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);
1159 static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None;
1160
1161 ImGui::SeparatorText(label: "Options");
1162 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoAlpha", flags: &base_flags, flags_value: ImGuiColorEditFlags_NoAlpha);
1163 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaOpaque", flags: &base_flags, flags_value: ImGuiColorEditFlags_AlphaOpaque);
1164 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaNoBg", flags: &base_flags, flags_value: ImGuiColorEditFlags_AlphaNoBg);
1165 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaPreviewHalf", flags: &base_flags, flags_value: ImGuiColorEditFlags_AlphaPreviewHalf);
1166 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoDragDrop", flags: &base_flags, flags_value: ImGuiColorEditFlags_NoDragDrop);
1167 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.");
1168 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.");
1169
1170 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit");
1171 ImGui::SeparatorText(label: "Inline color editor");
1172 ImGui::Text(fmt: "Color widget:");
1173 ImGui::SameLine(); HelpMarker(
1174 desc: "Click on the color square to open a color picker.\n"
1175 "CTRL+click on individual component to input value.\n");
1176 ImGui::ColorEdit3(label: "MyColor##1", col: (float*)&color, flags: base_flags);
1177
1178 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)");
1179 ImGui::Text(fmt: "Color widget HSV with Alpha:");
1180 ImGui::ColorEdit4(label: "MyColor##2", col: (float*)&color, flags: ImGuiColorEditFlags_DisplayHSV | base_flags);
1181
1182 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)");
1183 ImGui::Text(fmt: "Color widget with Float Display:");
1184 ImGui::ColorEdit4(label: "MyColor##2f", col: (float*)&color, flags: ImGuiColorEditFlags_Float | base_flags);
1185
1186 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)");
1187 ImGui::Text(fmt: "Color button with Picker:");
1188 ImGui::SameLine(); HelpMarker(
1189 desc: "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n"
1190 "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only "
1191 "be used for the tooltip and picker popup.");
1192 ImGui::ColorEdit4(label: "MyColor##3", col: (float*)&color, flags: ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags);
1193
1194 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)");
1195 ImGui::Text(fmt: "Color button with Custom Picker Popup:");
1196
1197 // Generate a default palette. The palette will persist and can be edited.
1198 static bool saved_palette_init = true;
1199 static ImVec4 saved_palette[32] = {};
1200 if (saved_palette_init)
1201 {
1202 for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1203 {
1204 ImGui::ColorConvertHSVtoRGB(h: n / 31.0f, s: 0.8f, v: 0.8f,
1205 out_r&: saved_palette[n].x, out_g&: saved_palette[n].y, out_b&: saved_palette[n].z);
1206 saved_palette[n].w = 1.0f; // Alpha
1207 }
1208 saved_palette_init = false;
1209 }
1210
1211 static ImVec4 backup_color;
1212 bool open_popup = ImGui::ColorButton(desc_id: "MyColor##3b", col: color, flags: base_flags);
1213 ImGui::SameLine(offset_from_start_x: 0, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
1214 open_popup |= ImGui::Button(label: "Palette");
1215 if (open_popup)
1216 {
1217 ImGui::OpenPopup(str_id: "mypicker");
1218 backup_color = color;
1219 }
1220 if (ImGui::BeginPopup(str_id: "mypicker"))
1221 {
1222 ImGui::Text(fmt: "MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!");
1223 ImGui::Separator();
1224 ImGui::ColorPicker4(label: "##picker", col: (float*)&color, flags: base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);
1225 ImGui::SameLine();
1226
1227 ImGui::BeginGroup(); // Lock X position
1228 ImGui::Text(fmt: "Current");
1229 ImGui::ColorButton(desc_id: "##current", col: color, flags: ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, size: ImVec2(60, 40));
1230 ImGui::Text(fmt: "Previous");
1231 if (ImGui::ColorButton(desc_id: "##previous", col: backup_color, flags: ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, size: ImVec2(60, 40)))
1232 color = backup_color;
1233 ImGui::Separator();
1234 ImGui::Text(fmt: "Palette");
1235 for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1236 {
1237 ImGui::PushID(int_id: n);
1238 if ((n % 8) != 0)
1239 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemSpacing.y);
1240
1241 ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
1242 if (ImGui::ColorButton(desc_id: "##palette", col: saved_palette[n], flags: palette_button_flags, size: ImVec2(20, 20)))
1243 color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
1244
1245 // Allow user to drop colors into each palette entry. Note that ColorButton() is already a
1246 // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
1247 if (ImGui::BeginDragDropTarget())
1248 {
1249 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
1250 memcpy(dest: (float*)&saved_palette[n], src: payload->Data, n: sizeof(float) * 3);
1251 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
1252 memcpy(dest: (float*)&saved_palette[n], src: payload->Data, n: sizeof(float) * 4);
1253 ImGui::EndDragDropTarget();
1254 }
1255
1256 ImGui::PopID();
1257 }
1258 ImGui::EndGroup();
1259 ImGui::EndPopup();
1260 }
1261
1262 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)");
1263 ImGui::Text(fmt: "Color button only:");
1264 static bool no_border = false;
1265 ImGui::Checkbox(label: "ImGuiColorEditFlags_NoBorder", v: &no_border);
1266 ImGui::ColorButton(desc_id: "MyColor##3c", col: *(ImVec4*)&color, flags: base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), size: ImVec2(80, 80));
1267
1268 IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker");
1269 ImGui::SeparatorText(label: "Color picker");
1270
1271 static bool ref_color = false;
1272 static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);
1273 static int picker_mode = 0;
1274 static int display_mode = 0;
1275 static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar;
1276
1277 ImGui::PushID(str_id: "Color picker");
1278 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoAlpha", flags: &color_picker_flags, flags_value: ImGuiColorEditFlags_NoAlpha);
1279 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_AlphaBar", flags: &color_picker_flags, flags_value: ImGuiColorEditFlags_AlphaBar);
1280 ImGui::CheckboxFlags(label: "ImGuiColorEditFlags_NoSidePreview", flags: &color_picker_flags, flags_value: ImGuiColorEditFlags_NoSidePreview);
1281 if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview)
1282 {
1283 ImGui::SameLine();
1284 ImGui::Checkbox(label: "With Ref Color", v: &ref_color);
1285 if (ref_color)
1286 {
1287 ImGui::SameLine();
1288 ImGui::ColorEdit4(label: "##RefColor", col: &ref_color_v.x, flags: ImGuiColorEditFlags_NoInputs | base_flags);
1289 }
1290 }
1291
1292 ImGui::Combo(label: "Picker Mode", current_item: &picker_mode, items_separated_by_zeros: "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0");
1293 ImGui::SameLine(); HelpMarker(desc: "When not specified explicitly, user can right-click the picker to change mode.");
1294
1295 ImGui::Combo(label: "Display Mode", current_item: &display_mode, items_separated_by_zeros: "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0");
1296 ImGui::SameLine(); HelpMarker(
1297 desc: "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
1298 "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
1299 "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
1300
1301 ImGuiColorEditFlags flags = base_flags | color_picker_flags;
1302 if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar;
1303 if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel;
1304 if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays
1305 if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode
1306 if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV;
1307 if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;
1308 ImGui::ColorPicker4(label: "MyColor##4", col: (float*)&color, flags, ref_col: ref_color ? &ref_color_v.x : NULL);
1309
1310 ImGui::Text(fmt: "Set defaults in code:");
1311 ImGui::SameLine(); HelpMarker(
1312 desc: "SetColorEditOptions() is designed to allow you to set boot-time default.\n"
1313 "We don't have Push/Pop functions because you can force options on a per-widget basis if needed, "
1314 "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid "
1315 "encouraging you to persistently save values that aren't forward-compatible.");
1316 if (ImGui::Button(label: "Default: Uint8 + HSV + Hue Bar"))
1317 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);
1318 if (ImGui::Button(label: "Default: Float + HDR + Hue Wheel"))
1319 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
1320
1321 // Always display a small version of both types of pickers
1322 // (that's in order to make it more visible in the demo to people who are skimming quickly through it)
1323 ImGui::Text(fmt: "Both types:");
1324 float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;
1325 ImGui::SetNextItemWidth(w);
1326 ImGui::ColorPicker3(label: "##MyColor##5", col: (float*)&color, flags: ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1327 ImGui::SameLine();
1328 ImGui::SetNextItemWidth(w);
1329 ImGui::ColorPicker3(label: "##MyColor##6", col: (float*)&color, flags: ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1330 ImGui::PopID();
1331
1332 // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
1333 static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
1334 ImGui::Spacing();
1335 ImGui::Text(fmt: "HSV encoded colors");
1336 ImGui::SameLine(); HelpMarker(
1337 desc: "By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV "
1338 "allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the "
1339 "added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
1340 ImGui::Text(fmt: "Color widget with InputHSV:");
1341 ImGui::ColorEdit4(label: "HSV shown as RGB##1", col: (float*)&color_hsv, flags: ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1342 ImGui::ColorEdit4(label: "HSV shown as HSV##1", col: (float*)&color_hsv, flags: ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1343 ImGui::DragFloat4(label: "Raw HSV values", v: (float*)&color_hsv, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
1344
1345 ImGui::TreePop();
1346 }
1347}
1348
1349//-----------------------------------------------------------------------------
1350// [SECTION] DemoWindowWidgetsComboBoxes()
1351//-----------------------------------------------------------------------------
1352
1353static void DemoWindowWidgetsComboBoxes()
1354{
1355 IMGUI_DEMO_MARKER("Widgets/Combo");
1356 if (ImGui::TreeNode(label: "Combo"))
1357 {
1358 // Combo Boxes are also called "Dropdown" in other systems
1359 // Expose flags as checkbox for the demo
1360 static ImGuiComboFlags flags = 0;
1361 ImGui::CheckboxFlags(label: "ImGuiComboFlags_PopupAlignLeft", flags: &flags, flags_value: ImGuiComboFlags_PopupAlignLeft);
1362 ImGui::SameLine(); HelpMarker(desc: "Only makes a difference if the popup is larger than the combo");
1363 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_NoArrowButton", flags: &flags, flags_value: ImGuiComboFlags_NoArrowButton))
1364 flags &= ~ImGuiComboFlags_NoPreview; // Clear incompatible flags
1365 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_NoPreview", flags: &flags, flags_value: ImGuiComboFlags_NoPreview))
1366 flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags
1367 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_WidthFitPreview", flags: &flags, flags_value: ImGuiComboFlags_WidthFitPreview))
1368 flags &= ~ImGuiComboFlags_NoPreview;
1369
1370 // Override default popup height
1371 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_HeightSmall", flags: &flags, flags_value: ImGuiComboFlags_HeightSmall))
1372 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall);
1373 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_HeightRegular", flags: &flags, flags_value: ImGuiComboFlags_HeightRegular))
1374 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular);
1375 if (ImGui::CheckboxFlags(label: "ImGuiComboFlags_HeightLargest", flags: &flags, flags_value: ImGuiComboFlags_HeightLargest))
1376 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest);
1377
1378 // Using the generic BeginCombo() API, you have full control over how to display the combo contents.
1379 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1380 // stored in the object itself, etc.)
1381 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1382 static int item_selected_idx = 0; // Here we store our selection data as an index.
1383
1384 // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[])
1385 const char* combo_preview_value = items[item_selected_idx];
1386 if (ImGui::BeginCombo(label: "combo 1", preview_value: combo_preview_value, flags))
1387 {
1388 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1389 {
1390 const bool is_selected = (item_selected_idx == n);
1391 if (ImGui::Selectable(label: items[n], selected: is_selected))
1392 item_selected_idx = n;
1393
1394 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1395 if (is_selected)
1396 ImGui::SetItemDefaultFocus();
1397 }
1398 ImGui::EndCombo();
1399 }
1400
1401 // Show case embedding a filter using a simple trick: displaying the filter inside combo contents.
1402 // See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives.
1403 if (ImGui::BeginCombo(label: "combo 2 (w/ filter)", preview_value: combo_preview_value, flags))
1404 {
1405 static ImGuiTextFilter filter;
1406 if (ImGui::IsWindowAppearing())
1407 {
1408 ImGui::SetKeyboardFocusHere();
1409 filter.Clear();
1410 }
1411 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_F);
1412 filter.Draw(label: "##Filter", width: -FLT_MIN);
1413
1414 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1415 {
1416 const bool is_selected = (item_selected_idx == n);
1417 if (filter.PassFilter(text: items[n]))
1418 if (ImGui::Selectable(label: items[n], selected: is_selected))
1419 item_selected_idx = n;
1420 }
1421 ImGui::EndCombo();
1422 }
1423
1424 ImGui::Spacing();
1425 ImGui::SeparatorText(label: "One-liner variants");
1426 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.");
1427
1428 // Simplified one-liner Combo() API, using values packed in a single constant string
1429 // This is a convenience for when the selection set is small and known at compile-time.
1430 static int item_current_2 = 0;
1431 ImGui::Combo(label: "combo 3 (one-liner)", current_item: &item_current_2, items_separated_by_zeros: "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
1432
1433 // Simplified one-liner Combo() using an array of const char*
1434 // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
1435 static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview
1436 ImGui::Combo(label: "combo 4 (array)", current_item: &item_current_3, items, IM_ARRAYSIZE(items));
1437
1438 // Simplified one-liner Combo() using an accessor function
1439 static int item_current_4 = 0;
1440 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));
1441
1442 ImGui::TreePop();
1443 }
1444}
1445
1446//-----------------------------------------------------------------------------
1447// [SECTION] DemoWindowWidgetsDataTypes()
1448//-----------------------------------------------------------------------------
1449
1450static void DemoWindowWidgetsDataTypes()
1451{
1452 IMGUI_DEMO_MARKER("Widgets/Data Types");
1453 if (ImGui::TreeNode(label: "Data Types"))
1454 {
1455 // DragScalar/InputScalar/SliderScalar functions allow various data types
1456 // - signed/unsigned
1457 // - 8/16/32/64-bits
1458 // - integer/float/double
1459 // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
1460 // to pass the type, and passing all arguments by pointer.
1461 // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type.
1462 // In practice, if you frequently use a given type that is not covered by the normal API entry points,
1463 // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
1464 // and then pass their address to the generic function. For example:
1465 // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
1466 // {
1467 // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
1468 // }
1469
1470 // Setup limits (as helper variables so we can take their address, as explained above)
1471 // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
1472 #ifndef LLONG_MIN
1473 ImS64 LLONG_MIN = -9223372036854775807LL - 1;
1474 ImS64 LLONG_MAX = 9223372036854775807LL;
1475 ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);
1476 #endif
1477 const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127;
1478 const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255;
1479 const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767;
1480 const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535;
1481 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;
1482 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;
1483 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;
1484 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;
1485 const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;
1486 const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;
1487
1488 // State
1489 static char s8_v = 127;
1490 static ImU8 u8_v = 255;
1491 static short s16_v = 32767;
1492 static ImU16 u16_v = 65535;
1493 static ImS32 s32_v = -1;
1494 static ImU32 u32_v = (ImU32)-1;
1495 static ImS64 s64_v = -1;
1496 static ImU64 u64_v = (ImU64)-1;
1497 static float f32_v = 0.123f;
1498 static double f64_v = 90000.01234567890123456789;
1499
1500 const float drag_speed = 0.2f;
1501 static bool drag_clamp = false;
1502 IMGUI_DEMO_MARKER("Widgets/Data Types/Drags");
1503 ImGui::SeparatorText(label: "Drags");
1504 ImGui::Checkbox(label: "Clamp integers to 0..50", v: &drag_clamp);
1505 ImGui::SameLine(); HelpMarker(
1506 desc: "As with every widget in dear imgui, we never modify values unless there is a user interaction.\n"
1507 "You can override the clamping limits by using CTRL+Click to input a value.");
1508 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);
1509 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");
1510 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);
1511 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");
1512 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);
1513 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");
1514 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");
1515 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);
1516 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);
1517 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");
1518 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);
1519 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");
1520 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);
1521
1522 IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders");
1523 ImGui::SeparatorText(label: "Sliders");
1524 ImGui::SliderScalar(label: "slider s8 full", data_type: ImGuiDataType_S8, p_data: &s8_v, p_min: &s8_min, p_max: &s8_max, format: "%d");
1525 ImGui::SliderScalar(label: "slider u8 full", data_type: ImGuiDataType_U8, p_data: &u8_v, p_min: &u8_min, p_max: &u8_max, format: "%u");
1526 ImGui::SliderScalar(label: "slider s16 full", data_type: ImGuiDataType_S16, p_data: &s16_v, p_min: &s16_min, p_max: &s16_max, format: "%d");
1527 ImGui::SliderScalar(label: "slider u16 full", data_type: ImGuiDataType_U16, p_data: &u16_v, p_min: &u16_min, p_max: &u16_max, format: "%u");
1528 ImGui::SliderScalar(label: "slider s32 low", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_zero, p_max: &s32_fifty,format: "%d");
1529 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");
1530 ImGui::SliderScalar(label: "slider s32 full", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_min, p_max: &s32_max, format: "%d");
1531 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");
1532 ImGui::SliderScalar(label: "slider u32 low", data_type: ImGuiDataType_U32, p_data: &u32_v, p_min: &u32_zero, p_max: &u32_fifty,format: "%u");
1533 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");
1534 ImGui::SliderScalar(label: "slider u32 full", data_type: ImGuiDataType_U32, p_data: &u32_v, p_min: &u32_min, p_max: &u32_max, format: "%u");
1535 ImGui::SliderScalar(label: "slider s64 low", data_type: ImGuiDataType_S64, p_data: &s64_v, p_min: &s64_zero, p_max: &s64_fifty,format: "%" PRId64);
1536 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);
1537 ImGui::SliderScalar(label: "slider s64 full", data_type: ImGuiDataType_S64, p_data: &s64_v, p_min: &s64_min, p_max: &s64_max, format: "%" PRId64);
1538 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");
1539 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");
1540 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");
1541 ImGui::SliderScalar(label: "slider float low", data_type: ImGuiDataType_Float, p_data: &f32_v, p_min: &f32_zero, p_max: &f32_one);
1542 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);
1543 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");
1544 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");
1545 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);
1546 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");
1547
1548 ImGui::SeparatorText(label: "Sliders (reverse)");
1549 ImGui::SliderScalar(label: "slider s8 reverse", data_type: ImGuiDataType_S8, p_data: &s8_v, p_min: &s8_max, p_max: &s8_min, format: "%d");
1550 ImGui::SliderScalar(label: "slider u8 reverse", data_type: ImGuiDataType_U8, p_data: &u8_v, p_min: &u8_max, p_max: &u8_min, format: "%u");
1551 ImGui::SliderScalar(label: "slider s32 reverse", data_type: ImGuiDataType_S32, p_data: &s32_v, p_min: &s32_fifty, p_max: &s32_zero, format: "%d");
1552 ImGui::SliderScalar(label: "slider u32 reverse", data_type: ImGuiDataType_U32, p_data: &u32_v, p_min: &u32_fifty, p_max: &u32_zero, format: "%u");
1553 ImGui::SliderScalar(label: "slider s64 reverse", data_type: ImGuiDataType_S64, p_data: &s64_v, p_min: &s64_fifty, p_max: &s64_zero, format: "%" PRId64);
1554 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");
1555
1556 IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs");
1557 static bool inputs_step = true;
1558 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
1559 ImGui::SeparatorText(label: "Inputs");
1560 ImGui::Checkbox(label: "Show step buttons", v: &inputs_step);
1561 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ReadOnly", flags: &flags, flags_value: ImGuiInputTextFlags_ReadOnly);
1562 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ParseEmptyRefVal", flags: &flags, flags_value: ImGuiInputTextFlags_ParseEmptyRefVal);
1563 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_DisplayEmptyRefVal", flags: &flags, flags_value: ImGuiInputTextFlags_DisplayEmptyRefVal);
1564 ImGui::InputScalar(label: "input s8", data_type: ImGuiDataType_S8, p_data: &s8_v, p_step: inputs_step ? &s8_one : NULL, NULL, format: "%d", flags);
1565 ImGui::InputScalar(label: "input u8", data_type: ImGuiDataType_U8, p_data: &u8_v, p_step: inputs_step ? &u8_one : NULL, NULL, format: "%u", flags);
1566 ImGui::InputScalar(label: "input s16", data_type: ImGuiDataType_S16, p_data: &s16_v, p_step: inputs_step ? &s16_one : NULL, NULL, format: "%d", flags);
1567 ImGui::InputScalar(label: "input u16", data_type: ImGuiDataType_U16, p_data: &u16_v, p_step: inputs_step ? &u16_one : NULL, NULL, format: "%u", flags);
1568 ImGui::InputScalar(label: "input s32", data_type: ImGuiDataType_S32, p_data: &s32_v, p_step: inputs_step ? &s32_one : NULL, NULL, format: "%d", flags);
1569 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);
1570 ImGui::InputScalar(label: "input u32", data_type: ImGuiDataType_U32, p_data: &u32_v, p_step: inputs_step ? &u32_one : NULL, NULL, format: "%u", flags);
1571 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);
1572 ImGui::InputScalar(label: "input s64", data_type: ImGuiDataType_S64, p_data: &s64_v, p_step: inputs_step ? &s64_one : NULL, NULL, NULL, flags);
1573 ImGui::InputScalar(label: "input u64", data_type: ImGuiDataType_U64, p_data: &u64_v, p_step: inputs_step ? &u64_one : NULL, NULL, NULL, flags);
1574 ImGui::InputScalar(label: "input float", data_type: ImGuiDataType_Float, p_data: &f32_v, p_step: inputs_step ? &f32_one : NULL, NULL, NULL, flags);
1575 ImGui::InputScalar(label: "input double", data_type: ImGuiDataType_Double, p_data: &f64_v, p_step: inputs_step ? &f64_one : NULL, NULL, NULL, flags);
1576
1577 ImGui::TreePop();
1578 }
1579}
1580
1581//-----------------------------------------------------------------------------
1582// [SECTION] DemoWindowWidgetsDisableBlocks()
1583//-----------------------------------------------------------------------------
1584
1585static void DemoWindowWidgetsDisableBlocks(ImGuiDemoWindowData* demo_data)
1586{
1587 IMGUI_DEMO_MARKER("Widgets/Disable Blocks");
1588 if (ImGui::TreeNode(label: "Disable Blocks"))
1589 {
1590 ImGui::Checkbox(label: "Disable entire section above", v: &demo_data->DisableSections);
1591 ImGui::SameLine(); HelpMarker(desc: "Demonstrate using BeginDisabled()/EndDisabled() across other sections.");
1592 ImGui::TreePop();
1593 }
1594}
1595
1596//-----------------------------------------------------------------------------
1597// [SECTION] DemoWindowWidgetsDragAndDrop()
1598//-----------------------------------------------------------------------------
1599
1600static void DemoWindowWidgetsDragAndDrop()
1601{
1602 IMGUI_DEMO_MARKER("Widgets/Drag and drop");
1603 if (ImGui::TreeNode(label: "Drag and Drop"))
1604 {
1605 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets");
1606 if (ImGui::TreeNode(label: "Drag and drop in standard widgets"))
1607 {
1608 // ColorEdit widgets automatically act as drag source and drag target.
1609 // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
1610 // to allow your own widgets to use colors in their drag and drop interaction.
1611 // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
1612 HelpMarker(desc: "You can drag from the color squares.");
1613 static float col1[3] = { 1.0f, 0.0f, 0.2f };
1614 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1615 ImGui::ColorEdit3(label: "color 1", col: col1);
1616 ImGui::ColorEdit4(label: "color 2", col: col2);
1617 ImGui::TreePop();
1618 }
1619
1620 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items");
1621 if (ImGui::TreeNode(label: "Drag and drop to copy/swap items"))
1622 {
1623 enum Mode
1624 {
1625 Mode_Copy,
1626 Mode_Move,
1627 Mode_Swap
1628 };
1629 static int mode = 0;
1630 if (ImGui::RadioButton(label: "Copy", active: mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();
1631 if (ImGui::RadioButton(label: "Move", active: mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();
1632 if (ImGui::RadioButton(label: "Swap", active: mode == Mode_Swap)) { mode = Mode_Swap; }
1633 static const char* names[9] =
1634 {
1635 "Bobby", "Beatrice", "Betty",
1636 "Brianna", "Barry", "Bernard",
1637 "Bibi", "Blaine", "Bryn"
1638 };
1639 for (int n = 0; n < IM_ARRAYSIZE(names); n++)
1640 {
1641 ImGui::PushID(int_id: n);
1642 if ((n % 3) != 0)
1643 ImGui::SameLine();
1644 ImGui::Button(label: names[n], size: ImVec2(60, 60));
1645
1646 // Our buttons are both drag sources and drag targets here!
1647 if (ImGui::BeginDragDropSource(flags: ImGuiDragDropFlags_None))
1648 {
1649 // Set payload to carry the index of our item (could be anything)
1650 ImGui::SetDragDropPayload(type: "DND_DEMO_CELL", data: &n, sz: sizeof(int));
1651
1652 // Display preview (could be anything, e.g. when dragging an image we could decide to display
1653 // the filename and a small preview of the image, etc.)
1654 if (mode == Mode_Copy) { ImGui::Text(fmt: "Copy %s", names[n]); }
1655 if (mode == Mode_Move) { ImGui::Text(fmt: "Move %s", names[n]); }
1656 if (mode == Mode_Swap) { ImGui::Text(fmt: "Swap %s", names[n]); }
1657 ImGui::EndDragDropSource();
1658 }
1659 if (ImGui::BeginDragDropTarget())
1660 {
1661 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(type: "DND_DEMO_CELL"))
1662 {
1663 IM_ASSERT(payload->DataSize == sizeof(int));
1664 int payload_n = *(const int*)payload->Data;
1665 if (mode == Mode_Copy)
1666 {
1667 names[n] = names[payload_n];
1668 }
1669 if (mode == Mode_Move)
1670 {
1671 names[n] = names[payload_n];
1672 names[payload_n] = "";
1673 }
1674 if (mode == Mode_Swap)
1675 {
1676 const char* tmp = names[n];
1677 names[n] = names[payload_n];
1678 names[payload_n] = tmp;
1679 }
1680 }
1681 ImGui::EndDragDropTarget();
1682 }
1683 ImGui::PopID();
1684 }
1685 ImGui::TreePop();
1686 }
1687
1688 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)");
1689 if (ImGui::TreeNode(label: "Drag to reorder items (simple)"))
1690 {
1691 // FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
1692 // This code was always slightly faulty but in a way which was not easily noticeable.
1693 // Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
1694 ImGui::PushItemFlag(option: ImGuiItemFlags_AllowDuplicateId, enabled: true);
1695
1696 // Simple reordering
1697 HelpMarker(
1698 desc: "We don't use the drag and drop api at all here! "
1699 "Instead we query when the item is held but not hovered, and order items accordingly.");
1700 static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
1701 for (int n = 0; n < IM_ARRAYSIZE(item_names); n++)
1702 {
1703 const char* item = item_names[n];
1704 ImGui::Selectable(label: item);
1705
1706 if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
1707 {
1708 int n_next = n + (ImGui::GetMouseDragDelta(button: 0).y < 0.f ? -1 : 1);
1709 if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names))
1710 {
1711 item_names[n] = item_names[n_next];
1712 item_names[n_next] = item;
1713 ImGui::ResetMouseDragDelta();
1714 }
1715 }
1716 }
1717
1718 ImGui::PopItemFlag();
1719 ImGui::TreePop();
1720 }
1721
1722 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location");
1723 if (ImGui::TreeNode(label: "Tooltip at target location"))
1724 {
1725 for (int n = 0; n < 2; n++)
1726 {
1727 // Drop targets
1728 ImGui::Button(label: n ? "drop here##1" : "drop here##0");
1729 if (ImGui::BeginDragDropTarget())
1730 {
1731 ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip;
1732 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, flags: drop_target_flags))
1733 {
1734 IM_UNUSED(payload);
1735 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
1736 ImGui::SetTooltip("Cannot drop here!");
1737 }
1738 ImGui::EndDragDropTarget();
1739 }
1740
1741 // Drop source
1742 static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f };
1743 if (n == 0)
1744 ImGui::ColorButton(desc_id: "drag me", col: col4);
1745
1746 }
1747 ImGui::TreePop();
1748 }
1749
1750 ImGui::TreePop();
1751 }
1752}
1753
1754//-----------------------------------------------------------------------------
1755// [SECTION] DemoWindowWidgetsDragsAndSliders()
1756//-----------------------------------------------------------------------------
1757
1758static void DemoWindowWidgetsDragsAndSliders()
1759{
1760 IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags");
1761 if (ImGui::TreeNode(label: "Drag/Slider Flags"))
1762 {
1763 // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
1764 static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
1765 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_AlwaysClamp", flags: &flags, flags_value: ImGuiSliderFlags_AlwaysClamp);
1766 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_ClampOnInput", flags: &flags, flags_value: ImGuiSliderFlags_ClampOnInput);
1767 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.");
1768 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_ClampZeroRange", flags: &flags, flags_value: ImGuiSliderFlags_ClampZeroRange);
1769 ImGui::SameLine(); HelpMarker(desc: "Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
1770 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_Logarithmic", flags: &flags, flags_value: ImGuiSliderFlags_Logarithmic);
1771 ImGui::SameLine(); HelpMarker(desc: "Enable logarithmic editing (more precision for small values).");
1772 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_NoRoundToFormat", flags: &flags, flags_value: ImGuiSliderFlags_NoRoundToFormat);
1773 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).");
1774 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_NoInput", flags: &flags, flags_value: ImGuiSliderFlags_NoInput);
1775 ImGui::SameLine(); HelpMarker(desc: "Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
1776 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_NoSpeedTweaks", flags: &flags, flags_value: ImGuiSliderFlags_NoSpeedTweaks);
1777 ImGui::SameLine(); HelpMarker(desc: "Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
1778 ImGui::CheckboxFlags(label: "ImGuiSliderFlags_WrapAround", flags: &flags, flags_value: ImGuiSliderFlags_WrapAround);
1779 ImGui::SameLine(); HelpMarker(desc: "Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)");
1780
1781 // Drags
1782 static float drag_f = 0.5f;
1783 static int drag_i = 50;
1784 ImGui::Text(fmt: "Underlying float value: %f", drag_f);
1785 ImGui::DragFloat(label: "DragFloat (0 -> 1)", v: &drag_f, v_speed: 0.005f, v_min: 0.0f, v_max: 1.0f, format: "%.3f", flags);
1786 ImGui::DragFloat(label: "DragFloat (0 -> +inf)", v: &drag_f, v_speed: 0.005f, v_min: 0.0f, FLT_MAX, format: "%.3f", flags);
1787 ImGui::DragFloat(label: "DragFloat (-inf -> 1)", v: &drag_f, v_speed: 0.005f, v_min: -FLT_MAX, v_max: 1.0f, format: "%.3f", flags);
1788 ImGui::DragFloat(label: "DragFloat (-inf -> +inf)", v: &drag_f, v_speed: 0.005f, v_min: -FLT_MAX, v_max: +FLT_MAX, format: "%.3f", flags);
1789 //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange
1790 //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags);
1791 ImGui::DragInt(label: "DragInt (0 -> 100)", v: &drag_i, v_speed: 0.5f, v_min: 0, v_max: 100, format: "%d", flags);
1792
1793 // Sliders
1794 static float slider_f = 0.5f;
1795 static int slider_i = 50;
1796 const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround;
1797 ImGui::Text(fmt: "Underlying float value: %f", slider_f);
1798 ImGui::SliderFloat(label: "SliderFloat (0 -> 1)", v: &slider_f, v_min: 0.0f, v_max: 1.0f, format: "%.3f", flags: flags_for_sliders);
1799 ImGui::SliderInt(label: "SliderInt (0 -> 100)", v: &slider_i, v_min: 0, v_max: 100, format: "%d", flags: flags_for_sliders);
1800
1801 ImGui::TreePop();
1802 }
1803}
1804
1805//-----------------------------------------------------------------------------
1806// [SECTION] DemoWindowWidgetsFonts()
1807//-----------------------------------------------------------------------------
1808
1809// Forward declare ShowFontAtlas() which isn't worth putting in public API yet
1810namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }
1811
1812static void DemoWindowWidgetsFonts()
1813{
1814 IMGUI_DEMO_MARKER("Widgets/Fonts");
1815 if (ImGui::TreeNode(label: "Fonts"))
1816 {
1817 ImFontAtlas* atlas = ImGui::GetIO().Fonts;
1818 ImGui::ShowFontAtlas(atlas);
1819 // FIXME-NEWATLAS: Provide a demo to add/create a procedural font?
1820 ImGui::TreePop();
1821 }
1822}
1823
1824//-----------------------------------------------------------------------------
1825// [SECTION] DemoWindowWidgetsImages()
1826//-----------------------------------------------------------------------------
1827
1828static void DemoWindowWidgetsImages()
1829{
1830 IMGUI_DEMO_MARKER("Widgets/Images");
1831 if (ImGui::TreeNode(label: "Images"))
1832 {
1833 ImGuiIO& io = ImGui::GetIO();
1834 ImGui::TextWrapped(
1835 fmt: "Below we are displaying the font texture (which is the only texture we have access to in this demo). "
1836 "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. "
1837 "Hover the texture for a zoomed view!");
1838
1839 // Below we are displaying the font texture because it is the only texture we have access to inside the demo!
1840 // Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers.
1841 // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
1842 // of their respective source file to specify what they are using as texture identifier, for example:
1843 // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer.
1844 // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
1845 // So with the DirectX11 backend, you call ImGui::Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID.
1846 // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
1847 // to ImGui::Image(), and gather width/height through your own functions, etc.
1848 // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
1849 // it will help you debug issues if you are confused about it.
1850 // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
1851 // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
1852 // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1853
1854 // Grab the current texture identifier used by the font atlas.
1855 ImTextureRef my_tex_id = io.Fonts->TexRef;
1856
1857 // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.
1858 float my_tex_w = (float)io.Fonts->TexData->Width;
1859 float my_tex_h = (float)io.Fonts->TexData->Height;
1860
1861 {
1862 ImGui::Text(fmt: "%.0fx%.0f", my_tex_w, my_tex_h);
1863 ImVec2 pos = ImGui::GetCursorScreenPos();
1864 ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
1865 ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
1866 ImGui::PushStyleVar(idx: ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
1867 ImGui::ImageWithBg(tex_ref: 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));
1868 if (ImGui::BeginItemTooltip())
1869 {
1870 float region_sz = 32.0f;
1871 float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
1872 float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
1873 float zoom = 4.0f;
1874 if (region_x < 0.0f) { region_x = 0.0f; }
1875 else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }
1876 if (region_y < 0.0f) { region_y = 0.0f; }
1877 else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }
1878 ImGui::Text(fmt: "Min: (%.2f, %.2f)", region_x, region_y);
1879 ImGui::Text(fmt: "Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
1880 ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
1881 ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
1882 ImGui::ImageWithBg(tex_ref: 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));
1883 ImGui::EndTooltip();
1884 }
1885 ImGui::PopStyleVar();
1886 }
1887
1888 IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons");
1889 ImGui::TextWrapped(fmt: "And now some textured buttons..");
1890 static int pressed_count = 0;
1891 for (int i = 0; i < 8; i++)
1892 {
1893 // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.
1894 // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.
1895 // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1896 ImGui::PushID(int_id: i);
1897 if (i > 0)
1898 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(i - 1.0f, i - 1.0f));
1899 ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible
1900 ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left
1901 ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h); // UV coordinates for (32,32) in our texture
1902 ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background
1903 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
1904 if (ImGui::ImageButton(str_id: "", tex_ref: my_tex_id, image_size: size, uv0, uv1, bg_col, tint_col))
1905 pressed_count += 1;
1906 if (i > 0)
1907 ImGui::PopStyleVar();
1908 ImGui::PopID();
1909 ImGui::SameLine();
1910 }
1911 ImGui::NewLine();
1912 ImGui::Text(fmt: "Pressed %d times.", pressed_count);
1913 ImGui::TreePop();
1914 }
1915}
1916
1917//-----------------------------------------------------------------------------
1918// [SECTION] DemoWindowWidgetsListBoxes()
1919//-----------------------------------------------------------------------------
1920
1921static void DemoWindowWidgetsListBoxes()
1922{
1923 IMGUI_DEMO_MARKER("Widgets/List Boxes");
1924 if (ImGui::TreeNode(label: "List Boxes"))
1925 {
1926 // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild()
1927 // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
1928 // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild()
1929 // to always be called (inconsistent with BeginListBox()/EndListBox()).
1930
1931 // Using the generic BeginListBox() API, you have full control over how to display the combo contents.
1932 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1933 // stored in the object itself, etc.)
1934 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1935 static int item_selected_idx = 0; // Here we store our selected data as an index.
1936
1937 static bool item_highlight = false;
1938 int item_highlighted_idx = -1; // Here we store our highlighted data as an index.
1939 ImGui::Checkbox(label: "Highlight hovered item in second listbox", v: &item_highlight);
1940
1941 if (ImGui::BeginListBox(label: "listbox 1"))
1942 {
1943 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1944 {
1945 const bool is_selected = (item_selected_idx == n);
1946 if (ImGui::Selectable(label: items[n], selected: is_selected))
1947 item_selected_idx = n;
1948
1949 if (item_highlight && ImGui::IsItemHovered())
1950 item_highlighted_idx = n;
1951
1952 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1953 if (is_selected)
1954 ImGui::SetItemDefaultFocus();
1955 }
1956 ImGui::EndListBox();
1957 }
1958 ImGui::SameLine(); HelpMarker(desc: "Here we are sharing selection state between both boxes.");
1959
1960 // Custom size: use all width, 5 items tall
1961 ImGui::Text(fmt: "Full-width:");
1962 if (ImGui::BeginListBox(label: "##listbox 2", size: ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
1963 {
1964 for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1965 {
1966 bool is_selected = (item_selected_idx == n);
1967 ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0;
1968 if (ImGui::Selectable(label: items[n], selected: is_selected, flags))
1969 item_selected_idx = n;
1970
1971 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1972 if (is_selected)
1973 ImGui::SetItemDefaultFocus();
1974 }
1975 ImGui::EndListBox();
1976 }
1977
1978 ImGui::TreePop();
1979 }
1980}
1981
1982//-----------------------------------------------------------------------------
1983// [SECTION] DemoWindowWidgetsMultiComponents()
1984//-----------------------------------------------------------------------------
1985
1986static void DemoWindowWidgetsMultiComponents()
1987{
1988 IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets");
1989 if (ImGui::TreeNode(label: "Multi-component Widgets"))
1990 {
1991 static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
1992 static int vec4i[4] = { 1, 5, 100, 255 };
1993
1994 ImGui::SeparatorText(label: "2-wide");
1995 ImGui::InputFloat2(label: "input float2", v: vec4f);
1996 ImGui::DragFloat2(label: "drag float2", v: vec4f, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
1997 ImGui::SliderFloat2(label: "slider float2", v: vec4f, v_min: 0.0f, v_max: 1.0f);
1998 ImGui::InputInt2(label: "input int2", v: vec4i);
1999 ImGui::DragInt2(label: "drag int2", v: vec4i, v_speed: 1, v_min: 0, v_max: 255);
2000 ImGui::SliderInt2(label: "slider int2", v: vec4i, v_min: 0, v_max: 255);
2001
2002 ImGui::SeparatorText(label: "3-wide");
2003 ImGui::InputFloat3(label: "input float3", v: vec4f);
2004 ImGui::DragFloat3(label: "drag float3", v: vec4f, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
2005 ImGui::SliderFloat3(label: "slider float3", v: vec4f, v_min: 0.0f, v_max: 1.0f);
2006 ImGui::InputInt3(label: "input int3", v: vec4i);
2007 ImGui::DragInt3(label: "drag int3", v: vec4i, v_speed: 1, v_min: 0, v_max: 255);
2008 ImGui::SliderInt3(label: "slider int3", v: vec4i, v_min: 0, v_max: 255);
2009
2010 ImGui::SeparatorText(label: "4-wide");
2011 ImGui::InputFloat4(label: "input float4", v: vec4f);
2012 ImGui::DragFloat4(label: "drag float4", v: vec4f, v_speed: 0.01f, v_min: 0.0f, v_max: 1.0f);
2013 ImGui::SliderFloat4(label: "slider float4", v: vec4f, v_min: 0.0f, v_max: 1.0f);
2014 ImGui::InputInt4(label: "input int4", v: vec4i);
2015 ImGui::DragInt4(label: "drag int4", v: vec4i, v_speed: 1, v_min: 0, v_max: 255);
2016 ImGui::SliderInt4(label: "slider int4", v: vec4i, v_min: 0, v_max: 255);
2017
2018 ImGui::SeparatorText(label: "Ranges");
2019 static float begin = 10, end = 90;
2020 static int begin_i = 100, end_i = 1000;
2021 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);
2022 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");
2023 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");
2024
2025 ImGui::TreePop();
2026 }
2027}
2028
2029//-----------------------------------------------------------------------------
2030// [SECTION] DemoWindowWidgetsPlotting()
2031//-----------------------------------------------------------------------------
2032
2033static void DemoWindowWidgetsPlotting()
2034{
2035 // Plot/Graph widgets are not very good.
2036// Consider using a third-party library such as ImPlot: https://github.com/epezent/implot
2037// (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)
2038 IMGUI_DEMO_MARKER("Widgets/Plotting");
2039 if (ImGui::TreeNode(label: "Plotting"))
2040 {
2041 ImGui::Text(fmt: "Need better plotting and graphing? Consider using ImPlot:");
2042 ImGui::TextLinkOpenURL(label: "https://github.com/epezent/implot");
2043 ImGui::Separator();
2044
2045 static bool animate = true;
2046 ImGui::Checkbox(label: "Animate", v: &animate);
2047
2048 // Plot as lines and plot as histogram
2049 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
2050 ImGui::PlotLines(label: "Frame Times", values: arr, IM_ARRAYSIZE(arr));
2051 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));
2052 //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!");
2053
2054 // Fill an array of contiguous float values to plot
2055 // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
2056 // and the sizeof() of your structure in the "stride" parameter.
2057 static float values[90] = {};
2058 static int values_offset = 0;
2059 static double refresh_time = 0.0;
2060 if (!animate || refresh_time == 0.0)
2061 refresh_time = ImGui::GetTime();
2062 while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
2063 {
2064 static float phase = 0.0f;
2065 values[values_offset] = cosf(x: phase);
2066 values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
2067 phase += 0.10f * values_offset;
2068 refresh_time += 1.0f / 60.0f;
2069 }
2070
2071 // Plots can display overlay texts
2072 // (in this example, we will display an average value)
2073 {
2074 float average = 0.0f;
2075 for (int n = 0; n < IM_ARRAYSIZE(values); n++)
2076 average += values[n];
2077 average /= (float)IM_ARRAYSIZE(values);
2078 char overlay[32];
2079 sprintf(s: overlay, format: "avg %f", average);
2080 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));
2081 }
2082
2083 // Use functions to generate output
2084 // FIXME: This is actually VERY awkward because current plot API only pass in indices.
2085 // We probably want an API passing floats and user provide sample rate/count.
2086 struct Funcs
2087 {
2088 static float Sin(void*, int i) { return sinf(x: i * 0.1f); }
2089 static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }
2090 };
2091 static int func_type = 0, display_count = 70;
2092 ImGui::SeparatorText(label: "Functions");
2093 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
2094 ImGui::Combo(label: "func", current_item: &func_type, items_separated_by_zeros: "Sin\0Saw\0");
2095 ImGui::SameLine();
2096 ImGui::SliderInt(label: "Sample count", v: &display_count, v_min: 1, v_max: 400);
2097 float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
2098 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));
2099 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));
2100
2101 ImGui::TreePop();
2102 }
2103}
2104
2105//-----------------------------------------------------------------------------
2106// [SECTION] DemoWindowWidgetsProgressBars()
2107//-----------------------------------------------------------------------------
2108
2109static void DemoWindowWidgetsProgressBars()
2110{
2111 IMGUI_DEMO_MARKER("Widgets/Progress Bars");
2112 if (ImGui::TreeNode(label: "Progress Bars"))
2113 {
2114 // Animate a simple progress bar
2115 static float progress = 0.0f, progress_dir = 1.0f;
2116 progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
2117 if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; }
2118 if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; }
2119
2120 // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
2121 // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
2122 ImGui::ProgressBar(fraction: progress, size_arg: ImVec2(0.0f, 0.0f));
2123 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
2124 ImGui::Text(fmt: "Progress Bar");
2125
2126 float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f);
2127 char buf[32];
2128 sprintf(s: buf, format: "%d/%d", (int)(progress_saturated * 1753), 1753);
2129 ImGui::ProgressBar(fraction: progress, size_arg: ImVec2(0.f, 0.f), overlay: buf);
2130
2131 // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value.
2132 // Adjust the factor if you want to adjust the animation speed.
2133 ImGui::ProgressBar(fraction: -1.0f * (float)ImGui::GetTime(), size_arg: ImVec2(0.0f, 0.0f), overlay: "Searching..");
2134 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
2135 ImGui::Text(fmt: "Indeterminate");
2136
2137 ImGui::TreePop();
2138 }
2139}
2140
2141//-----------------------------------------------------------------------------
2142// [SECTION] DemoWindowWidgetsQueryingStatuses()
2143//-----------------------------------------------------------------------------
2144
2145static void DemoWindowWidgetsQueryingStatuses()
2146{
2147 IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)");
2148 if (ImGui::TreeNode(label: "Querying Item Status (Edited/Active/Hovered etc.)"))
2149 {
2150 // Select an item type
2151 const char* item_names[] =
2152 {
2153 "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat",
2154 "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox"
2155 };
2156 static int item_type = 4;
2157 static bool item_disabled = false;
2158 ImGui::Combo(label: "Item Type", current_item: &item_type, items: item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names));
2159 ImGui::SameLine();
2160 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().");
2161 ImGui::Checkbox(label: "Item Disabled", v: &item_disabled);
2162
2163 // Submit selected items so we can query their status in the code following it.
2164 bool ret = false;
2165 static bool b = false;
2166 static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
2167 static char str[16] = {};
2168 if (item_disabled)
2169 ImGui::BeginDisabled(disabled: true);
2170 if (item_type == 0) { ImGui::Text(fmt: "ITEM: Text"); } // Testing text items with no identifier/interaction
2171 if (item_type == 1) { ret = ImGui::Button(label: "ITEM: Button"); } // Testing button
2172 if (item_type == 2) { ImGui::PushItemFlag(option: ImGuiItemFlags_ButtonRepeat, enabled: true); ret = ImGui::Button(label: "ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater)
2173 if (item_type == 3) { ret = ImGui::Checkbox(label: "ITEM: Checkbox", v: &b); } // Testing checkbox
2174 if (item_type == 4) { ret = ImGui::SliderFloat(label: "ITEM: SliderFloat", v: &col4f[0], v_min: 0.0f, v_max: 1.0f); } // Testing basic item
2175 if (item_type == 5) { ret = ImGui::InputText(label: "ITEM: InputText", buf: &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing)
2176 if (item_type == 6) { ret = ImGui::InputTextMultiline(label: "ITEM: InputTextMultiline", buf: &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window)
2177 if (item_type == 7) { ret = ImGui::InputFloat(label: "ITEM: InputFloat", v: col4f, step: 1.0f); } // Testing +/- buttons on scalar input
2178 if (item_type == 8) { ret = ImGui::InputFloat3(label: "ITEM: InputFloat3", v: col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2179 if (item_type == 9) { ret = ImGui::ColorEdit4(label: "ITEM: ColorEdit4", col: col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
2180 if (item_type == 10) { ret = ImGui::Selectable(label: "ITEM: Selectable"); } // Testing selectable item
2181 if (item_type == 11) { ret = ImGui::MenuItem(label: "ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)
2182 if (item_type == 12) { ret = ImGui::TreeNode(label: "ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node
2183 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.
2184 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)); }
2185 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)); }
2186
2187 bool hovered_delay_none = ImGui::IsItemHovered();
2188 bool hovered_delay_stationary = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_Stationary);
2189 bool hovered_delay_short = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayShort);
2190 bool hovered_delay_normal = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayNormal);
2191 bool hovered_delay_tooltip = ImGui::IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary
2192
2193 // Display the values of IsItemHovered() and other common item state functions.
2194 // Note that the ImGuiHoveredFlags_XXX flags can be combined.
2195 // Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
2196 // we query every state in a single call to avoid storing them and to simplify the code.
2197 ImGui::BulletText(
2198 fmt: "Return value = %d\n"
2199 "IsItemFocused() = %d\n"
2200 "IsItemHovered() = %d\n"
2201 "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
2202 "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2203 "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n"
2204 "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n"
2205 "IsItemHovered(_AllowWhenDisabled) = %d\n"
2206 "IsItemHovered(_RectOnly) = %d\n"
2207 "IsItemActive() = %d\n"
2208 "IsItemEdited() = %d\n"
2209 "IsItemActivated() = %d\n"
2210 "IsItemDeactivated() = %d\n"
2211 "IsItemDeactivatedAfterEdit() = %d\n"
2212 "IsItemVisible() = %d\n"
2213 "IsItemClicked() = %d\n"
2214 "IsItemToggledOpen() = %d\n"
2215 "GetItemRectMin() = (%.1f, %.1f)\n"
2216 "GetItemRectMax() = (%.1f, %.1f)\n"
2217 "GetItemRectSize() = (%.1f, %.1f)",
2218 ret,
2219 ImGui::IsItemFocused(),
2220 ImGui::IsItemHovered(),
2221 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2222 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2223 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenOverlappedByItem),
2224 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenOverlappedByWindow),
2225 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_AllowWhenDisabled),
2226 ImGui::IsItemHovered(flags: ImGuiHoveredFlags_RectOnly),
2227 ImGui::IsItemActive(),
2228 ImGui::IsItemEdited(),
2229 ImGui::IsItemActivated(),
2230 ImGui::IsItemDeactivated(),
2231 ImGui::IsItemDeactivatedAfterEdit(),
2232 ImGui::IsItemVisible(),
2233 ImGui::IsItemClicked(),
2234 ImGui::IsItemToggledOpen(),
2235 ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y,
2236 ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y,
2237 ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y
2238 );
2239 ImGui::BulletText(
2240 fmt: "with Hovering Delay or Stationary test:\n"
2241 "IsItemHovered() = %d\n"
2242 "IsItemHovered(_Stationary) = %d\n"
2243 "IsItemHovered(_DelayShort) = %d\n"
2244 "IsItemHovered(_DelayNormal) = %d\n"
2245 "IsItemHovered(_Tooltip) = %d",
2246 hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);
2247
2248 if (item_disabled)
2249 ImGui::EndDisabled();
2250
2251 char buf[1] = "";
2252 ImGui::InputText(label: "unused", buf, IM_ARRAYSIZE(buf), flags: ImGuiInputTextFlags_ReadOnly);
2253 ImGui::SameLine();
2254 HelpMarker(desc: "This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.");
2255
2256 ImGui::TreePop();
2257 }
2258
2259 IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)");
2260 if (ImGui::TreeNode(label: "Querying Window Status (Focused/Hovered etc.)"))
2261 {
2262 static bool embed_all_inside_a_child_window = false;
2263 ImGui::Checkbox(label: "Embed everything inside a child window for testing _RootWindow flag.", v: &embed_all_inside_a_child_window);
2264 if (embed_all_inside_a_child_window)
2265 ImGui::BeginChild(str_id: "outer_child", size: ImVec2(0, ImGui::GetFontSize() * 20.0f), child_flags: ImGuiChildFlags_Borders);
2266
2267 // Testing IsWindowFocused() function with its various flags.
2268 ImGui::BulletText(
2269 fmt: "IsWindowFocused() = %d\n"
2270 "IsWindowFocused(_ChildWindows) = %d\n"
2271 "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n"
2272 "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n"
2273 "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
2274 "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2275 "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
2276 "IsWindowFocused(_RootWindow) = %d\n"
2277 "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n"
2278 "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n"
2279 "IsWindowFocused(_AnyWindow) = %d\n",
2280 ImGui::IsWindowFocused(),
2281 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows),
2282 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),
2283 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy),
2284 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
2285 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2286 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
2287 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_RootWindow),
2288 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2289 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
2290 ImGui::IsWindowFocused(flags: ImGuiFocusedFlags_AnyWindow));
2291
2292 // Testing IsWindowHovered() function with its various flags.
2293 ImGui::BulletText(
2294 fmt: "IsWindowHovered() = %d\n"
2295 "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
2296 "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2297 "IsWindowHovered(_ChildWindows) = %d\n"
2298 "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n"
2299 "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n"
2300 "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
2301 "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2302 "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
2303 "IsWindowHovered(_RootWindow) = %d\n"
2304 "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
2305 "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n"
2306 "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
2307 "IsWindowHovered(_AnyWindow) = %d\n"
2308 "IsWindowHovered(_Stationary) = %d\n",
2309 ImGui::IsWindowHovered(),
2310 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2311 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2312 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows),
2313 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),
2314 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy),
2315 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
2316 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2317 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
2318 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_RootWindow),
2319 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2320 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
2321 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2322 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_AnyWindow),
2323 ImGui::IsWindowHovered(flags: ImGuiHoveredFlags_Stationary));
2324
2325 ImGui::BeginChild(str_id: "child", size: ImVec2(0, 50), child_flags: ImGuiChildFlags_Borders);
2326 ImGui::Text(fmt: "This is another child window for testing the _ChildWindows flag.");
2327 ImGui::EndChild();
2328 if (embed_all_inside_a_child_window)
2329 ImGui::EndChild();
2330
2331 // Calling IsItemHovered() after begin returns the hovered status of the title bar.
2332 // This is useful in particular if you want to create a context menu associated to the title bar of a window.
2333 // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).
2334 static bool test_window = false;
2335 ImGui::Checkbox(label: "Hovered/Active tests after Begin() for title bar testing", v: &test_window);
2336 if (test_window)
2337 {
2338 // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck.
2339 // Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as "don't skip items"?
2340 ImGui::Begin(name: "Title bar Hovered/Active tests", p_open: &test_window);
2341 if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()
2342 {
2343 if (ImGui::MenuItem(label: "Close")) { test_window = false; }
2344 ImGui::EndPopup();
2345 }
2346 ImGui::Text(
2347 fmt: "IsItemHovered() after begin = %d (== is title bar hovered)\n"
2348 "IsItemActive() after begin = %d (== is window being clicked/moved)\n",
2349 ImGui::IsItemHovered(), ImGui::IsItemActive());
2350 ImGui::End();
2351 }
2352
2353 ImGui::TreePop();
2354 }
2355}
2356
2357//-----------------------------------------------------------------------------
2358// [SECTION] DemoWindowWidgetsSelectables()
2359//-----------------------------------------------------------------------------
2360
2361static void DemoWindowWidgetsSelectables()
2362{
2363 IMGUI_DEMO_MARKER("Widgets/Selectables");
2364 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
2365 if (ImGui::TreeNode(label: "Selectables"))
2366 {
2367 // Selectable() has 2 overloads:
2368 // - The one taking "bool selected" as a read-only selection information.
2369 // When Selectable() has been clicked it returns true and you can alter selection state accordingly.
2370 // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
2371 // The earlier is more flexible, as in real application your selection may be stored in many different ways
2372 // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
2373 IMGUI_DEMO_MARKER("Widgets/Selectables/Basic");
2374 if (ImGui::TreeNode(label: "Basic"))
2375 {
2376 static bool selection[5] = { false, true, false, false };
2377 ImGui::Selectable(label: "1. I am selectable", p_selected: &selection[0]);
2378 ImGui::Selectable(label: "2. I am selectable", p_selected: &selection[1]);
2379 ImGui::Selectable(label: "3. I am selectable", p_selected: &selection[2]);
2380 if (ImGui::Selectable(label: "4. I am double clickable", selected: selection[3], flags: ImGuiSelectableFlags_AllowDoubleClick))
2381 if (ImGui::IsMouseDoubleClicked(button: 0))
2382 selection[3] = !selection[3];
2383 ImGui::TreePop();
2384 }
2385
2386 IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line");
2387 if (ImGui::TreeNode(label: "Rendering more items on the same line"))
2388 {
2389 // (1) Using SetNextItemAllowOverlap()
2390 // (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically.
2391 static bool selected[3] = { false, false, false };
2392 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(label: "main.c", p_selected: &selected[0]); ImGui::SameLine(); ImGui::SmallButton(label: "Link 1");
2393 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(label: "Hello.cpp", p_selected: &selected[1]); ImGui::SameLine(); ImGui::SmallButton(label: "Link 2");
2394 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(label: "Hello.h", p_selected: &selected[2]); ImGui::SameLine(); ImGui::SmallButton(label: "Link 3");
2395 ImGui::TreePop();
2396 }
2397
2398 IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables");
2399 if (ImGui::TreeNode(label: "In Tables"))
2400 {
2401 static bool selected[10] = {};
2402
2403 if (ImGui::BeginTable(str_id: "split1", columns: 3, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2404 {
2405 for (int i = 0; i < 10; i++)
2406 {
2407 char label[32];
2408 sprintf(s: label, format: "Item %d", i);
2409 ImGui::TableNextColumn();
2410 ImGui::Selectable(label, p_selected: &selected[i]); // FIXME-TABLE: Selection overlap
2411 }
2412 ImGui::EndTable();
2413 }
2414 ImGui::Spacing();
2415 if (ImGui::BeginTable(str_id: "split2", columns: 3, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2416 {
2417 for (int i = 0; i < 10; i++)
2418 {
2419 char label[32];
2420 sprintf(s: label, format: "Item %d", i);
2421 ImGui::TableNextRow();
2422 ImGui::TableNextColumn();
2423 ImGui::Selectable(label, p_selected: &selected[i], flags: ImGuiSelectableFlags_SpanAllColumns);
2424 ImGui::TableNextColumn();
2425 ImGui::Text(fmt: "Some other contents");
2426 ImGui::TableNextColumn();
2427 ImGui::Text(fmt: "123456");
2428 }
2429 ImGui::EndTable();
2430 }
2431 ImGui::TreePop();
2432 }
2433
2434 IMGUI_DEMO_MARKER("Widgets/Selectables/Grid");
2435 if (ImGui::TreeNode(label: "Grid"))
2436 {
2437 static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
2438
2439 // Add in a bit of silly fun...
2440 const float time = (float)ImGui::GetTime();
2441 const bool winning_state = memchr(s: selected, c: 0, n: sizeof(selected)) == NULL; // If all cells are selected...
2442 if (winning_state)
2443 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)));
2444
2445 for (int y = 0; y < 4; y++)
2446 for (int x = 0; x < 4; x++)
2447 {
2448 if (x > 0)
2449 ImGui::SameLine();
2450 ImGui::PushID(int_id: y * 4 + x);
2451 if (ImGui::Selectable(label: "Sailor", selected: selected[y][x] != 0, flags: 0, size: ImVec2(50, 50)))
2452 {
2453 // Toggle clicked cell + toggle neighbors
2454 selected[y][x] ^= 1;
2455 if (x > 0) { selected[y][x - 1] ^= 1; }
2456 if (x < 3) { selected[y][x + 1] ^= 1; }
2457 if (y > 0) { selected[y - 1][x] ^= 1; }
2458 if (y < 3) { selected[y + 1][x] ^= 1; }
2459 }
2460 ImGui::PopID();
2461 }
2462
2463 if (winning_state)
2464 ImGui::PopStyleVar();
2465 ImGui::TreePop();
2466 }
2467 IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment");
2468 if (ImGui::TreeNode(label: "Alignment"))
2469 {
2470 HelpMarker(
2471 desc: "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item "
2472 "basis using PushStyleVar(). You'll probably want to always keep your default situation to "
2473 "left-align otherwise it becomes difficult to layout multiple items on a same line");
2474 static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };
2475 for (int y = 0; y < 3; y++)
2476 {
2477 for (int x = 0; x < 3; x++)
2478 {
2479 ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);
2480 char name[32];
2481 sprintf(s: name, format: "(%.1f,%.1f)", alignment.x, alignment.y);
2482 if (x > 0) ImGui::SameLine();
2483 ImGui::PushStyleVar(idx: ImGuiStyleVar_SelectableTextAlign, val: alignment);
2484 ImGui::Selectable(label: name, p_selected: &selected[3 * y + x], flags: ImGuiSelectableFlags_None, size: ImVec2(80, 80));
2485 ImGui::PopStyleVar();
2486 }
2487 }
2488 ImGui::TreePop();
2489 }
2490 ImGui::TreePop();
2491 }
2492}
2493
2494//-----------------------------------------------------------------------------
2495// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
2496//-----------------------------------------------------------------------------
2497// Multi-selection demos
2498// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select
2499//-----------------------------------------------------------------------------
2500
2501static const char* ExampleNames[] =
2502{
2503 "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper",
2504 "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava",
2505 "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
2506};
2507
2508// Extra functions to add deletion support to ImGuiSelectionBasicStorage
2509struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage
2510{
2511 // Find which item should be Focused after deletion.
2512 // Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
2513 // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
2514 // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
2515 // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.
2516 // - 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.
2517 // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset.
2518 int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count)
2519 {
2520 if (Size == 0)
2521 return -1;
2522
2523 // If focused item is not selected...
2524 const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item
2525 if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx))
2526 {
2527 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.
2528 return focused_idx; // Request to focus same item after deletion.
2529 }
2530
2531 // If focused item is selected: land on first unselected item after focused item.
2532 for (int idx = focused_idx + 1; idx < items_count; idx++)
2533 if (!Contains(id: GetStorageIdFromIndex(idx)))
2534 return idx;
2535
2536 // If focused item is selected: otherwise return last unselected item before focused item.
2537 for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--)
2538 if (!Contains(id: GetStorageIdFromIndex(idx)))
2539 return idx;
2540
2541 return -1;
2542 }
2543
2544 // Rewrite item list (delete items) + update selection.
2545 // - Call after EndMultiSelect()
2546 // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data.
2547 template<typename ITEM_TYPE>
2548 void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& items, int item_curr_idx_to_select)
2549 {
2550 // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection).
2551 // If NavId was not part of selection, we will stay on same item.
2552 ImVector<ITEM_TYPE> new_items;
2553 new_items.reserve(items.Size - Size);
2554 int item_next_idx_to_select = -1;
2555 for (int idx = 0; idx < items.Size; idx++)
2556 {
2557 if (!Contains(id: GetStorageIdFromIndex(idx)))
2558 new_items.push_back(items[idx]);
2559 if (item_curr_idx_to_select == idx)
2560 item_next_idx_to_select = new_items.Size - 1;
2561 }
2562 items.swap(new_items);
2563
2564 // Update selection
2565 Clear();
2566 if (item_next_idx_to_select != -1 && ms_io->NavIdSelected)
2567 SetItemSelected(id: GetStorageIdFromIndex(idx: item_next_idx_to_select), selected: true);
2568 }
2569};
2570
2571// Example: Implement dual list box storage and interface
2572struct ExampleDualListBox
2573{
2574 ImVector<ImGuiID> Items[2]; // ID is index into ExampleName[]
2575 ImGuiSelectionBasicStorage Selections[2]; // Store ExampleItemId into selection
2576 bool OptKeepSorted = true;
2577
2578 void MoveAll(int src, int dst)
2579 {
2580 IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0));
2581 for (ImGuiID item_id : Items[src])
2582 Items[dst].push_back(v: item_id);
2583 Items[src].clear();
2584 SortItems(n: dst);
2585 Selections[src].Swap(r&: Selections[dst]);
2586 Selections[src].Clear();
2587 }
2588 void MoveSelected(int src, int dst)
2589 {
2590 for (int src_n = 0; src_n < Items[src].Size; src_n++)
2591 {
2592 ImGuiID item_id = Items[src][src_n];
2593 if (!Selections[src].Contains(id: item_id))
2594 continue;
2595 Items[src].erase(it: &Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap)
2596 Items[dst].push_back(v: item_id);
2597 src_n--;
2598 }
2599 if (OptKeepSorted)
2600 SortItems(n: dst);
2601 Selections[src].Swap(r&: Selections[dst]);
2602 Selections[src].Clear();
2603 }
2604 void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side)
2605 {
2606 // In this example we store item id in selection (instead of item index)
2607 Selections[side].UserData = Items[side].Data;
2608 Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; };
2609 Selections[side].ApplyRequests(ms_io);
2610 }
2611 static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)
2612 {
2613 const int* a = (const int*)lhs;
2614 const int* b = (const int*)rhs;
2615 return (*a - *b);
2616 }
2617 void SortItems(int n)
2618 {
2619 qsort(base: Items[n].Data, nmemb: (size_t)Items[n].Size, size: sizeof(Items[n][0]), compar: CompareItemsByValue);
2620 }
2621 void Show()
2622 {
2623 //if (ImGui::Checkbox("Sorted", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); }
2624 if (ImGui::BeginTable(str_id: "split", columns: 3, flags: ImGuiTableFlags_None))
2625 {
2626 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch); // Left side
2627 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed); // Buttons
2628 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch); // Right side
2629 ImGui::TableNextRow();
2630
2631 int request_move_selected = -1;
2632 int request_move_all = -1;
2633 float child_height_0 = 0.0f;
2634 for (int side = 0; side < 2; side++)
2635 {
2636 // FIXME-MULTISELECT: Dual List Box: Add context menus
2637 // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.
2638 ImVector<ImGuiID>& items = Items[side];
2639 ImGuiSelectionBasicStorage& selection = Selections[side];
2640
2641 ImGui::TableSetColumnIndex(column_n: (side == 0) ? 0 : 2);
2642 ImGui::Text(fmt: "%s (%d)", (side == 0) ? "Available" : "Basket", items.Size);
2643
2644 // Submit scrolling range to avoid glitches on moving/deletion
2645 const float items_height = ImGui::GetTextLineHeightWithSpacing();
2646 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2647
2648 bool child_visible;
2649 if (side == 0)
2650 {
2651 // Left child is resizable
2652 ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), size_max: ImVec2(FLT_MAX, FLT_MAX));
2653 child_visible = ImGui::BeginChild(str_id: "0", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY);
2654 child_height_0 = ImGui::GetWindowSize().y;
2655 }
2656 else
2657 {
2658 // Right child use same height as left one
2659 child_visible = ImGui::BeginChild(str_id: "1", size: ImVec2(-FLT_MIN, child_height_0), child_flags: ImGuiChildFlags_FrameStyle);
2660 }
2661 if (child_visible)
2662 {
2663 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None;
2664 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: items.Size);
2665 ApplySelectionRequests(ms_io, side);
2666
2667 for (int item_n = 0; item_n < items.Size; item_n++)
2668 {
2669 ImGuiID item_id = items[item_n];
2670 bool item_is_selected = selection.Contains(id: item_id);
2671 ImGui::SetNextItemSelectionUserData(item_n);
2672 ImGui::Selectable(label: ExampleNames[item_id], selected: item_is_selected, flags: ImGuiSelectableFlags_AllowDoubleClick);
2673 if (ImGui::IsItemFocused())
2674 {
2675 // FIXME-MULTISELECT: Dual List Box: Transfer focus
2676 if (ImGui::IsKeyPressed(key: ImGuiKey_Enter) || ImGui::IsKeyPressed(key: ImGuiKey_KeypadEnter))
2677 request_move_selected = side;
2678 if (ImGui::IsMouseDoubleClicked(button: 0)) // FIXME-MULTISELECT: Double-click on multi-selection?
2679 request_move_selected = side;
2680 }
2681 }
2682
2683 ms_io = ImGui::EndMultiSelect();
2684 ApplySelectionRequests(ms_io, side);
2685 }
2686 ImGui::EndChild();
2687 }
2688
2689 // Buttons columns
2690 ImGui::TableSetColumnIndex(column_n: 1);
2691 ImGui::NewLine();
2692 //ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f };
2693 ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() };
2694
2695 // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized)
2696 if (ImGui::Button(label: ">>", size: button_sz))
2697 request_move_all = 0;
2698 if (ImGui::Button(label: ">", size: button_sz))
2699 request_move_selected = 0;
2700 if (ImGui::Button(label: "<", size: button_sz))
2701 request_move_selected = 1;
2702 if (ImGui::Button(label: "<<", size: button_sz))
2703 request_move_all = 1;
2704
2705 // Process requests
2706 if (request_move_all != -1)
2707 MoveAll(src: request_move_all, dst: request_move_all ^ 1);
2708 if (request_move_selected != -1)
2709 MoveSelected(src: request_move_selected, dst: request_move_selected ^ 1);
2710
2711 // FIXME-MULTISELECT: Support action from outside
2712 /*
2713 if (OptKeepSorted == false)
2714 {
2715 ImGui::NewLine();
2716 if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {}
2717 if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {}
2718 }
2719 */
2720
2721 ImGui::EndTable();
2722 }
2723 }
2724};
2725
2726static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_data)
2727{
2728 IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select");
2729 if (ImGui::TreeNode(label: "Selection State & Multi-Select"))
2730 {
2731 HelpMarker(desc: "Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
2732
2733 // Without any fancy API: manage single-selection yourself.
2734 IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
2735 if (ImGui::TreeNode(label: "Single-Select"))
2736 {
2737 static int selected = -1;
2738 for (int n = 0; n < 5; n++)
2739 {
2740 char buf[32];
2741 sprintf(s: buf, format: "Object %d", n);
2742 if (ImGui::Selectable(label: buf, selected: selected == n))
2743 selected = n;
2744 }
2745 ImGui::TreePop();
2746 }
2747
2748 // Demonstrate implementation a most-basic form of multi-selection manually
2749 // This doesn't support the SHIFT modifier which requires BeginMultiSelect()!
2750 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)");
2751 if (ImGui::TreeNode(label: "Multi-Select (manual/simplified, without BeginMultiSelect)"))
2752 {
2753 HelpMarker(desc: "Hold CTRL and click to select multiple items.");
2754 static bool selection[5] = { false, false, false, false, false };
2755 for (int n = 0; n < 5; n++)
2756 {
2757 char buf[32];
2758 sprintf(s: buf, format: "Object %d", n);
2759 if (ImGui::Selectable(label: buf, selected: selection[n]))
2760 {
2761 if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held
2762 memset(s: selection, c: 0, n: sizeof(selection));
2763 selection[n] ^= 1; // Toggle current item
2764 }
2765 }
2766 ImGui::TreePop();
2767 }
2768
2769 // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.
2770 // SHIFT+Click w/ CTRL and other standard features are supported.
2771 // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.
2772 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select");
2773 if (ImGui::TreeNode(label: "Multi-Select"))
2774 {
2775 ImGui::Text(fmt: "Supported features:");
2776 ImGui::BulletText(fmt: "Keyboard navigation (arrows, page up/down, home/end, space).");
2777 ImGui::BulletText(fmt: "Ctrl modifier to preserve and toggle selection.");
2778 ImGui::BulletText(fmt: "Shift modifier for range selection.");
2779 ImGui::BulletText(fmt: "CTRL+A to select all.");
2780 ImGui::BulletText(fmt: "Escape to clear selection.");
2781 ImGui::BulletText(fmt: "Click and drag to box-select.");
2782 ImGui::Text(fmt: "Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.");
2783
2784 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2785 const int ITEMS_COUNT = 50;
2786 static ImGuiSelectionBasicStorage selection;
2787 ImGui::Text(fmt: "Selection: %d/%d", selection.Size, ITEMS_COUNT);
2788
2789 // The BeginChild() has no purpose for selection logic, other that offering a scrolling region.
2790 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2791 {
2792 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2793 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: ITEMS_COUNT);
2794 selection.ApplyRequests(ms_io);
2795
2796 for (int n = 0; n < ITEMS_COUNT; n++)
2797 {
2798 char label[64];
2799 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2800 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
2801 ImGui::SetNextItemSelectionUserData(n);
2802 ImGui::Selectable(label, selected: item_is_selected);
2803 }
2804
2805 ms_io = ImGui::EndMultiSelect();
2806 selection.ApplyRequests(ms_io);
2807 }
2808 ImGui::EndChild();
2809 ImGui::TreePop();
2810 }
2811
2812 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2813 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)");
2814 if (ImGui::TreeNode(label: "Multi-Select (with clipper)"))
2815 {
2816 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2817 static ImGuiSelectionBasicStorage selection;
2818
2819 ImGui::Text(fmt: "Added features:");
2820 ImGui::BulletText(fmt: "Using ImGuiListClipper.");
2821
2822 const int ITEMS_COUNT = 10000;
2823 ImGui::Text(fmt: "Selection: %d/%d", selection.Size, ITEMS_COUNT);
2824 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2825 {
2826 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2827 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: ITEMS_COUNT);
2828 selection.ApplyRequests(ms_io);
2829
2830 ImGuiListClipper clipper;
2831 clipper.Begin(items_count: ITEMS_COUNT);
2832 if (ms_io->RangeSrcItem != -1)
2833 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2834 while (clipper.Step())
2835 {
2836 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2837 {
2838 char label[64];
2839 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2840 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
2841 ImGui::SetNextItemSelectionUserData(n);
2842 ImGui::Selectable(label, selected: item_is_selected);
2843 }
2844 }
2845
2846 ms_io = ImGui::EndMultiSelect();
2847 selection.ApplyRequests(ms_io);
2848 }
2849 ImGui::EndChild();
2850 ImGui::TreePop();
2851 }
2852
2853 // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API.
2854 // In order to support Deletion without any glitches you need to:
2855 // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling.
2856 // - (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.
2857 // - (3) BeginXXXX process
2858 // - (4) Focus process
2859 // - (5) EndXXXX process
2860 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)");
2861 if (ImGui::TreeNode(label: "Multi-Select (with deletion)"))
2862 {
2863 // Storing items data separately from selection data.
2864 // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items)
2865 // Use a custom selection.Adapter: store item identifier in Selection (instead of index)
2866 static ImVector<ImGuiID> items;
2867 static ExampleSelectionWithDeletion selection;
2868 selection.UserData = (void*)&items;
2869 selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector<ImGuiID>* p_items = (ImVector<ImGuiID>*)self->UserData; return (*p_items)[idx]; }; // Index -> ID
2870
2871 ImGui::Text(fmt: "Added features:");
2872 ImGui::BulletText(fmt: "Dynamic list with Delete key support.");
2873 ImGui::Text(fmt: "Selection size: %d/%d", selection.Size, items.Size);
2874
2875 // Initialize default list with 50 items + button to add/remove items.
2876 static ImGuiID items_next_id = 0;
2877 if (items_next_id == 0)
2878 for (ImGuiID n = 0; n < 50; n++)
2879 items.push_back(v: items_next_id++);
2880 if (ImGui::SmallButton(label: "Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(v: items_next_id++); } }
2881 ImGui::SameLine();
2882 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(); } }
2883
2884 // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
2885 const float items_height = ImGui::GetTextLineHeightWithSpacing();
2886 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2887
2888 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2889 {
2890 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2891 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: items.Size);
2892 selection.ApplyRequests(ms_io);
2893
2894 const bool want_delete = ImGui::Shortcut(key_chord: ImGuiKey_Delete, flags: ImGuiInputFlags_Repeat) && (selection.Size > 0);
2895 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items_count: items.Size) : -1;
2896
2897 for (int n = 0; n < items.Size; n++)
2898 {
2899 const ImGuiID item_id = items[n];
2900 char label[64];
2901 sprintf(s: label, format: "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]);
2902
2903 bool item_is_selected = selection.Contains(id: item_id);
2904 ImGui::SetNextItemSelectionUserData(n);
2905 ImGui::Selectable(label, selected: item_is_selected);
2906 if (item_curr_idx_to_focus == n)
2907 ImGui::SetKeyboardFocusHere(-1);
2908 }
2909
2910 // Apply multi-select requests
2911 ms_io = ImGui::EndMultiSelect();
2912 selection.ApplyRequests(ms_io);
2913 if (want_delete)
2914 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_select: item_curr_idx_to_focus);
2915 }
2916 ImGui::EndChild();
2917 ImGui::TreePop();
2918 }
2919
2920 // Implement a Dual List Box (#6648)
2921 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)");
2922 if (ImGui::TreeNode(label: "Multi-Select (dual list box)"))
2923 {
2924 // Init default state
2925 static ExampleDualListBox dlb;
2926 if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0)
2927 for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++)
2928 dlb.Items[0].push_back(v: (ImGuiID)item_id);
2929
2930 // Show
2931 dlb.Show();
2932
2933 ImGui::TreePop();
2934 }
2935
2936 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2937 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)");
2938 if (ImGui::TreeNode(label: "Multi-Select (in a table)"))
2939 {
2940 static ImGuiSelectionBasicStorage selection;
2941
2942 const int ITEMS_COUNT = 10000;
2943 ImGui::Text(fmt: "Selection: %d/%d", selection.Size, ITEMS_COUNT);
2944 if (ImGui::BeginTable(str_id: "##Basket", columns: 2, flags: ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter))
2945 {
2946 ImGui::TableSetupColumn(label: "Object");
2947 ImGui::TableSetupColumn(label: "Action");
2948 ImGui::TableSetupScrollFreeze(cols: 0, rows: 1);
2949 ImGui::TableHeadersRow();
2950
2951 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2952 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: ITEMS_COUNT);
2953 selection.ApplyRequests(ms_io);
2954
2955 ImGuiListClipper clipper;
2956 clipper.Begin(items_count: ITEMS_COUNT);
2957 if (ms_io->RangeSrcItem != -1)
2958 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2959 while (clipper.Step())
2960 {
2961 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2962 {
2963 ImGui::TableNextRow();
2964 ImGui::TableNextColumn();
2965 char label[64];
2966 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2967 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
2968 ImGui::SetNextItemSelectionUserData(n);
2969 ImGui::Selectable(label, selected: item_is_selected, flags: ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
2970 ImGui::TableNextColumn();
2971 ImGui::SmallButton(label: "hello");
2972 }
2973 }
2974
2975 ms_io = ImGui::EndMultiSelect();
2976 selection.ApplyRequests(ms_io);
2977 ImGui::EndTable();
2978 }
2979 ImGui::TreePop();
2980 }
2981
2982 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)");
2983 if (ImGui::TreeNode(label: "Multi-Select (checkboxes)"))
2984 {
2985 ImGui::Text(fmt: "In a list of checkboxes (not selectable):");
2986 ImGui::BulletText(fmt: "Using _NoAutoSelect + _NoAutoClear flags.");
2987 ImGui::BulletText(fmt: "Shift+Click to check multiple boxes.");
2988 ImGui::BulletText(fmt: "Shift+Keyboard to copy current value to other boxes.");
2989
2990 // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper.
2991 static bool items[20] = {};
2992 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape;
2993 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoSelect);
2994 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoClear", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoClear);
2995 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect2d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.
2996
2997 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
2998 {
2999 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: -1, IM_ARRAYSIZE(items));
3000 ImGuiSelectionExternalStorage storage_wrapper;
3001 storage_wrapper.UserData = (void*)items;
3002 storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; };
3003 storage_wrapper.ApplyRequests(ms_io);
3004 for (int n = 0; n < 20; n++)
3005 {
3006 char label[32];
3007 sprintf(s: label, format: "Item %d", n);
3008 ImGui::SetNextItemSelectionUserData(n);
3009 ImGui::Checkbox(label, v: &items[n]);
3010 }
3011 ms_io = ImGui::EndMultiSelect();
3012 storage_wrapper.ApplyRequests(ms_io);
3013 }
3014 ImGui::EndChild();
3015
3016 ImGui::TreePop();
3017 }
3018
3019 // Demonstrate individual selection scopes in same window
3020 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)");
3021 if (ImGui::TreeNode(label: "Multi-Select (multiple scopes)"))
3022 {
3023 // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection
3024 const int SCOPES_COUNT = 3;
3025 const int ITEMS_COUNT = 8; // Per scope
3026 static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT];
3027
3028 // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.
3029 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid;
3030 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeWindow", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
3031 flags &= ~ImGuiMultiSelectFlags_ScopeRect;
3032 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeRect", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
3033 flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
3034 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ClearOnClickVoid", flags: &flags, flags_value: ImGuiMultiSelectFlags_ClearOnClickVoid);
3035 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect1d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect1d);
3036
3037 for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++)
3038 {
3039 ImGui::PushID(int_id: selection_scope_n);
3040 ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];
3041 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection->Size, items_count: ITEMS_COUNT);
3042 selection->ApplyRequests(ms_io);
3043
3044 ImGui::SeparatorText(label: "Selection scope");
3045 ImGui::Text(fmt: "Selection size: %d/%d", selection->Size, ITEMS_COUNT);
3046
3047 for (int n = 0; n < ITEMS_COUNT; n++)
3048 {
3049 char label[64];
3050 sprintf(s: label, format: "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
3051 bool item_is_selected = selection->Contains(id: (ImGuiID)n);
3052 ImGui::SetNextItemSelectionUserData(n);
3053 ImGui::Selectable(label, selected: item_is_selected);
3054 }
3055
3056 // Apply multi-select requests
3057 ms_io = ImGui::EndMultiSelect();
3058 selection->ApplyRequests(ms_io);
3059 ImGui::PopID();
3060 }
3061 ImGui::TreePop();
3062 }
3063
3064 // See ShowExampleAppAssetsBrowser()
3065 if (ImGui::TreeNode(label: "Multi-Select (tiled assets browser)"))
3066 {
3067 ImGui::Checkbox(label: "Assets Browser", v: &demo_data->ShowAppAssetsBrowser);
3068 ImGui::Text(fmt: "(also access from 'Examples->Assets Browser' in menu)");
3069 ImGui::TreePop();
3070 }
3071
3072 // Demonstrate supporting multiple-selection in a tree.
3073 // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly!
3074 // This showcase how SetNextItemSelectionUserData() never assume indices!
3075 // - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request.
3076 // We want this interpolation to match what the user sees: in visible order, skipping closed nodes.
3077 // This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper.
3078 // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you
3079 // are more likely to build an array mapping sequential indices to visible tree nodes, since your
3080 // filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier.
3081 // - Consider this a prototype: we are working toward simplifying some of it.
3082 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)");
3083 if (ImGui::TreeNode(label: "Multi-Select (trees)"))
3084 {
3085 HelpMarker(
3086 desc: "This is rather advanced and experimental. If you are getting started with multi-select, "
3087 "please don't start by looking at how to use it for a tree!\n\n"
3088 "Future versions will try to simplify and formalize some of this.");
3089
3090 struct ExampleTreeFuncs
3091 {
3092 static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection)
3093 {
3094 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3095 tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Enable pressing left to jump to parent
3096 if (node->Childs.Size == 0)
3097 tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf;
3098 if (selection->Contains(id: (ImGuiID)node->UID))
3099 tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3100
3101 // Using SetNextItemStorageID() to specify storage id, so we can easily peek into
3102 // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions.
3103 ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node);
3104 ImGui::SetNextItemStorageID((ImGuiID)node->UID);
3105 if (ImGui::TreeNodeEx(label: node->Name, flags: tree_node_flags))
3106 {
3107 for (ExampleTreeNode* child : node->Childs)
3108 DrawNode(node: child, selection);
3109 ImGui::TreePop();
3110 }
3111 else if (ImGui::IsItemToggledOpen())
3112 {
3113 TreeCloseAndUnselectChildNodes(node, selection);
3114 }
3115 }
3116
3117 static bool TreeNodeGetOpen(ExampleTreeNode* node)
3118 {
3119 return ImGui::GetStateStorage()->GetBool(key: (ImGuiID)node->UID);
3120 }
3121
3122 static void TreeNodeSetOpen(ExampleTreeNode* node, bool open)
3123 {
3124 ImGui::GetStateStorage()->SetBool(key: (ImGuiID)node->UID, val: open);
3125 }
3126
3127 // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected.
3128 // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node
3129 // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc.
3130 static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0)
3131 {
3132 // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!)
3133 int unselected_count = selection->Contains(id: (ImGuiID)node->UID) ? 1 : 0;
3134 if (depth == 0 || TreeNodeGetOpen(node))
3135 {
3136 for (ExampleTreeNode* child : node->Childs)
3137 unselected_count += TreeCloseAndUnselectChildNodes(node: child, selection, depth: depth + 1);
3138 TreeNodeSetOpen(node, open: false);
3139 }
3140
3141 // Select root node if any of its child was selected, otherwise unselect
3142 selection->SetItemSelected(id: (ImGuiID)node->UID, selected: (depth == 0 && unselected_count > 0));
3143 return unselected_count;
3144 }
3145
3146 // Apply multi-selection requests
3147 static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection)
3148 {
3149 for (ImGuiSelectionRequest& req : ms_io->Requests)
3150 {
3151 if (req.Type == ImGuiSelectionRequestType_SetAll)
3152 {
3153 if (req.Selected)
3154 TreeSetAllInOpenNodes(node: tree, selection, selected: req.Selected);
3155 else
3156 selection->Clear();
3157 }
3158 else if (req.Type == ImGuiSelectionRequestType_SetRange)
3159 {
3160 ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem;
3161 ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem;
3162 for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(curr_node: node, last_node))
3163 selection->SetItemSelected(id: (ImGuiID)node->UID, selected: req.Selected);
3164 }
3165 }
3166 }
3167
3168 static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected)
3169 {
3170 if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme
3171 selection->SetItemSelected(id: (ImGuiID)node->UID, selected);
3172 if (node->Parent == NULL || TreeNodeGetOpen(node))
3173 for (ExampleTreeNode* child : node->Childs)
3174 TreeSetAllInOpenNodes(node: child, selection, selected);
3175 }
3176
3177 // Interpolate in *user-visible order* AND only *over opened nodes*.
3178 // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler.
3179 // Here the tricks are that:
3180 // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion.
3181 // this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)'
3182 // which would only be called when crossing from child to a parent, aka not too much.
3183 // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack,
3184 // making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location.
3185 static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node)
3186 {
3187 // Reached last node
3188 if (curr_node == last_node)
3189 return NULL;
3190
3191 // Recurse into childs. Query storage to tell if the node is open.
3192 if (curr_node->Childs.Size > 0 && TreeNodeGetOpen(node: curr_node))
3193 return curr_node->Childs[0];
3194
3195 // Next sibling, then into our own parent
3196 while (curr_node->Parent != NULL)
3197 {
3198 if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size)
3199 return curr_node->Parent->Childs[curr_node->IndexInParent + 1];
3200 curr_node = curr_node->Parent;
3201 }
3202 return NULL;
3203 }
3204
3205 }; // ExampleTreeFuncs
3206
3207 static ImGuiSelectionBasicStorage selection;
3208 if (demo_data->DemoTree == NULL)
3209 demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once
3210 ImGui::Text(fmt: "Selection size: %d", selection.Size);
3211
3212 if (ImGui::BeginChild(str_id: "##Tree", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3213 {
3214 ExampleTreeNode* tree = demo_data->DemoTree;
3215 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d;
3216 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags: ms_flags, selection_size: selection.Size, items_count: -1);
3217 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, selection: &selection);
3218 for (ExampleTreeNode* node : tree->Childs)
3219 ExampleTreeFuncs::DrawNode(node, selection: &selection);
3220 ms_io = ImGui::EndMultiSelect();
3221 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, selection: &selection);
3222 }
3223 ImGui::EndChild();
3224
3225 ImGui::TreePop();
3226 }
3227
3228 // Advanced demonstration of BeginMultiSelect()
3229 // - Showcase clipping.
3230 // - Showcase deletion.
3231 // - Showcase basic drag and drop.
3232 // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
3233 // - Showcase using inside a table.
3234 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)");
3235 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
3236 if (ImGui::TreeNode(label: "Multi-Select (advanced)"))
3237 {
3238 // Options
3239 enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
3240 static bool use_clipper = true;
3241 static bool use_deletion = true;
3242 static bool use_drag_drop = true;
3243 static bool show_in_table = false;
3244 static bool show_color_button = true;
3245 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
3246 static WidgetType widget_type = WidgetType_Selectable;
3247
3248 if (ImGui::TreeNode(label: "Options"))
3249 {
3250 if (ImGui::RadioButton(label: "Selectables", active: widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; }
3251 ImGui::SameLine();
3252 if (ImGui::RadioButton(label: "Tree nodes", active: widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; }
3253 ImGui::SameLine();
3254 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.");
3255 ImGui::Checkbox(label: "Enable clipper", v: &use_clipper);
3256 ImGui::Checkbox(label: "Enable deletion", v: &use_deletion);
3257 ImGui::Checkbox(label: "Enable drag & drop", v: &use_drag_drop);
3258 ImGui::Checkbox(label: "Show in a table", v: &show_in_table);
3259 ImGui::Checkbox(label: "Show color button", v: &show_color_button);
3260 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_SingleSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_SingleSelect);
3261 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoSelectAll", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoSelectAll);
3262 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoRangeSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoRangeSelect);
3263 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoSelect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoSelect);
3264 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoClear", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoClear);
3265 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_NoAutoClearOnReselect", flags: &flags, flags_value: ImGuiMultiSelectFlags_NoAutoClearOnReselect);
3266 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect1d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect1d);
3267 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelect2d", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelect2d);
3268 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_BoxSelectNoScroll", flags: &flags, flags_value: ImGuiMultiSelectFlags_BoxSelectNoScroll);
3269 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ClearOnEscape", flags: &flags, flags_value: ImGuiMultiSelectFlags_ClearOnEscape);
3270 ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ClearOnClickVoid", flags: &flags, flags_value: ImGuiMultiSelectFlags_ClearOnClickVoid);
3271 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeWindow", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
3272 flags &= ~ImGuiMultiSelectFlags_ScopeRect;
3273 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_ScopeRect", flags: &flags, flags_value: ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
3274 flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
3275 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_SelectOnClick", flags: &flags, flags_value: ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick))
3276 flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease;
3277 if (ImGui::CheckboxFlags(label: "ImGuiMultiSelectFlags_SelectOnClickRelease", flags: &flags, flags_value: ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease))
3278 flags &= ~ImGuiMultiSelectFlags_SelectOnClick;
3279 ImGui::SameLine(); HelpMarker(desc: "Allow dragging an unselected item without altering selection.");
3280 ImGui::TreePop();
3281 }
3282
3283 // Initialize default list with 1000 items.
3284 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
3285 static ImVector<int> items;
3286 static int items_next_id = 0;
3287 if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(v: items_next_id++); } }
3288 static ExampleSelectionWithDeletion selection;
3289 static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu
3290
3291 ImGui::Text(fmt: "Selection size: %d/%d", selection.Size, items.Size);
3292
3293 const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing();
3294 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
3295 if (ImGui::BeginChild(str_id: "##Basket", size: ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), child_flags: ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3296 {
3297 ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());
3298 if (widget_type == WidgetType_TreeNode)
3299 ImGui::PushStyleVarY(idx: ImGuiStyleVar_ItemSpacing, val_y: 0.0f);
3300
3301 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection_size: selection.Size, items_count: items.Size);
3302 selection.ApplyRequests(ms_io);
3303
3304 const bool want_delete = (ImGui::Shortcut(key_chord: ImGuiKey_Delete, flags: ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu;
3305 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items_count: items.Size) : -1;
3306 request_deletion_from_menu = false;
3307
3308 if (show_in_table)
3309 {
3310 if (widget_type == WidgetType_TreeNode)
3311 ImGui::PushStyleVar(idx: ImGuiStyleVar_CellPadding, val: ImVec2(0.0f, 0.0f));
3312 ImGui::BeginTable(str_id: "##Split", columns: 2, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
3313 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 0.70f);
3314 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 0.30f);
3315 //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f);
3316 }
3317
3318 ImGuiListClipper clipper;
3319 if (use_clipper)
3320 {
3321 clipper.Begin(items_count: items.Size);
3322 if (item_curr_idx_to_focus != -1)
3323 clipper.IncludeItemByIndex(item_index: item_curr_idx_to_focus); // Ensure focused item is not clipped.
3324 if (ms_io->RangeSrcItem != -1)
3325 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
3326 }
3327
3328 while (!use_clipper || clipper.Step())
3329 {
3330 const int item_begin = use_clipper ? clipper.DisplayStart : 0;
3331 const int item_end = use_clipper ? clipper.DisplayEnd : items.Size;
3332 for (int n = item_begin; n < item_end; n++)
3333 {
3334 if (show_in_table)
3335 ImGui::TableNextColumn();
3336
3337 const int item_id = items[n];
3338 const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)];
3339 char label[64];
3340 sprintf(s: label, format: "Object %05d: %s", item_id, item_category);
3341
3342 // IMPORTANT: for deletion refocus to work we need object ID to be stable,
3343 // aka not depend on their index in the list. Here we use our persistent item_id
3344 // instead of index to build a unique ID that will persist.
3345 // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).
3346 ImGui::PushID(int_id: item_id);
3347
3348 // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part
3349 // of the selection scope doesn't erroneously alter our selection.
3350 if (show_color_button)
3351 {
3352 ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK;
3353 ImGui::ColorButton(desc_id: "##", col: ImColor(dummy_col), flags: ImGuiColorEditFlags_NoTooltip, size: color_button_sz);
3354 ImGui::SameLine();
3355 }
3356
3357 // Submit item
3358 bool item_is_selected = selection.Contains(id: (ImGuiID)n);
3359 bool item_is_open = false;
3360 ImGui::SetNextItemSelectionUserData(n);
3361 if (widget_type == WidgetType_Selectable)
3362 {
3363 ImGui::Selectable(label, selected: item_is_selected, flags: ImGuiSelectableFlags_None);
3364 }
3365 else if (widget_type == WidgetType_TreeNode)
3366 {
3367 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3368 if (item_is_selected)
3369 tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3370 item_is_open = ImGui::TreeNodeEx(label, flags: tree_node_flags);
3371 }
3372
3373 // Focus (for after deletion)
3374 if (item_curr_idx_to_focus == n)
3375 ImGui::SetKeyboardFocusHere(-1);
3376
3377 // Drag and Drop
3378 if (use_drag_drop && ImGui::BeginDragDropSource())
3379 {
3380 // Create payload with full selection OR single unselected item.
3381 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
3382 if (ImGui::GetDragDropPayload() == NULL)
3383 {
3384 ImVector<int> payload_items;
3385 void* it = NULL;
3386 ImGuiID id = 0;
3387 if (!item_is_selected)
3388 payload_items.push_back(v: item_id);
3389 else
3390 while (selection.GetNextSelectedItem(opaque_it: &it, out_id: &id))
3391 payload_items.push_back(v: (int)id);
3392 ImGui::SetDragDropPayload(type: "MULTISELECT_DEMO_ITEMS", data: payload_items.Data, sz: (size_t)payload_items.size_in_bytes());
3393 }
3394
3395 // Display payload content in tooltip
3396 const ImGuiPayload* payload = ImGui::GetDragDropPayload();
3397 const int* payload_items = (int*)payload->Data;
3398 const int payload_count = (int)payload->DataSize / (int)sizeof(int);
3399 if (payload_count == 1)
3400 ImGui::Text(fmt: "Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]);
3401 else
3402 ImGui::Text(fmt: "Dragging %d objects", payload_count);
3403
3404 ImGui::EndDragDropSource();
3405 }
3406
3407 if (widget_type == WidgetType_TreeNode && item_is_open)
3408 ImGui::TreePop();
3409
3410 // Right-click: context menu
3411 if (ImGui::BeginPopupContextItem())
3412 {
3413 ImGui::BeginDisabled(disabled: !use_deletion || selection.Size == 0);
3414 sprintf(s: label, format: "Delete %d item(s)###DeleteSelected", selection.Size);
3415 if (ImGui::Selectable(label))
3416 request_deletion_from_menu = true;
3417 ImGui::EndDisabled();
3418 ImGui::Selectable(label: "Close");
3419 ImGui::EndPopup();
3420 }
3421
3422 // Demo content within a table
3423 if (show_in_table)
3424 {
3425 ImGui::TableNextColumn();
3426 ImGui::SetNextItemWidth(-FLT_MIN);
3427 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(0, 0));
3428 ImGui::InputText(label: "###NoLabel", buf: (char*)(void*)item_category, buf_size: strlen(s: item_category), flags: ImGuiInputTextFlags_ReadOnly);
3429 ImGui::PopStyleVar();
3430 }
3431
3432 ImGui::PopID();
3433 }
3434 if (!use_clipper)
3435 break;
3436 }
3437
3438 if (show_in_table)
3439 {
3440 ImGui::EndTable();
3441 if (widget_type == WidgetType_TreeNode)
3442 ImGui::PopStyleVar();
3443 }
3444
3445 // Apply multi-select requests
3446 ms_io = ImGui::EndMultiSelect();
3447 selection.ApplyRequests(ms_io);
3448 if (want_delete)
3449 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_select: item_curr_idx_to_focus);
3450
3451 if (widget_type == WidgetType_TreeNode)
3452 ImGui::PopStyleVar();
3453 }
3454 ImGui::EndChild();
3455 ImGui::TreePop();
3456 }
3457 ImGui::TreePop();
3458 }
3459}
3460
3461//-----------------------------------------------------------------------------
3462// [SECTION] DemoWindowWidgetsTabs()
3463//-----------------------------------------------------------------------------
3464
3465static void EditTabBarFittingPolicyFlags(ImGuiTabBarFlags* p_flags)
3466{
3467 if ((*p_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
3468 *p_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
3469 if (ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_FittingPolicyMixed", flags: p_flags, flags_value: ImGuiTabBarFlags_FittingPolicyMixed))
3470 *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyMixed);
3471 if (ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_FittingPolicyShrink", flags: p_flags, flags_value: ImGuiTabBarFlags_FittingPolicyShrink))
3472 *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyShrink);
3473 if (ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_FittingPolicyScroll", flags: p_flags, flags_value: ImGuiTabBarFlags_FittingPolicyScroll))
3474 *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
3475}
3476
3477static void DemoWindowWidgetsTabs()
3478{
3479 IMGUI_DEMO_MARKER("Widgets/Tabs");
3480 if (ImGui::TreeNode(label: "Tabs"))
3481 {
3482 IMGUI_DEMO_MARKER("Widgets/Tabs/Basic");
3483 if (ImGui::TreeNode(label: "Basic"))
3484 {
3485 ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
3486 if (ImGui::BeginTabBar(str_id: "MyTabBar", flags: tab_bar_flags))
3487 {
3488 if (ImGui::BeginTabItem(label: "Avocado"))
3489 {
3490 ImGui::Text(fmt: "This is the Avocado tab!\nblah blah blah blah blah");
3491 ImGui::EndTabItem();
3492 }
3493 if (ImGui::BeginTabItem(label: "Broccoli"))
3494 {
3495 ImGui::Text(fmt: "This is the Broccoli tab!\nblah blah blah blah blah");
3496 ImGui::EndTabItem();
3497 }
3498 if (ImGui::BeginTabItem(label: "Cucumber"))
3499 {
3500 ImGui::Text(fmt: "This is the Cucumber tab!\nblah blah blah blah blah");
3501 ImGui::EndTabItem();
3502 }
3503 ImGui::EndTabBar();
3504 }
3505 ImGui::Separator();
3506 ImGui::TreePop();
3507 }
3508
3509 IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button");
3510 if (ImGui::TreeNode(label: "Advanced & Close Button"))
3511 {
3512 // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).
3513 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;
3514 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_Reorderable", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_Reorderable);
3515 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_AutoSelectNewTabs", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_AutoSelectNewTabs);
3516 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_TabListPopupButton", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_TabListPopupButton);
3517 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);
3518 ImGui::CheckboxFlags(label: "ImGuiTabBarFlags_DrawSelectedOverline", flags: &tab_bar_flags, flags_value: ImGuiTabBarFlags_DrawSelectedOverline);
3519 EditTabBarFittingPolicyFlags(p_flags: &tab_bar_flags);
3520
3521 // Tab Bar
3522 ImGui::AlignTextToFramePadding();
3523 ImGui::Text(fmt: "Opened:");
3524 const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" };
3525 static bool opened[4] = { true, true, true, true }; // Persistent user state
3526 for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3527 {
3528 ImGui::SameLine();
3529 ImGui::Checkbox(label: names[n], v: &opened[n]);
3530 }
3531
3532 // Passing a bool* to BeginTabItem() is similar to passing one to Begin():
3533 // the underlying bool will be set to false when the tab is closed.
3534 if (ImGui::BeginTabBar(str_id: "MyTabBar", flags: tab_bar_flags))
3535 {
3536 for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3537 if (opened[n] && ImGui::BeginTabItem(label: names[n], p_open: &opened[n], flags: ImGuiTabItemFlags_None))
3538 {
3539 ImGui::Text(fmt: "This is the %s tab!", names[n]);
3540 if (n & 1)
3541 ImGui::Text(fmt: "I am an odd tab.");
3542 ImGui::EndTabItem();
3543 }
3544 ImGui::EndTabBar();
3545 }
3546 ImGui::Separator();
3547 ImGui::TreePop();
3548 }
3549
3550 IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags");
3551 if (ImGui::TreeNode(label: "TabItemButton & Leading/Trailing flags"))
3552 {
3553 static ImVector<int> active_tabs;
3554 static int next_tab_id = 0;
3555 if (next_tab_id == 0) // Initialize with some default tabs
3556 for (int i = 0; i < 3; i++)
3557 active_tabs.push_back(v: next_tab_id++);
3558
3559 // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.
3560 // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...
3561 // but they tend to make more sense together)
3562 static bool show_leading_button = true;
3563 static bool show_trailing_button = true;
3564 ImGui::Checkbox(label: "Show Leading TabItemButton()", v: &show_leading_button);
3565 ImGui::Checkbox(label: "Show Trailing TabItemButton()", v: &show_trailing_button);
3566
3567 // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
3568 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink;
3569 EditTabBarFittingPolicyFlags(p_flags: &tab_bar_flags);
3570
3571 if (ImGui::BeginTabBar(str_id: "MyTabBar", flags: tab_bar_flags))
3572 {
3573 // Demo a Leading TabItemButton(): click the "?" button to open a menu
3574 if (show_leading_button)
3575 if (ImGui::TabItemButton(label: "?", flags: ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))
3576 ImGui::OpenPopup(str_id: "MyHelpMenu");
3577 if (ImGui::BeginPopup(str_id: "MyHelpMenu"))
3578 {
3579 ImGui::Selectable(label: "Hello!");
3580 ImGui::EndPopup();
3581 }
3582
3583 // Demo Trailing Tabs: click the "+" button to add a new tab.
3584 // (In your app you may want to use a font icon instead of the "+")
3585 // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end.
3586 if (show_trailing_button)
3587 if (ImGui::TabItemButton(label: "+", flags: ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))
3588 active_tabs.push_back(v: next_tab_id++); // Add new tab
3589
3590 // Submit our regular tabs
3591 for (int n = 0; n < active_tabs.Size; )
3592 {
3593 bool open = true;
3594 char name[16];
3595 snprintf(s: name, IM_ARRAYSIZE(name), format: "%04d", active_tabs[n]);
3596 if (ImGui::BeginTabItem(label: name, p_open: &open, flags: ImGuiTabItemFlags_None))
3597 {
3598 ImGui::Text(fmt: "This is the %s tab!", name);
3599 ImGui::EndTabItem();
3600 }
3601
3602 if (!open)
3603 active_tabs.erase(it: active_tabs.Data + n);
3604 else
3605 n++;
3606 }
3607
3608 ImGui::EndTabBar();
3609 }
3610 ImGui::Separator();
3611 ImGui::TreePop();
3612 }
3613 ImGui::TreePop();
3614 }
3615}
3616
3617//-----------------------------------------------------------------------------
3618// [SECTION] DemoWindowWidgetsText()
3619//-----------------------------------------------------------------------------
3620
3621static void DemoWindowWidgetsText()
3622{
3623 IMGUI_DEMO_MARKER("Widgets/Text");
3624 if (ImGui::TreeNode(label: "Text"))
3625 {
3626 IMGUI_DEMO_MARKER("Widgets/Text/Colored Text");
3627 if (ImGui::TreeNode(label: "Colorful Text"))
3628 {
3629 // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
3630 ImGui::TextColored(col: ImVec4(1.0f, 0.0f, 1.0f, 1.0f), fmt: "Pink");
3631 ImGui::TextColored(col: ImVec4(1.0f, 1.0f, 0.0f, 1.0f), fmt: "Yellow");
3632 ImGui::TextDisabled(fmt: "Disabled");
3633 ImGui::SameLine(); HelpMarker(desc: "The TextDisabled color is stored in ImGuiStyle.");
3634 ImGui::TreePop();
3635 }
3636
3637 IMGUI_DEMO_MARKER("Widgets/Text/Font Size");
3638 if (ImGui::TreeNode(label: "Font Size"))
3639 {
3640 ImGuiStyle& style = ImGui::GetStyle();
3641 const float global_scale = style.FontScaleMain * style.FontScaleDpi;
3642 ImGui::Text(fmt: "style.FontScaleMain = %0.2f", style.FontScaleMain);
3643 ImGui::Text(fmt: "style.FontScaleDpi = %0.2f", style.FontScaleDpi);
3644 ImGui::Text(fmt: "global_scale = ~%0.2f", global_scale); // This is not technically accurate as internal scales may apply, but conceptually let's pretend it is.
3645 ImGui::Text(fmt: "FontSize = %0.2f", ImGui::GetFontSize());
3646
3647 ImGui::SeparatorText(label: "");
3648 static float custom_size = 16.0f;
3649 ImGui::SliderFloat(label: "custom_size", v: &custom_size, v_min: 10.0f, v_max: 100.0f, format: "%.0f");
3650 ImGui::Text(fmt: "ImGui::PushFont(nullptr, custom_size);");
3651 ImGui::PushFont(NULL, font_size_base_unscaled: custom_size);
3652 ImGui::Text(fmt: "FontSize = %.2f (== %.2f * global_scale)", ImGui::GetFontSize(), custom_size);
3653 ImGui::PopFont();
3654
3655 ImGui::SeparatorText(label: "");
3656 static float custom_scale = 1.0f;
3657 ImGui::SliderFloat(label: "custom_scale", v: &custom_scale, v_min: 0.5f, v_max: 4.0f, format: "%.2f");
3658 ImGui::Text(fmt: "ImGui::PushFont(nullptr, style.FontSizeBase * custom_scale);");
3659 ImGui::PushFont(NULL, font_size_base_unscaled: style.FontSizeBase * custom_scale);
3660 ImGui::Text(fmt: "FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), custom_scale);
3661 ImGui::PopFont();
3662
3663 ImGui::SeparatorText(label: "");
3664 for (float scaling = 0.5f; scaling <= 4.0f; scaling += 0.5f)
3665 {
3666 ImGui::PushFont(NULL, font_size_base_unscaled: style.FontSizeBase * scaling);
3667 ImGui::Text(fmt: "FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), scaling);
3668 ImGui::PopFont();
3669 }
3670
3671 ImGui::TreePop();
3672 }
3673
3674 IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping");
3675 if (ImGui::TreeNode(label: "Word Wrapping"))
3676 {
3677 // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
3678 ImGui::TextWrapped(
3679 fmt: "This text should automatically wrap on the edge of the window. The current implementation "
3680 "for text wrapping follows simple rules suitable for English and possibly other languages.");
3681 ImGui::Spacing();
3682
3683 static float wrap_width = 200.0f;
3684 ImGui::SliderFloat(label: "Wrap width", v: &wrap_width, v_min: -20, v_max: 600, format: "%.0f");
3685
3686 ImDrawList* draw_list = ImGui::GetWindowDrawList();
3687 for (int n = 0; n < 2; n++)
3688 {
3689 ImGui::Text(fmt: "Test paragraph %d:", n);
3690 ImVec2 pos = ImGui::GetCursorScreenPos();
3691 ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);
3692 ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());
3693 ImGui::PushTextWrapPos(wrap_local_pos_x: ImGui::GetCursorPos().x + wrap_width);
3694 if (n == 0)
3695 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);
3696 else
3697 ImGui::Text(fmt: "aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh");
3698
3699 // Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
3700 draw_list->AddRect(p_min: ImGui::GetItemRectMin(), p_max: ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));
3701 draw_list->AddRectFilled(p_min: marker_min, p_max: marker_max, IM_COL32(255, 0, 255, 255));
3702 ImGui::PopTextWrapPos();
3703 }
3704
3705 ImGui::TreePop();
3706 }
3707
3708 IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text");
3709 if (ImGui::TreeNode(label: "UTF-8 Text"))
3710 {
3711 // UTF-8 test with Japanese characters
3712 // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
3713 // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
3714 // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
3715 // can save your source files as 'UTF-8 without signature').
3716 // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
3717 // CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
3718 // Don't do this in your application! Please use u8"text in any language" in your application!
3719 // Note that characters values are preserved even by InputText() if the font cannot be displayed,
3720 // so you can safely copy & paste garbled characters into another application.
3721 ImGui::TextWrapped(
3722 fmt: "CJK text will only appear if the font was loaded with the appropriate CJK character ranges. "
3723 "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. "
3724 "Read docs/FONTS.md for details.");
3725 ImGui::Text(fmt: "Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
3726 ImGui::Text(fmt: "Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
3727 static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
3728 //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis
3729 ImGui::InputText(label: "UTF-8 input", buf, IM_ARRAYSIZE(buf));
3730 ImGui::TreePop();
3731 }
3732 ImGui::TreePop();
3733 }
3734}
3735
3736//-----------------------------------------------------------------------------
3737// [SECTION] DemoWindowWidgetsTextFilter()
3738//-----------------------------------------------------------------------------
3739
3740static void DemoWindowWidgetsTextFilter()
3741{
3742 IMGUI_DEMO_MARKER("Widgets/Text Filter");
3743 if (ImGui::TreeNode(label: "Text Filter"))
3744 {
3745 // Helper class to easy setup a text filter.
3746 // You may want to implement a more feature-full filtering scheme in your own application.
3747 HelpMarker(desc: "Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.");
3748 static ImGuiTextFilter filter;
3749 ImGui::Text(fmt: "Filter usage:\n"
3750 " \"\" display all lines\n"
3751 " \"xxx\" display lines containing \"xxx\"\n"
3752 " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n"
3753 " \"-xxx\" hide lines containing \"xxx\"");
3754 filter.Draw();
3755 const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" };
3756 for (int i = 0; i < IM_ARRAYSIZE(lines); i++)
3757 if (filter.PassFilter(text: lines[i]))
3758 ImGui::BulletText(fmt: "%s", lines[i]);
3759 ImGui::TreePop();
3760 }
3761}
3762
3763//-----------------------------------------------------------------------------
3764// [SECTION] DemoWindowWidgetsTextInput()
3765//-----------------------------------------------------------------------------
3766
3767static void DemoWindowWidgetsTextInput()
3768{
3769 // To wire InputText() with std::string or any other custom string type,
3770 // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
3771 IMGUI_DEMO_MARKER("Widgets/Text Input");
3772 if (ImGui::TreeNode(label: "Text Input"))
3773 {
3774 IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input");
3775 if (ImGui::TreeNode(label: "Multi-line Text Input"))
3776 {
3777 // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize
3778 // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.
3779 static char text[1024 * 16] =
3780 "/*\n"
3781 " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"
3782 " the hexadecimal encoding of one offending instruction,\n"
3783 " more formally, the invalid operand with locked CMPXCHG8B\n"
3784 " instruction bug, is a design flaw in the majority of\n"
3785 " Intel Pentium, Pentium MMX, and Pentium OverDrive\n"
3786 " processors (all in the P5 microarchitecture).\n"
3787 "*/\n\n"
3788 "label:\n"
3789 "\tlock cmpxchg8b eax\n";
3790
3791 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
3792 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)");
3793 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ReadOnly", flags: &flags, flags_value: ImGuiInputTextFlags_ReadOnly);
3794 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_AllowTabInput", flags: &flags, flags_value: ImGuiInputTextFlags_AllowTabInput);
3795 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.");
3796 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_CtrlEnterForNewLine", flags: &flags, flags_value: ImGuiInputTextFlags_CtrlEnterForNewLine);
3797 ImGui::InputTextMultiline(label: "##source", buf: text, IM_ARRAYSIZE(text), size: ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
3798 ImGui::TreePop();
3799 }
3800
3801 IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input");
3802 if (ImGui::TreeNode(label: "Filtered Text Input"))
3803 {
3804 struct TextFilters
3805 {
3806 // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)
3807 static int FilterCasingSwap(ImGuiInputTextCallbackData* data)
3808 {
3809 if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase
3810 else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase
3811 return 0;
3812 }
3813
3814 // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)
3815 static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)
3816 {
3817 if (data->EventChar < 256 && strchr(s: "imgui", c: (char)data->EventChar))
3818 return 0;
3819 return 1;
3820 }
3821 };
3822
3823 static char buf1[32] = ""; ImGui::InputText(label: "default", buf: buf1, IM_ARRAYSIZE(buf1));
3824 static char buf2[32] = ""; ImGui::InputText(label: "decimal", buf: buf2, IM_ARRAYSIZE(buf2), flags: ImGuiInputTextFlags_CharsDecimal);
3825 static char buf3[32] = ""; ImGui::InputText(label: "hexadecimal", buf: buf3, IM_ARRAYSIZE(buf3), flags: ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
3826 static char buf4[32] = ""; ImGui::InputText(label: "uppercase", buf: buf4, IM_ARRAYSIZE(buf4), flags: ImGuiInputTextFlags_CharsUppercase);
3827 static char buf5[32] = ""; ImGui::InputText(label: "no blank", buf: buf5, IM_ARRAYSIZE(buf5), flags: ImGuiInputTextFlags_CharsNoBlank);
3828 static char buf6[32] = ""; ImGui::InputText(label: "casing swap", buf: buf6, IM_ARRAYSIZE(buf6), flags: ImGuiInputTextFlags_CallbackCharFilter, callback: TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.
3829 static char buf7[32] = ""; ImGui::InputText(label: "\"imgui\"", buf: buf7, IM_ARRAYSIZE(buf7), flags: ImGuiInputTextFlags_CallbackCharFilter, callback: TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.
3830 ImGui::TreePop();
3831 }
3832
3833 IMGUI_DEMO_MARKER("Widgets/Text Input/Password input");
3834 if (ImGui::TreeNode(label: "Password Input"))
3835 {
3836 static char password[64] = "password123";
3837 ImGui::InputText(label: "password", buf: password, IM_ARRAYSIZE(password), flags: ImGuiInputTextFlags_Password);
3838 ImGui::SameLine(); HelpMarker(desc: "Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
3839 ImGui::InputTextWithHint(label: "password (w/ hint)", hint: "<password>", buf: password, IM_ARRAYSIZE(password), flags: ImGuiInputTextFlags_Password);
3840 ImGui::InputText(label: "password (clear)", buf: password, IM_ARRAYSIZE(password));
3841 ImGui::TreePop();
3842 }
3843
3844 IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks");
3845 if (ImGui::TreeNode(label: "Completion, History, Edit Callbacks"))
3846 {
3847 struct Funcs
3848 {
3849 static int MyCallback(ImGuiInputTextCallbackData* data)
3850 {
3851 if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)
3852 {
3853 data->InsertChars(pos: data->CursorPos, text: "..");
3854 }
3855 else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)
3856 {
3857 if (data->EventKey == ImGuiKey_UpArrow)
3858 {
3859 data->DeleteChars(pos: 0, bytes_count: data->BufTextLen);
3860 data->InsertChars(pos: 0, text: "Pressed Up!");
3861 data->SelectAll();
3862 }
3863 else if (data->EventKey == ImGuiKey_DownArrow)
3864 {
3865 data->DeleteChars(pos: 0, bytes_count: data->BufTextLen);
3866 data->InsertChars(pos: 0, text: "Pressed Down!");
3867 data->SelectAll();
3868 }
3869 }
3870 else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)
3871 {
3872 // Toggle casing of first character
3873 char c = data->Buf[0];
3874 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32;
3875 data->BufDirty = true;
3876
3877 // Increment a counter
3878 int* p_int = (int*)data->UserData;
3879 *p_int = *p_int + 1;
3880 }
3881 return 0;
3882 }
3883 };
3884 static char buf1[64];
3885 ImGui::InputText(label: "Completion", buf: buf1, IM_ARRAYSIZE(buf1), flags: ImGuiInputTextFlags_CallbackCompletion, callback: Funcs::MyCallback);
3886 ImGui::SameLine(); HelpMarker(
3887 desc: "Here we append \"..\" each time Tab is pressed. "
3888 "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3889
3890 static char buf2[64];
3891 ImGui::InputText(label: "History", buf: buf2, IM_ARRAYSIZE(buf2), flags: ImGuiInputTextFlags_CallbackHistory, callback: Funcs::MyCallback);
3892 ImGui::SameLine(); HelpMarker(
3893 desc: "Here we replace and select text each time Up/Down are pressed. "
3894 "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3895
3896 static char buf3[64];
3897 static int edit_count = 0;
3898 ImGui::InputText(label: "Edit", buf: buf3, IM_ARRAYSIZE(buf3), flags: ImGuiInputTextFlags_CallbackEdit, callback: Funcs::MyCallback, user_data: (void*)&edit_count);
3899 ImGui::SameLine(); HelpMarker(
3900 desc: "Here we toggle the casing of the first character on every edit + count edits.");
3901 ImGui::SameLine(); ImGui::Text(fmt: "(%d)", edit_count);
3902
3903 ImGui::TreePop();
3904 }
3905
3906 IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback");
3907 if (ImGui::TreeNode(label: "Resize Callback"))
3908 {
3909 // To wire InputText() with std::string or any other custom string type,
3910 // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
3911 // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
3912 HelpMarker(
3913 desc: "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n"
3914 "See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
3915 struct Funcs
3916 {
3917 static int MyResizeCallback(ImGuiInputTextCallbackData* data)
3918 {
3919 if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
3920 {
3921 ImVector<char>* my_str = (ImVector<char>*)data->UserData;
3922 IM_ASSERT(my_str->begin() == data->Buf);
3923 my_str->resize(new_size: data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
3924 data->Buf = my_str->begin();
3925 }
3926 return 0;
3927 }
3928
3929 // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
3930 // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
3931 static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
3932 {
3933 IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
3934 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);
3935 }
3936 };
3937
3938 // For this demo we are using ImVector as a string container.
3939 // Note that because we need to store a terminating zero character, our size/capacity are 1 more
3940 // than usually reported by a typical string class.
3941 static ImVector<char> my_str;
3942 if (my_str.empty())
3943 my_str.push_back(v: 0);
3944 Funcs::MyInputTextMultiline(label: "##MyStr", my_str: &my_str, size: ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));
3945 ImGui::Text(fmt: "Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
3946 ImGui::TreePop();
3947 }
3948
3949 IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment");
3950 if (ImGui::TreeNode(label: "Eliding, Alignment"))
3951 {
3952 static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp";
3953 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft;
3954 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ElideLeft", flags: &flags, flags_value: ImGuiInputTextFlags_ElideLeft);
3955 ImGui::InputText(label: "Path", buf: buf1, IM_ARRAYSIZE(buf1), flags);
3956 ImGui::TreePop();
3957 }
3958
3959 IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous");
3960 if (ImGui::TreeNode(label: "Miscellaneous"))
3961 {
3962 static char buf1[16];
3963 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll;
3964 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_EscapeClearsAll", flags: &flags, flags_value: ImGuiInputTextFlags_EscapeClearsAll);
3965 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_ReadOnly", flags: &flags, flags_value: ImGuiInputTextFlags_ReadOnly);
3966 ImGui::CheckboxFlags(label: "ImGuiInputTextFlags_NoUndoRedo", flags: &flags, flags_value: ImGuiInputTextFlags_NoUndoRedo);
3967 ImGui::InputText(label: "Hello", buf: buf1, IM_ARRAYSIZE(buf1), flags);
3968 ImGui::TreePop();
3969 }
3970
3971 ImGui::TreePop();
3972 }
3973
3974}
3975
3976//-----------------------------------------------------------------------------
3977// [SECTION] DemoWindowWidgetsTooltips()
3978//-----------------------------------------------------------------------------
3979
3980static void DemoWindowWidgetsTooltips()
3981{
3982 IMGUI_DEMO_MARKER("Widgets/Tooltips");
3983 if (ImGui::TreeNode(label: "Tooltips"))
3984 {
3985 // Tooltips are windows following the mouse. They do not take focus away.
3986 ImGui::SeparatorText(label: "General");
3987
3988 // Typical use cases:
3989 // - Short-form (text only): SetItemTooltip("Hello");
3990 // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); }
3991
3992 // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); }
3993 // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); }
3994
3995 HelpMarker(
3996 desc: "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n"
3997 "We provide a helper SetItemTooltip() function to perform the two with standards flags.");
3998
3999 ImVec2 sz = ImVec2(-FLT_MIN, 0.0f);
4000
4001 ImGui::Button(label: "Basic", size: sz);
4002 ImGui::SetItemTooltip("I am a tooltip");
4003
4004 ImGui::Button(label: "Fancy", size: sz);
4005 if (ImGui::BeginItemTooltip())
4006 {
4007 ImGui::Text(fmt: "I am a fancy tooltip");
4008 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
4009 ImGui::PlotLines(label: "Curve", values: arr, IM_ARRAYSIZE(arr));
4010 ImGui::Text(fmt: "Sin(time) = %f", sinf(x: (float)ImGui::GetTime()));
4011 ImGui::EndTooltip();
4012 }
4013
4014 ImGui::SeparatorText(label: "Always On");
4015
4016 // Showcase NOT relying on a IsItemHovered() to emit a tooltip.
4017 // Here the tooltip is always emitted when 'always_on == true'.
4018 static int always_on = 0;
4019 ImGui::RadioButton(label: "Off", v: &always_on, v_button: 0);
4020 ImGui::SameLine();
4021 ImGui::RadioButton(label: "Always On (Simple)", v: &always_on, v_button: 1);
4022 ImGui::SameLine();
4023 ImGui::RadioButton(label: "Always On (Advanced)", v: &always_on, v_button: 2);
4024 if (always_on == 1)
4025 ImGui::SetTooltip("I am following you around.");
4026 else if (always_on == 2 && ImGui::BeginTooltip())
4027 {
4028 ImGui::ProgressBar(fraction: sinf(x: (float)ImGui::GetTime()) * 0.5f + 0.5f, size_arg: ImVec2(ImGui::GetFontSize() * 25, 0.0f));
4029 ImGui::EndTooltip();
4030 }
4031
4032 ImGui::SeparatorText(label: "Custom");
4033
4034 HelpMarker(
4035 desc: "Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize "
4036 "tooltip activation details across your application. You may however decide to use custom "
4037 "flags for a specific tooltip instance.");
4038
4039 // The following examples are passed for documentation purpose but may not be useful to most users.
4040 // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from
4041 // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.
4042 // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.
4043 ImGui::Button(label: "Manual", size: sz);
4044 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip))
4045 ImGui::SetTooltip("I am a manually emitted tooltip.");
4046
4047 ImGui::Button(label: "DelayNone", size: sz);
4048 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayNone))
4049 ImGui::SetTooltip("I am a tooltip with no delay.");
4050
4051 ImGui::Button(label: "DelayShort", size: sz);
4052 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))
4053 ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort);
4054
4055 ImGui::Button(label: "DelayLong", size: sz);
4056 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay))
4057 ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal);
4058
4059 ImGui::Button(label: "Stationary", size: sz);
4060 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_Stationary))
4061 ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating.");
4062
4063 // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',
4064 // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.
4065 ImGui::BeginDisabled();
4066 ImGui::Button(label: "Disabled item", size: sz);
4067 if (ImGui::IsItemHovered(flags: ImGuiHoveredFlags_ForTooltip))
4068 ImGui::SetTooltip("I am a a tooltip for a disabled item.");
4069 ImGui::EndDisabled();
4070
4071 ImGui::TreePop();
4072 }
4073}
4074
4075//-----------------------------------------------------------------------------
4076// [SECTION] DemoWindowWidgetsTreeNodes()
4077//-----------------------------------------------------------------------------
4078
4079static void DemoWindowWidgetsTreeNodes()
4080{
4081 IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
4082 if (ImGui::TreeNode(label: "Tree Nodes"))
4083 {
4084 // See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
4085 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees");
4086 if (ImGui::TreeNode(label: "Basic trees"))
4087 {
4088 for (int i = 0; i < 5; i++)
4089 {
4090 // Use SetNextItemOpen() so set the default state of a node to be open. We could
4091 // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
4092 if (i == 0)
4093 ImGui::SetNextItemOpen(is_open: true, cond: ImGuiCond_Once);
4094
4095 // Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict.
4096 // An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)',
4097 // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine.
4098 ImGui::PushID(int_id: i);
4099 if (ImGui::TreeNode(str_id: "", fmt: "Child %d", i))
4100 {
4101 ImGui::Text(fmt: "blah blah");
4102 ImGui::SameLine();
4103 if (ImGui::SmallButton(label: "button")) {}
4104 ImGui::TreePop();
4105 }
4106 ImGui::PopID();
4107 }
4108 ImGui::TreePop();
4109 }
4110
4111 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines");
4112 if (ImGui::TreeNode(label: "Hierarchy lines"))
4113 {
4114 static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;
4115 HelpMarker(desc: "Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
4116 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_DrawLinesNone", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_DrawLinesNone);
4117 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_DrawLinesFull", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_DrawLinesFull);
4118 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_DrawLinesToNodes", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_DrawLinesToNodes);
4119
4120 if (ImGui::TreeNodeEx(label: "Parent", flags: base_flags))
4121 {
4122 if (ImGui::TreeNodeEx(label: "Child 1", flags: base_flags))
4123 {
4124 ImGui::Button(label: "Button for Child 1");
4125 ImGui::TreePop();
4126 }
4127 if (ImGui::TreeNodeEx(label: "Child 2", flags: base_flags))
4128 {
4129 ImGui::Button(label: "Button for Child 2");
4130 ImGui::TreePop();
4131 }
4132 ImGui::Text(fmt: "Remaining contents");
4133 ImGui::Text(fmt: "Remaining contents");
4134 ImGui::TreePop();
4135 }
4136
4137 ImGui::TreePop();
4138 }
4139
4140 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
4141 if (ImGui::TreeNode(label: "Advanced, with Selectable nodes"))
4142 {
4143 HelpMarker(
4144 desc: "This is a more typical looking tree with selectable nodes.\n"
4145 "Click to select, CTRL+Click to toggle, click on arrows or double-click to open.");
4146 static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
4147 static bool align_label_with_current_x_position = false;
4148 static bool test_drag_and_drop = false;
4149 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_OpenOnArrow", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_OpenOnArrow);
4150 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_OpenOnDoubleClick", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_OpenOnDoubleClick);
4151 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.");
4152 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanFullWidth", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_SpanFullWidth);
4153 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.");
4154 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanAllColumns", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker(desc: "For use in Tables only.");
4155 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_AllowOverlap", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_AllowOverlap);
4156 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_Framed", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker(desc: "Draw frame with background (e.g. for CollapsingHeader)");
4157 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_NavLeftJumpsToParent", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_NavLeftJumpsToParent);
4158
4159 HelpMarker(desc: "Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
4160 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_DrawLinesNone", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_DrawLinesNone);
4161 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_DrawLinesFull", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_DrawLinesFull);
4162 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_DrawLinesToNodes", flags: &base_flags, flags_value: ImGuiTreeNodeFlags_DrawLinesToNodes);
4163
4164 ImGui::Checkbox(label: "Align label with current X position", v: &align_label_with_current_x_position);
4165 ImGui::Checkbox(label: "Test tree node as drag source", v: &test_drag_and_drop);
4166 ImGui::Text(fmt: "Hello!");
4167 if (align_label_with_current_x_position)
4168 ImGui::Unindent(indent_w: ImGui::GetTreeNodeToLabelSpacing());
4169
4170 // 'selection_mask' is dumb representation of what may be user-side selection state.
4171 // You may retain selection state inside or outside your objects in whatever format you see fit.
4172 // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end
4173 /// of the loop. May be a pointer to your own node type, etc.
4174 static int selection_mask = (1 << 2);
4175 int node_clicked = -1;
4176 for (int i = 0; i < 6; i++)
4177 {
4178 // Disable the default "open on single-click behavior" + set Selected flag according to our selection.
4179 // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
4180 ImGuiTreeNodeFlags node_flags = base_flags;
4181 const bool is_selected = (selection_mask & (1 << i)) != 0;
4182 if (is_selected)
4183 node_flags |= ImGuiTreeNodeFlags_Selected;
4184 if (i < 3)
4185 {
4186 // Items 0..2 are Tree Node
4187 bool node_open = ImGui::TreeNodeEx(ptr_id: (void*)(intptr_t)i, flags: node_flags, fmt: "Selectable Node %d", i);
4188 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4189 node_clicked = i;
4190 if (test_drag_and_drop && ImGui::BeginDragDropSource())
4191 {
4192 ImGui::SetDragDropPayload(type: "_TREENODE", NULL, sz: 0);
4193 ImGui::Text(fmt: "This is a drag and drop source");
4194 ImGui::EndDragDropSource();
4195 }
4196 if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
4197 {
4198 // Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
4199 ImGui::SameLine();
4200 if (ImGui::SmallButton(label: "button")) {}
4201 }
4202 if (node_open)
4203 {
4204 ImGui::BulletText(fmt: "Blah blah\nBlah Blah");
4205 ImGui::SameLine();
4206 ImGui::SmallButton(label: "Button");
4207 ImGui::TreePop();
4208 }
4209 }
4210 else
4211 {
4212 // Items 3..5 are Tree Leaves
4213 // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
4214 // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
4215 node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
4216 ImGui::TreeNodeEx(ptr_id: (void*)(intptr_t)i, flags: node_flags, fmt: "Selectable Leaf %d", i);
4217 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4218 node_clicked = i;
4219 if (test_drag_and_drop && ImGui::BeginDragDropSource())
4220 {
4221 ImGui::SetDragDropPayload(type: "_TREENODE", NULL, sz: 0);
4222 ImGui::Text(fmt: "This is a drag and drop source");
4223 ImGui::EndDragDropSource();
4224 }
4225 }
4226 }
4227 if (node_clicked != -1)
4228 {
4229 // Update selection state
4230 // (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
4231 if (ImGui::GetIO().KeyCtrl)
4232 selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
4233 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
4234 selection_mask = (1 << node_clicked); // Click to single-select
4235 }
4236 if (align_label_with_current_x_position)
4237 ImGui::Indent(indent_w: ImGui::GetTreeNodeToLabelSpacing());
4238 ImGui::TreePop();
4239 }
4240 ImGui::TreePop();
4241 }
4242}
4243
4244//-----------------------------------------------------------------------------
4245// [SECTION] DemoWindowWidgetsVerticalSliders()
4246//-----------------------------------------------------------------------------
4247
4248static void DemoWindowWidgetsVerticalSliders()
4249{
4250 IMGUI_DEMO_MARKER("Widgets/Vertical Sliders");
4251 if (ImGui::TreeNode(label: "Vertical Sliders"))
4252 {
4253 const float spacing = 4;
4254 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(spacing, spacing));
4255
4256 static int int_value = 0;
4257 ImGui::VSliderInt(label: "##int", size: ImVec2(18, 160), v: &int_value, v_min: 0, v_max: 5);
4258 ImGui::SameLine();
4259
4260 static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };
4261 ImGui::PushID(str_id: "set1");
4262 for (int i = 0; i < 7; i++)
4263 {
4264 if (i > 0) ImGui::SameLine();
4265 ImGui::PushID(int_id: i);
4266 ImGui::PushStyleColor(idx: ImGuiCol_FrameBg, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.5f, v: 0.5f));
4267 ImGui::PushStyleColor(idx: ImGuiCol_FrameBgHovered, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.6f, v: 0.5f));
4268 ImGui::PushStyleColor(idx: ImGuiCol_FrameBgActive, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.7f, v: 0.5f));
4269 ImGui::PushStyleColor(idx: ImGuiCol_SliderGrab, col: (ImVec4)ImColor::HSV(h: i / 7.0f, s: 0.9f, v: 0.9f));
4270 ImGui::VSliderFloat(label: "##v", size: ImVec2(18, 160), v: &values[i], v_min: 0.0f, v_max: 1.0f, format: "");
4271 if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4272 ImGui::SetTooltip("%.3f", values[i]);
4273 ImGui::PopStyleColor(count: 4);
4274 ImGui::PopID();
4275 }
4276 ImGui::PopID();
4277
4278 ImGui::SameLine();
4279 ImGui::PushID(str_id: "set2");
4280 static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f };
4281 const int rows = 3;
4282 const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));
4283 for (int nx = 0; nx < 4; nx++)
4284 {
4285 if (nx > 0) ImGui::SameLine();
4286 ImGui::BeginGroup();
4287 for (int ny = 0; ny < rows; ny++)
4288 {
4289 ImGui::PushID(int_id: nx * rows + ny);
4290 ImGui::VSliderFloat(label: "##v", size: small_slider_size, v: &values2[nx], v_min: 0.0f, v_max: 1.0f, format: "");
4291 if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4292 ImGui::SetTooltip("%.3f", values2[nx]);
4293 ImGui::PopID();
4294 }
4295 ImGui::EndGroup();
4296 }
4297 ImGui::PopID();
4298
4299 ImGui::SameLine();
4300 ImGui::PushID(str_id: "set3");
4301 for (int i = 0; i < 4; i++)
4302 {
4303 if (i > 0) ImGui::SameLine();
4304 ImGui::PushID(int_id: i);
4305 ImGui::PushStyleVar(idx: ImGuiStyleVar_GrabMinSize, val: 40);
4306 ImGui::VSliderFloat(label: "##v", size: ImVec2(40, 160), v: &values[i], v_min: 0.0f, v_max: 1.0f, format: "%.2f\nsec");
4307 ImGui::PopStyleVar();
4308 ImGui::PopID();
4309 }
4310 ImGui::PopID();
4311 ImGui::PopStyleVar();
4312 ImGui::TreePop();
4313 }
4314}
4315
4316//-----------------------------------------------------------------------------
4317// [SECTION] DemoWindowWidgets()
4318//-----------------------------------------------------------------------------
4319
4320static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)
4321{
4322 IMGUI_DEMO_MARKER("Widgets");
4323 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
4324 if (!ImGui::CollapsingHeader(label: "Widgets"))
4325 return;
4326
4327 const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom
4328 if (disable_all)
4329 ImGui::BeginDisabled();
4330
4331 DemoWindowWidgetsBasic();
4332 DemoWindowWidgetsBullets();
4333 DemoWindowWidgetsCollapsingHeaders();
4334 DemoWindowWidgetsComboBoxes();
4335 DemoWindowWidgetsColorAndPickers();
4336 DemoWindowWidgetsDataTypes();
4337
4338 if (disable_all)
4339 ImGui::EndDisabled();
4340 DemoWindowWidgetsDisableBlocks(demo_data);
4341 if (disable_all)
4342 ImGui::BeginDisabled();
4343
4344 DemoWindowWidgetsDragAndDrop();
4345 DemoWindowWidgetsDragsAndSliders();
4346 DemoWindowWidgetsFonts();
4347 DemoWindowWidgetsImages();
4348 DemoWindowWidgetsListBoxes();
4349 DemoWindowWidgetsMultiComponents();
4350 DemoWindowWidgetsPlotting();
4351 DemoWindowWidgetsProgressBars();
4352 DemoWindowWidgetsQueryingStatuses();
4353 DemoWindowWidgetsSelectables();
4354 DemoWindowWidgetsSelectionAndMultiSelect(demo_data);
4355 DemoWindowWidgetsTabs();
4356 DemoWindowWidgetsText();
4357 DemoWindowWidgetsTextFilter();
4358 DemoWindowWidgetsTextInput();
4359 DemoWindowWidgetsTooltips();
4360 DemoWindowWidgetsTreeNodes();
4361 DemoWindowWidgetsVerticalSliders();
4362
4363 if (disable_all)
4364 ImGui::EndDisabled();
4365}
4366
4367//-----------------------------------------------------------------------------
4368// [SECTION] DemoWindowLayout()
4369//-----------------------------------------------------------------------------
4370
4371static void DemoWindowLayout()
4372{
4373 IMGUI_DEMO_MARKER("Layout");
4374 if (!ImGui::CollapsingHeader(label: "Layout & Scrolling"))
4375 return;
4376
4377 IMGUI_DEMO_MARKER("Layout/Child windows");
4378 if (ImGui::TreeNode(label: "Child windows"))
4379 {
4380 ImGui::SeparatorText(label: "Child windows");
4381
4382 HelpMarker(desc: "Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.");
4383 static bool disable_mouse_wheel = false;
4384 static bool disable_menu = false;
4385 ImGui::Checkbox(label: "Disable Mouse Wheel", v: &disable_mouse_wheel);
4386 ImGui::Checkbox(label: "Disable Menu", v: &disable_menu);
4387
4388 // Child 1: no border, enable horizontal scrollbar
4389 {
4390 ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
4391 if (disable_mouse_wheel)
4392 window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4393 ImGui::BeginChild(str_id: "ChildL", size: ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), child_flags: ImGuiChildFlags_None, window_flags);
4394 for (int i = 0; i < 100; i++)
4395 ImGui::Text(fmt: "%04d: scrollable region", i);
4396 ImGui::EndChild();
4397 }
4398
4399 ImGui::SameLine();
4400
4401 // Child 2: rounded border
4402 {
4403 ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
4404 if (disable_mouse_wheel)
4405 window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4406 if (!disable_menu)
4407 window_flags |= ImGuiWindowFlags_MenuBar;
4408 ImGui::PushStyleVar(idx: ImGuiStyleVar_ChildRounding, val: 5.0f);
4409 ImGui::BeginChild(str_id: "ChildR", size: ImVec2(0, 260), child_flags: ImGuiChildFlags_Borders, window_flags);
4410 if (!disable_menu && ImGui::BeginMenuBar())
4411 {
4412 if (ImGui::BeginMenu(label: "Menu"))
4413 {
4414 ShowExampleMenuFile();
4415 ImGui::EndMenu();
4416 }
4417 ImGui::EndMenuBar();
4418 }
4419 if (ImGui::BeginTable(str_id: "split", columns: 2, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))
4420 {
4421 for (int i = 0; i < 100; i++)
4422 {
4423 char buf[32];
4424 sprintf(s: buf, format: "%03d", i);
4425 ImGui::TableNextColumn();
4426 ImGui::Button(label: buf, size: ImVec2(-FLT_MIN, 0.0f));
4427 }
4428 ImGui::EndTable();
4429 }
4430 ImGui::EndChild();
4431 ImGui::PopStyleVar();
4432 }
4433
4434 // Child 3: manual-resize
4435 ImGui::SeparatorText(label: "Manual-resize");
4436 {
4437 HelpMarker(desc: "Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
4438 //if (ImGui::Button("Set Height to 200"))
4439 // ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
4440
4441 ImGui::PushStyleColor(idx: ImGuiCol_ChildBg, col: ImGui::GetStyleColorVec4(idx: ImGuiCol_FrameBg));
4442 if (ImGui::BeginChild(str_id: "ResizableChild", size: ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
4443 for (int n = 0; n < 10; n++)
4444 ImGui::Text(fmt: "Line %04d", n);
4445 ImGui::PopStyleColor();
4446 ImGui::EndChild();
4447 }
4448
4449 // Child 4: auto-resizing height with a limit
4450 ImGui::SeparatorText(label: "Auto-resize with constraints");
4451 {
4452 static int draw_lines = 3;
4453 static int max_height_in_lines = 10;
4454 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4455 ImGui::DragInt(label: "Lines Count", v: &draw_lines, v_speed: 0.2f);
4456 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4457 ImGui::DragInt(label: "Max Height (in Lines)", v: &max_height_in_lines, v_speed: 0.2f);
4458
4459 ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), size_max: ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));
4460 if (ImGui::BeginChild(str_id: "ConstrainedChild", size: ImVec2(-FLT_MIN, 0.0f), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY))
4461 for (int n = 0; n < draw_lines; n++)
4462 ImGui::Text(fmt: "Line %04d", n);
4463 ImGui::EndChild();
4464 }
4465
4466 ImGui::SeparatorText(label: "Misc/Advanced");
4467
4468 // Demonstrate a few extra things
4469 // - Changing ImGuiCol_ChildBg (which is transparent black in default styles)
4470 // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
4471 // You can also call SetNextWindowPos() to position the child window. The parent window will effectively
4472 // layout from this position.
4473 // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
4474 // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.
4475 {
4476 static int offset_x = 0;
4477 static bool override_bg_color = true;
4478 static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;
4479 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4480 ImGui::DragInt(label: "Offset X", v: &offset_x, v_speed: 1.0f, v_min: -1000, v_max: 1000);
4481 ImGui::Checkbox(label: "Override ChildBg color", v: &override_bg_color);
4482 ImGui::CheckboxFlags(label: "ImGuiChildFlags_Borders", flags: &child_flags, flags_value: ImGuiChildFlags_Borders);
4483 ImGui::CheckboxFlags(label: "ImGuiChildFlags_AlwaysUseWindowPadding", flags: &child_flags, flags_value: ImGuiChildFlags_AlwaysUseWindowPadding);
4484 ImGui::CheckboxFlags(label: "ImGuiChildFlags_ResizeX", flags: &child_flags, flags_value: ImGuiChildFlags_ResizeX);
4485 ImGui::CheckboxFlags(label: "ImGuiChildFlags_ResizeY", flags: &child_flags, flags_value: ImGuiChildFlags_ResizeY);
4486 ImGui::CheckboxFlags(label: "ImGuiChildFlags_FrameStyle", flags: &child_flags, flags_value: ImGuiChildFlags_FrameStyle);
4487 ImGui::SameLine(); HelpMarker(desc: "Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.");
4488 if (child_flags & ImGuiChildFlags_FrameStyle)
4489 override_bg_color = false;
4490
4491 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
4492 if (override_bg_color)
4493 ImGui::PushStyleColor(idx: ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
4494 ImGui::BeginChild(str_id: "Red", size: ImVec2(200, 100), child_flags, window_flags: ImGuiWindowFlags_None);
4495 if (override_bg_color)
4496 ImGui::PopStyleColor();
4497
4498 for (int n = 0; n < 50; n++)
4499 ImGui::Text(fmt: "Some test %d", n);
4500 ImGui::EndChild();
4501 bool child_is_hovered = ImGui::IsItemHovered();
4502 ImVec2 child_rect_min = ImGui::GetItemRectMin();
4503 ImVec2 child_rect_max = ImGui::GetItemRectMax();
4504 ImGui::Text(fmt: "Hovered: %d", child_is_hovered);
4505 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);
4506 }
4507
4508 ImGui::TreePop();
4509 }
4510
4511 IMGUI_DEMO_MARKER("Layout/Widgets Width");
4512 if (ImGui::TreeNode(label: "Widgets Width"))
4513 {
4514 static float f = 0.0f;
4515 static bool show_indented_items = true;
4516 ImGui::Checkbox(label: "Show indented items", v: &show_indented_items);
4517
4518 // Use SetNextItemWidth() to set the width of a single upcoming item.
4519 // Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
4520 // In real code use you'll probably want to choose width values that are proportional to your font size
4521 // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
4522
4523 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(100)");
4524 ImGui::SameLine(); HelpMarker(desc: "Fixed width.");
4525 ImGui::PushItemWidth(item_width: 100);
4526 ImGui::DragFloat(label: "float##1b", v: &f);
4527 if (show_indented_items)
4528 {
4529 ImGui::Indent();
4530 ImGui::DragFloat(label: "float (indented)##1b", v: &f);
4531 ImGui::Unindent();
4532 }
4533 ImGui::PopItemWidth();
4534
4535 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(-100)");
4536 ImGui::SameLine(); HelpMarker(desc: "Align to right edge minus 100");
4537 ImGui::PushItemWidth(item_width: -100);
4538 ImGui::DragFloat(label: "float##2a", v: &f);
4539 if (show_indented_items)
4540 {
4541 ImGui::Indent();
4542 ImGui::DragFloat(label: "float (indented)##2b", v: &f);
4543 ImGui::Unindent();
4544 }
4545 ImGui::PopItemWidth();
4546
4547 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)");
4548 ImGui::SameLine(); HelpMarker(desc: "Half of available width.\n(~ right-cursor_pos)\n(works within a column set)");
4549 ImGui::PushItemWidth(item_width: ImGui::GetContentRegionAvail().x * 0.5f);
4550 ImGui::DragFloat(label: "float##3a", v: &f);
4551 if (show_indented_items)
4552 {
4553 ImGui::Indent();
4554 ImGui::DragFloat(label: "float (indented)##3b", v: &f);
4555 ImGui::Unindent();
4556 }
4557 ImGui::PopItemWidth();
4558
4559 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)");
4560 ImGui::SameLine(); HelpMarker(desc: "Align to right edge minus half");
4561 ImGui::PushItemWidth(item_width: -ImGui::GetContentRegionAvail().x * 0.5f);
4562 ImGui::DragFloat(label: "float##4a", v: &f);
4563 if (show_indented_items)
4564 {
4565 ImGui::Indent();
4566 ImGui::DragFloat(label: "float (indented)##4b", v: &f);
4567 ImGui::Unindent();
4568 }
4569 ImGui::PopItemWidth();
4570
4571 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))");
4572 ImGui::PushItemWidth(item_width: -IM_MIN(ImGui::GetFontSize() * 12, ImGui::GetContentRegionAvail().x * 0.40f));
4573 ImGui::DragFloat(label: "float##5a", v: &f);
4574 if (show_indented_items)
4575 {
4576 ImGui::Indent();
4577 ImGui::DragFloat(label: "float (indented)##5b", v: &f);
4578 ImGui::Unindent();
4579 }
4580 ImGui::PopItemWidth();
4581
4582 // Demonstrate using PushItemWidth to surround three items.
4583 // Calling SetNextItemWidth() before each of them would have the same effect.
4584 ImGui::Text(fmt: "SetNextItemWidth/PushItemWidth(-FLT_MIN)");
4585 ImGui::SameLine(); HelpMarker(desc: "Align to right edge");
4586 ImGui::PushItemWidth(item_width: -FLT_MIN);
4587 ImGui::DragFloat(label: "##float6a", v: &f);
4588 if (show_indented_items)
4589 {
4590 ImGui::Indent();
4591 ImGui::DragFloat(label: "float (indented)##6b", v: &f);
4592 ImGui::Unindent();
4593 }
4594 ImGui::PopItemWidth();
4595
4596 ImGui::TreePop();
4597 }
4598
4599 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout");
4600 if (ImGui::TreeNode(label: "Basic Horizontal Layout"))
4601 {
4602 ImGui::TextWrapped(fmt: "(Use ImGui::SameLine() to keep adding items to the right of the preceding item)");
4603
4604 // Text
4605 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine");
4606 ImGui::Text(fmt: "Two items: Hello"); ImGui::SameLine();
4607 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Sailor");
4608
4609 // Adjust spacing
4610 ImGui::Text(fmt: "More spacing: Hello"); ImGui::SameLine(offset_from_start_x: 0, spacing: 20);
4611 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Sailor");
4612
4613 // Button
4614 ImGui::AlignTextToFramePadding();
4615 ImGui::Text(fmt: "Normal buttons"); ImGui::SameLine();
4616 ImGui::Button(label: "Banana"); ImGui::SameLine();
4617 ImGui::Button(label: "Apple"); ImGui::SameLine();
4618 ImGui::Button(label: "Corniflower");
4619
4620 // Button
4621 ImGui::Text(fmt: "Small buttons"); ImGui::SameLine();
4622 ImGui::SmallButton(label: "Like this one"); ImGui::SameLine();
4623 ImGui::Text(fmt: "can fit within a text block.");
4624
4625 // Aligned to arbitrary position. Easy/cheap column.
4626 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)");
4627 ImGui::Text(fmt: "Aligned");
4628 ImGui::SameLine(offset_from_start_x: 150); ImGui::Text(fmt: "x=150");
4629 ImGui::SameLine(offset_from_start_x: 300); ImGui::Text(fmt: "x=300");
4630 ImGui::Text(fmt: "Aligned");
4631 ImGui::SameLine(offset_from_start_x: 150); ImGui::SmallButton(label: "x=150");
4632 ImGui::SameLine(offset_from_start_x: 300); ImGui::SmallButton(label: "x=300");
4633
4634 // Checkbox
4635 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)");
4636 static bool c1 = false, c2 = false, c3 = false, c4 = false;
4637 ImGui::Checkbox(label: "My", v: &c1); ImGui::SameLine();
4638 ImGui::Checkbox(label: "Tailor", v: &c2); ImGui::SameLine();
4639 ImGui::Checkbox(label: "Is", v: &c3); ImGui::SameLine();
4640 ImGui::Checkbox(label: "Rich", v: &c4);
4641
4642 // Various
4643 static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;
4644 ImGui::PushItemWidth(item_width: 80);
4645 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" };
4646 static int item = -1;
4647 ImGui::Combo(label: "Combo", current_item: &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine();
4648 ImGui::SliderFloat(label: "X", v: &f0, v_min: 0.0f, v_max: 5.0f); ImGui::SameLine();
4649 ImGui::SliderFloat(label: "Y", v: &f1, v_min: 0.0f, v_max: 5.0f); ImGui::SameLine();
4650 ImGui::SliderFloat(label: "Z", v: &f2, v_min: 0.0f, v_max: 5.0f);
4651 ImGui::PopItemWidth();
4652
4653 ImGui::PushItemWidth(item_width: 80);
4654 ImGui::Text(fmt: "Lists:");
4655 static int selection[4] = { 0, 1, 2, 3 };
4656 for (int i = 0; i < 4; i++)
4657 {
4658 if (i > 0) ImGui::SameLine();
4659 ImGui::PushID(int_id: i);
4660 ImGui::ListBox(label: "", current_item: &selection[i], items, IM_ARRAYSIZE(items));
4661 ImGui::PopID();
4662 //ImGui::SetItemTooltip("ListBox %d hovered", i);
4663 }
4664 ImGui::PopItemWidth();
4665
4666 // Dummy
4667 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy");
4668 ImVec2 button_sz(40, 40);
4669 ImGui::Button(label: "A", size: button_sz); ImGui::SameLine();
4670 ImGui::Dummy(size: button_sz); ImGui::SameLine();
4671 ImGui::Button(label: "B", size: button_sz);
4672
4673 // Manually wrapping
4674 // (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
4675 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping");
4676 ImGui::Text(fmt: "Manual wrapping:");
4677 ImGuiStyle& style = ImGui::GetStyle();
4678 int buttons_count = 20;
4679 float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
4680 for (int n = 0; n < buttons_count; n++)
4681 {
4682 ImGui::PushID(int_id: n);
4683 ImGui::Button(label: "Box", size: button_sz);
4684 float last_button_x2 = ImGui::GetItemRectMax().x;
4685 float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line
4686 if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)
4687 ImGui::SameLine();
4688 ImGui::PopID();
4689 }
4690
4691 ImGui::TreePop();
4692 }
4693
4694 IMGUI_DEMO_MARKER("Layout/Groups");
4695 if (ImGui::TreeNode(label: "Groups"))
4696 {
4697 HelpMarker(
4698 desc: "BeginGroup() basically locks the horizontal position for new line. "
4699 "EndGroup() bundles the whole group so that you can use \"item\" functions such as "
4700 "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
4701 ImGui::BeginGroup();
4702 {
4703 ImGui::BeginGroup();
4704 ImGui::Button(label: "AAA");
4705 ImGui::SameLine();
4706 ImGui::Button(label: "BBB");
4707 ImGui::SameLine();
4708 ImGui::BeginGroup();
4709 ImGui::Button(label: "CCC");
4710 ImGui::Button(label: "DDD");
4711 ImGui::EndGroup();
4712 ImGui::SameLine();
4713 ImGui::Button(label: "EEE");
4714 ImGui::EndGroup();
4715 ImGui::SetItemTooltip("First group hovered");
4716 }
4717 // Capture the group size and create widgets using the same size
4718 ImVec2 size = ImGui::GetItemRectSize();
4719 const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };
4720 ImGui::PlotHistogram(label: "##values", values, IM_ARRAYSIZE(values), values_offset: 0, NULL, scale_min: 0.0f, scale_max: 1.0f, graph_size: size);
4721
4722 ImGui::Button(label: "ACTION", size: ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4723 ImGui::SameLine();
4724 ImGui::Button(label: "REACTION", size: ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4725 ImGui::EndGroup();
4726 ImGui::SameLine();
4727
4728 ImGui::Button(label: "LEVERAGE\nBUZZWORD", size);
4729 ImGui::SameLine();
4730
4731 if (ImGui::BeginListBox(label: "List", size))
4732 {
4733 ImGui::Selectable(label: "Selected", selected: true);
4734 ImGui::Selectable(label: "Not Selected", selected: false);
4735 ImGui::EndListBox();
4736 }
4737
4738 ImGui::TreePop();
4739 }
4740
4741 IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment");
4742 if (ImGui::TreeNode(label: "Text Baseline Alignment"))
4743 {
4744 {
4745 ImGui::BulletText(fmt: "Text baseline:");
4746 ImGui::SameLine(); HelpMarker(
4747 desc: "This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. "
4748 "Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets.");
4749 ImGui::Indent();
4750
4751 ImGui::Text(fmt: "KO Blahblah"); ImGui::SameLine();
4752 ImGui::Button(label: "Some framed item"); ImGui::SameLine();
4753 HelpMarker(desc: "Baseline of button will look misaligned with text..");
4754
4755 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4756 // (because we don't know what's coming after the Text() statement, we need to move the text baseline
4757 // down by FramePadding.y ahead of time)
4758 ImGui::AlignTextToFramePadding();
4759 ImGui::Text(fmt: "OK Blahblah"); ImGui::SameLine();
4760 ImGui::Button(label: "Some framed item##2"); ImGui::SameLine();
4761 HelpMarker(desc: "We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y");
4762
4763 // SmallButton() uses the same vertical padding as Text
4764 ImGui::Button(label: "TEST##1"); ImGui::SameLine();
4765 ImGui::Text(fmt: "TEST"); ImGui::SameLine();
4766 ImGui::SmallButton(label: "TEST##2");
4767
4768 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4769 ImGui::AlignTextToFramePadding();
4770 ImGui::Text(fmt: "Text aligned to framed item"); ImGui::SameLine();
4771 ImGui::Button(label: "Item##1"); ImGui::SameLine();
4772 ImGui::Text(fmt: "Item"); ImGui::SameLine();
4773 ImGui::SmallButton(label: "Item##2"); ImGui::SameLine();
4774 ImGui::Button(label: "Item##3");
4775
4776 ImGui::Unindent();
4777 }
4778
4779 ImGui::Spacing();
4780
4781 {
4782 ImGui::BulletText(fmt: "Multi-line text:");
4783 ImGui::Indent();
4784 ImGui::Text(fmt: "One\nTwo\nThree"); ImGui::SameLine();
4785 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4786 ImGui::Text(fmt: "Banana");
4787
4788 ImGui::Text(fmt: "Banana"); ImGui::SameLine();
4789 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4790 ImGui::Text(fmt: "One\nTwo\nThree");
4791
4792 ImGui::Button(label: "HOP##1"); ImGui::SameLine();
4793 ImGui::Text(fmt: "Banana"); ImGui::SameLine();
4794 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4795 ImGui::Text(fmt: "Banana");
4796
4797 ImGui::Button(label: "HOP##2"); ImGui::SameLine();
4798 ImGui::Text(fmt: "Hello\nWorld"); ImGui::SameLine();
4799 ImGui::Text(fmt: "Banana");
4800 ImGui::Unindent();
4801 }
4802
4803 ImGui::Spacing();
4804
4805 {
4806 ImGui::BulletText(fmt: "Misc items:");
4807 ImGui::Indent();
4808
4809 // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
4810 ImGui::Button(label: "80x80", size: ImVec2(80, 80));
4811 ImGui::SameLine();
4812 ImGui::Button(label: "50x50", size: ImVec2(50, 50));
4813 ImGui::SameLine();
4814 ImGui::Button(label: "Button()");
4815 ImGui::SameLine();
4816 ImGui::SmallButton(label: "SmallButton()");
4817
4818 // Tree
4819 // (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline)
4820 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
4821 ImGui::Button(label: "Button##1");
4822 ImGui::SameLine(offset_from_start_x: 0.0f, spacing);
4823 if (ImGui::TreeNodeEx(label: "Node##1", flags: ImGuiTreeNodeFlags_DrawLinesNone))
4824 {
4825 // Placeholder tree data
4826 for (int i = 0; i < 6; i++)
4827 ImGui::BulletText(fmt: "Item %d..", i);
4828 ImGui::TreePop();
4829 }
4830
4831 // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
4832 // Otherwise you can use SmallButton() (smaller fit).
4833 ImGui::AlignTextToFramePadding();
4834
4835 // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
4836 // other contents below the node.
4837 bool node_open = ImGui::TreeNode(label: "Node##2");
4838 ImGui::SameLine(offset_from_start_x: 0.0f, spacing); ImGui::Button(label: "Button##2");
4839 if (node_open)
4840 {
4841 // Placeholder tree data
4842 for (int i = 0; i < 6; i++)
4843 ImGui::BulletText(fmt: "Item %d..", i);
4844 ImGui::TreePop();
4845 }
4846
4847 // Bullet
4848 ImGui::Button(label: "Button##3");
4849 ImGui::SameLine(offset_from_start_x: 0.0f, spacing);
4850 ImGui::BulletText(fmt: "Bullet text");
4851
4852 ImGui::AlignTextToFramePadding();
4853 ImGui::BulletText(fmt: "Node");
4854 ImGui::SameLine(offset_from_start_x: 0.0f, spacing); ImGui::Button(label: "Button##4");
4855 ImGui::Unindent();
4856 }
4857
4858 ImGui::TreePop();
4859 }
4860
4861 IMGUI_DEMO_MARKER("Layout/Scrolling");
4862 if (ImGui::TreeNode(label: "Scrolling"))
4863 {
4864 // Vertical scroll functions
4865 IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical");
4866 HelpMarker(desc: "Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.");
4867
4868 static int track_item = 50;
4869 static bool enable_track = true;
4870 static bool enable_extra_decorations = false;
4871 static float scroll_to_off_px = 0.0f;
4872 static float scroll_to_pos_px = 200.0f;
4873
4874 ImGui::Checkbox(label: "Decoration", v: &enable_extra_decorations);
4875
4876 ImGui::Checkbox(label: "Track", v: &enable_track);
4877 ImGui::PushItemWidth(item_width: 100);
4878 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");
4879
4880 bool scroll_to_off = ImGui::Button(label: "Scroll Offset");
4881 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");
4882
4883 bool scroll_to_pos = ImGui::Button(label: "Scroll To Pos");
4884 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");
4885 ImGui::PopItemWidth();
4886
4887 if (scroll_to_off || scroll_to_pos)
4888 enable_track = false;
4889
4890 ImGuiStyle& style = ImGui::GetStyle();
4891 float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;
4892 if (child_w < 1.0f)
4893 child_w = 1.0f;
4894 ImGui::PushID(str_id: "##VerticalScrolling");
4895 for (int i = 0; i < 5; i++)
4896 {
4897 if (i > 0) ImGui::SameLine();
4898 ImGui::BeginGroup();
4899 const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" };
4900 ImGui::TextUnformatted(text: names[i]);
4901
4902 const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
4903 const ImGuiID child_id = ImGui::GetID(ptr_id: (void*)(intptr_t)i);
4904 const bool child_is_visible = ImGui::BeginChild(id: child_id, size: ImVec2(child_w, 200.0f), child_flags: ImGuiChildFlags_Borders, window_flags: child_flags);
4905 if (ImGui::BeginMenuBar())
4906 {
4907 ImGui::TextUnformatted(text: "abc");
4908 ImGui::EndMenuBar();
4909 }
4910 if (scroll_to_off)
4911 ImGui::SetScrollY(scroll_to_off_px);
4912 if (scroll_to_pos)
4913 ImGui::SetScrollFromPosY(local_y: ImGui::GetCursorStartPos().y + scroll_to_pos_px, center_y_ratio: i * 0.25f);
4914 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4915 {
4916 for (int item = 0; item < 100; item++)
4917 {
4918 if (enable_track && item == track_item)
4919 {
4920 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Item %d", item);
4921 ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom
4922 }
4923 else
4924 {
4925 ImGui::Text(fmt: "Item %d", item);
4926 }
4927 }
4928 }
4929 float scroll_y = ImGui::GetScrollY();
4930 float scroll_max_y = ImGui::GetScrollMaxY();
4931 ImGui::EndChild();
4932 ImGui::Text(fmt: "%.0f/%.0f", scroll_y, scroll_max_y);
4933 ImGui::EndGroup();
4934 }
4935 ImGui::PopID();
4936
4937 // Horizontal scroll functions
4938 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal");
4939 ImGui::Spacing();
4940 HelpMarker(
4941 desc: "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
4942 "Because the clipping rectangle of most window hides half worth of WindowPadding on the "
4943 "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the "
4944 "equivalent SetScrollFromPosY(+1) wouldn't.");
4945 ImGui::PushID(str_id: "##HorizontalScrolling");
4946 for (int i = 0; i < 5; i++)
4947 {
4948 float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
4949 ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
4950 ImGuiID child_id = ImGui::GetID(ptr_id: (void*)(intptr_t)i);
4951 bool child_is_visible = ImGui::BeginChild(id: child_id, size: ImVec2(-100, child_height), child_flags: ImGuiChildFlags_Borders, window_flags: child_flags);
4952 if (scroll_to_off)
4953 ImGui::SetScrollX(scroll_to_off_px);
4954 if (scroll_to_pos)
4955 ImGui::SetScrollFromPosX(local_x: ImGui::GetCursorStartPos().x + scroll_to_pos_px, center_x_ratio: i * 0.25f);
4956 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4957 {
4958 for (int item = 0; item < 100; item++)
4959 {
4960 if (item > 0)
4961 ImGui::SameLine();
4962 if (enable_track && item == track_item)
4963 {
4964 ImGui::TextColored(col: ImVec4(1, 1, 0, 1), fmt: "Item %d", item);
4965 ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right
4966 }
4967 else
4968 {
4969 ImGui::Text(fmt: "Item %d", item);
4970 }
4971 }
4972 }
4973 float scroll_x = ImGui::GetScrollX();
4974 float scroll_max_x = ImGui::GetScrollMaxX();
4975 ImGui::EndChild();
4976 ImGui::SameLine();
4977 const char* names[] = { "Left", "25%", "Center", "75%", "Right" };
4978 ImGui::Text(fmt: "%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x);
4979 ImGui::Spacing();
4980 }
4981 ImGui::PopID();
4982
4983 // Miscellaneous Horizontal Scrolling Demo
4984 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)");
4985 HelpMarker(
4986 desc: "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n"
4987 "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().");
4988 static int lines = 7;
4989 ImGui::SliderInt(label: "Lines", v: &lines, v_min: 1, v_max: 15);
4990 ImGui::PushStyleVar(idx: ImGuiStyleVar_FrameRounding, val: 3.0f);
4991 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(2.0f, 1.0f));
4992 ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
4993 ImGui::BeginChild(str_id: "scrolling", size: scrolling_child_size, child_flags: ImGuiChildFlags_Borders, window_flags: ImGuiWindowFlags_HorizontalScrollbar);
4994 for (int line = 0; line < lines; line++)
4995 {
4996 // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
4997 // If you want to create your own time line for a real application you may be better off manipulating
4998 // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
4999 // yourself. You may also want to use the lower-level ImDrawList API.
5000 int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);
5001 for (int n = 0; n < num_buttons; n++)
5002 {
5003 if (n > 0) ImGui::SameLine();
5004 ImGui::PushID(int_id: n + line * 1000);
5005 char num_buf[16];
5006 sprintf(s: num_buf, format: "%d", n);
5007 const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf;
5008 float hue = n * 0.05f;
5009 ImGui::PushStyleColor(idx: ImGuiCol_Button, col: (ImVec4)ImColor::HSV(h: hue, s: 0.6f, v: 0.6f));
5010 ImGui::PushStyleColor(idx: ImGuiCol_ButtonHovered, col: (ImVec4)ImColor::HSV(h: hue, s: 0.7f, v: 0.7f));
5011 ImGui::PushStyleColor(idx: ImGuiCol_ButtonActive, col: (ImVec4)ImColor::HSV(h: hue, s: 0.8f, v: 0.8f));
5012 ImGui::Button(label, size: ImVec2(40.0f + sinf(x: (float)(line + n)) * 20.0f, 0.0f));
5013 ImGui::PopStyleColor(count: 3);
5014 ImGui::PopID();
5015 }
5016 }
5017 float scroll_x = ImGui::GetScrollX();
5018 float scroll_max_x = ImGui::GetScrollMaxX();
5019 ImGui::EndChild();
5020 ImGui::PopStyleVar(count: 2);
5021 float scroll_x_delta = 0.0f;
5022 ImGui::SmallButton(label: "<<");
5023 if (ImGui::IsItemActive())
5024 scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;
5025 ImGui::SameLine();
5026 ImGui::Text(fmt: "Scroll from code"); ImGui::SameLine();
5027 ImGui::SmallButton(label: ">>");
5028 if (ImGui::IsItemActive())
5029 scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;
5030 ImGui::SameLine();
5031 ImGui::Text(fmt: "%.0f/%.0f", scroll_x, scroll_max_x);
5032 if (scroll_x_delta != 0.0f)
5033 {
5034 // Demonstrate a trick: you can use Begin to set yourself in the context of another window
5035 // (here we are already out of your child window)
5036 ImGui::BeginChild(str_id: "scrolling");
5037 ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
5038 ImGui::EndChild();
5039 }
5040 ImGui::Spacing();
5041
5042 static bool show_horizontal_contents_size_demo_window = false;
5043 ImGui::Checkbox(label: "Show Horizontal contents size demo window", v: &show_horizontal_contents_size_demo_window);
5044
5045 if (show_horizontal_contents_size_demo_window)
5046 {
5047 static bool show_h_scrollbar = true;
5048 static bool show_button = true;
5049 static bool show_tree_nodes = true;
5050 static bool show_text_wrapped = false;
5051 static bool show_columns = true;
5052 static bool show_tab_bar = true;
5053 static bool show_child = false;
5054 static bool explicit_content_size = false;
5055 static float contents_size_x = 300.0f;
5056 if (explicit_content_size)
5057 ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));
5058 ImGui::Begin(name: "Horizontal contents size demo window", p_open: &show_horizontal_contents_size_demo_window, flags: show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);
5059 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window");
5060 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(2, 0));
5061 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(2, 0));
5062 HelpMarker(
5063 desc: "Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n"
5064 "Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles.");
5065 ImGui::Checkbox(label: "H-scrollbar", v: &show_h_scrollbar);
5066 ImGui::Checkbox(label: "Button", v: &show_button); // Will grow contents size (unless explicitly overwritten)
5067 ImGui::Checkbox(label: "Tree nodes", v: &show_tree_nodes); // Will grow contents size and display highlight over full width
5068 ImGui::Checkbox(label: "Text wrapped", v: &show_text_wrapped);// Will grow and use contents size
5069 ImGui::Checkbox(label: "Columns", v: &show_columns); // Will use contents size
5070 ImGui::Checkbox(label: "Tab bar", v: &show_tab_bar); // Will use contents size
5071 ImGui::Checkbox(label: "Child", v: &show_child); // Will grow and use contents size
5072 ImGui::Checkbox(label: "Explicit content size", v: &explicit_content_size);
5073 ImGui::Text(fmt: "Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY());
5074 if (explicit_content_size)
5075 {
5076 ImGui::SameLine();
5077 ImGui::SetNextItemWidth(100);
5078 ImGui::DragFloat(label: "##csx", v: &contents_size_x);
5079 ImVec2 p = ImGui::GetCursorScreenPos();
5080 ImGui::GetWindowDrawList()->AddRectFilled(p_min: p, p_max: ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);
5081 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);
5082 ImGui::Dummy(size: ImVec2(0, 10));
5083 }
5084 ImGui::PopStyleVar(count: 2);
5085 ImGui::Separator();
5086 if (show_button)
5087 {
5088 ImGui::Button(label: "this is a 300-wide button", size: ImVec2(300, 0));
5089 }
5090 if (show_tree_nodes)
5091 {
5092 bool open = true;
5093 if (ImGui::TreeNode(label: "this is a tree node"))
5094 {
5095 if (ImGui::TreeNode(label: "another one of those tree node..."))
5096 {
5097 ImGui::Text(fmt: "Some tree contents");
5098 ImGui::TreePop();
5099 }
5100 ImGui::TreePop();
5101 }
5102 ImGui::CollapsingHeader(label: "CollapsingHeader", p_visible: &open);
5103 }
5104 if (show_text_wrapped)
5105 {
5106 ImGui::TextWrapped(fmt: "This text should automatically wrap on the edge of the work rectangle.");
5107 }
5108 if (show_columns)
5109 {
5110 ImGui::Text(fmt: "Tables:");
5111 if (ImGui::BeginTable(str_id: "table", columns: 4, flags: ImGuiTableFlags_Borders))
5112 {
5113 for (int n = 0; n < 4; n++)
5114 {
5115 ImGui::TableNextColumn();
5116 ImGui::Text(fmt: "Width %.2f", ImGui::GetContentRegionAvail().x);
5117 }
5118 ImGui::EndTable();
5119 }
5120 ImGui::Text(fmt: "Columns:");
5121 ImGui::Columns(count: 4);
5122 for (int n = 0; n < 4; n++)
5123 {
5124 ImGui::Text(fmt: "Width %.2f", ImGui::GetColumnWidth());
5125 ImGui::NextColumn();
5126 }
5127 ImGui::Columns(count: 1);
5128 }
5129 if (show_tab_bar && ImGui::BeginTabBar(str_id: "Hello"))
5130 {
5131 if (ImGui::BeginTabItem(label: "OneOneOne")) { ImGui::EndTabItem(); }
5132 if (ImGui::BeginTabItem(label: "TwoTwoTwo")) { ImGui::EndTabItem(); }
5133 if (ImGui::BeginTabItem(label: "ThreeThreeThree")) { ImGui::EndTabItem(); }
5134 if (ImGui::BeginTabItem(label: "FourFourFour")) { ImGui::EndTabItem(); }
5135 ImGui::EndTabBar();
5136 }
5137 if (show_child)
5138 {
5139 ImGui::BeginChild(str_id: "child", size: ImVec2(0, 0), child_flags: ImGuiChildFlags_Borders);
5140 ImGui::EndChild();
5141 }
5142 ImGui::End();
5143 }
5144
5145 ImGui::TreePop();
5146 }
5147
5148 IMGUI_DEMO_MARKER("Layout/Text Clipping");
5149 if (ImGui::TreeNode(label: "Text Clipping"))
5150 {
5151 static ImVec2 size(100.0f, 100.0f);
5152 static ImVec2 offset(30.0f, 30.0f);
5153 ImGui::DragFloat2(label: "size", v: (float*)&size, v_speed: 0.5f, v_min: 1.0f, v_max: 200.0f, format: "%.0f");
5154 ImGui::TextWrapped(fmt: "(Click and drag to scroll)");
5155
5156 HelpMarker(
5157 desc: "(Left) Using ImGui::PushClipRect():\n"
5158 "Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
5159 "(use this if you want your clipping rectangle to affect interactions)\n\n"
5160 "(Center) Using ImDrawList::PushClipRect():\n"
5161 "Will alter ImDrawList rendering only.\n"
5162 "(use this as a shortcut if you are only using ImDrawList calls)\n\n"
5163 "(Right) Using ImDrawList::AddText() with a fine ClipRect:\n"
5164 "Will alter only this specific ImDrawList::AddText() rendering.\n"
5165 "This is often used internally to avoid altering the clipping rectangle and minimize draw calls.");
5166
5167 for (int n = 0; n < 3; n++)
5168 {
5169 if (n > 0)
5170 ImGui::SameLine();
5171
5172 ImGui::PushID(int_id: n);
5173 ImGui::InvisibleButton(str_id: "##canvas", size);
5174 if (ImGui::IsItemActive() && ImGui::IsMouseDragging(button: ImGuiMouseButton_Left))
5175 {
5176 offset.x += ImGui::GetIO().MouseDelta.x;
5177 offset.y += ImGui::GetIO().MouseDelta.y;
5178 }
5179 ImGui::PopID();
5180 if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.
5181 continue;
5182
5183 const ImVec2 p0 = ImGui::GetItemRectMin();
5184 const ImVec2 p1 = ImGui::GetItemRectMax();
5185 const char* text_str = "Line 1 hello\nLine 2 clip me!";
5186 const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
5187 ImDrawList* draw_list = ImGui::GetWindowDrawList();
5188 switch (n)
5189 {
5190 case 0:
5191 ImGui::PushClipRect(clip_rect_min: p0, clip_rect_max: p1, intersect_with_current_clip_rect: true);
5192 draw_list->AddRectFilled(p_min: p0, p_max: p1, IM_COL32(90, 90, 120, 255));
5193 draw_list->AddText(pos: text_pos, IM_COL32_WHITE, text_begin: text_str);
5194 ImGui::PopClipRect();
5195 break;
5196 case 1:
5197 draw_list->PushClipRect(clip_rect_min: p0, clip_rect_max: p1, intersect_with_current_clip_rect: true);
5198 draw_list->AddRectFilled(p_min: p0, p_max: p1, IM_COL32(90, 90, 120, 255));
5199 draw_list->AddText(pos: text_pos, IM_COL32_WHITE, text_begin: text_str);
5200 draw_list->PopClipRect();
5201 break;
5202 case 2:
5203 ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
5204 draw_list->AddRectFilled(p_min: p0, p_max: p1, IM_COL32(90, 90, 120, 255));
5205 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);
5206 break;
5207 }
5208 }
5209
5210 ImGui::TreePop();
5211 }
5212
5213 IMGUI_DEMO_MARKER("Layout/Overlap Mode");
5214 if (ImGui::TreeNode(label: "Overlap Mode"))
5215 {
5216 static bool enable_allow_overlap = true;
5217
5218 HelpMarker(
5219 desc: "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n"
5220 "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. "
5221 "Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.");
5222 ImGui::Checkbox(label: "Enable AllowOverlap", v: &enable_allow_overlap);
5223
5224 ImVec2 button1_pos = ImGui::GetCursorScreenPos();
5225 ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f);
5226 if (enable_allow_overlap)
5227 ImGui::SetNextItemAllowOverlap();
5228 ImGui::Button(label: "Button 1", size: ImVec2(80, 80));
5229 ImGui::SetCursorScreenPos(button2_pos);
5230 ImGui::Button(label: "Button 2", size: ImVec2(80, 80));
5231
5232 // This is typically used with width-spanning items.
5233 // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut
5234 // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)
5235 if (enable_allow_overlap)
5236 ImGui::SetNextItemAllowOverlap();
5237 ImGui::Selectable(label: "Some Selectable", selected: false);
5238 ImGui::SameLine();
5239 ImGui::SmallButton(label: "++");
5240
5241 ImGui::TreePop();
5242 }
5243}
5244
5245//-----------------------------------------------------------------------------
5246// [SECTION] DemoWindowPopups()
5247//-----------------------------------------------------------------------------
5248
5249static void DemoWindowPopups()
5250{
5251 IMGUI_DEMO_MARKER("Popups");
5252 if (!ImGui::CollapsingHeader(label: "Popups & Modal windows"))
5253 return;
5254
5255 // The properties of popups windows are:
5256 // - They block normal mouse hovering detection outside them. (*)
5257 // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
5258 // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
5259 // we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
5260 // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even
5261 // when normally blocked by a popup.
5262 // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
5263 // popups at any time.
5264
5265 // Typical use for regular windows:
5266 // 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();
5267 // Typical use for popups:
5268 // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); }
5269
5270 // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
5271 // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
5272
5273 IMGUI_DEMO_MARKER("Popups/Popups");
5274 if (ImGui::TreeNode(label: "Popups"))
5275 {
5276 ImGui::TextWrapped(
5277 fmt: "When a popup is active, it inhibits interacting with windows that are behind the popup. "
5278 "Clicking outside the popup closes it.");
5279
5280 static int selected_fish = -1;
5281 const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" };
5282 static bool toggles[] = { true, false, false, false, false };
5283
5284 // Simple selection popup (if you want to show the current selection inside the Button itself,
5285 // you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
5286 if (ImGui::Button(label: "Select.."))
5287 ImGui::OpenPopup(str_id: "my_select_popup");
5288 ImGui::SameLine();
5289 ImGui::TextUnformatted(text: selected_fish == -1 ? "<None>" : names[selected_fish]);
5290 if (ImGui::BeginPopup(str_id: "my_select_popup"))
5291 {
5292 ImGui::SeparatorText(label: "Aquarium");
5293 for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5294 if (ImGui::Selectable(label: names[i]))
5295 selected_fish = i;
5296 ImGui::EndPopup();
5297 }
5298
5299 // Showing a menu with toggles
5300 if (ImGui::Button(label: "Toggle.."))
5301 ImGui::OpenPopup(str_id: "my_toggle_popup");
5302 if (ImGui::BeginPopup(str_id: "my_toggle_popup"))
5303 {
5304 for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5305 ImGui::MenuItem(label: names[i], shortcut: "", p_selected: &toggles[i]);
5306 if (ImGui::BeginMenu(label: "Sub-menu"))
5307 {
5308 ImGui::MenuItem(label: "Click me");
5309 ImGui::EndMenu();
5310 }
5311
5312 ImGui::Separator();
5313 ImGui::Text(fmt: "Tooltip here");
5314 ImGui::SetItemTooltip("I am a tooltip over a popup");
5315
5316 if (ImGui::Button(label: "Stacked Popup"))
5317 ImGui::OpenPopup(str_id: "another popup");
5318 if (ImGui::BeginPopup(str_id: "another popup"))
5319 {
5320 for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5321 ImGui::MenuItem(label: names[i], shortcut: "", p_selected: &toggles[i]);
5322 if (ImGui::BeginMenu(label: "Sub-menu"))
5323 {
5324 ImGui::MenuItem(label: "Click me");
5325 if (ImGui::Button(label: "Stacked Popup"))
5326 ImGui::OpenPopup(str_id: "another popup");
5327 if (ImGui::BeginPopup(str_id: "another popup"))
5328 {
5329 ImGui::Text(fmt: "I am the last one here.");
5330 ImGui::EndPopup();
5331 }
5332 ImGui::EndMenu();
5333 }
5334 ImGui::EndPopup();
5335 }
5336 ImGui::EndPopup();
5337 }
5338
5339 // Call the more complete ShowExampleMenuFile which we use in various places of this demo
5340 if (ImGui::Button(label: "With a menu.."))
5341 ImGui::OpenPopup(str_id: "my_file_popup");
5342 if (ImGui::BeginPopup(str_id: "my_file_popup", flags: ImGuiWindowFlags_MenuBar))
5343 {
5344 if (ImGui::BeginMenuBar())
5345 {
5346 if (ImGui::BeginMenu(label: "File"))
5347 {
5348 ShowExampleMenuFile();
5349 ImGui::EndMenu();
5350 }
5351 if (ImGui::BeginMenu(label: "Edit"))
5352 {
5353 ImGui::MenuItem(label: "Dummy");
5354 ImGui::EndMenu();
5355 }
5356 ImGui::EndMenuBar();
5357 }
5358 ImGui::Text(fmt: "Hello from popup!");
5359 ImGui::Button(label: "This is a dummy button..");
5360 ImGui::EndPopup();
5361 }
5362
5363 ImGui::TreePop();
5364 }
5365
5366 IMGUI_DEMO_MARKER("Popups/Context menus");
5367 if (ImGui::TreeNode(label: "Context menus"))
5368 {
5369 HelpMarker(desc: "\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.");
5370
5371 // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
5372 // if (id == 0)
5373 // id = GetItemID(); // Use last item id
5374 // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
5375 // OpenPopup(id);
5376 // return BeginPopup(id);
5377 // For advanced uses you may want to replicate and customize this code.
5378 // See more details in BeginPopupContextItem().
5379
5380 // Example 1
5381 // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
5382 // and BeginPopupContextItem() will use the last item ID as the popup ID.
5383 {
5384 const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" };
5385 static int selected = -1;
5386 for (int n = 0; n < 5; n++)
5387 {
5388 if (ImGui::Selectable(label: names[n], selected: selected == n))
5389 selected = n;
5390 if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
5391 {
5392 selected = n;
5393 ImGui::Text(fmt: "This a popup for \"%s\"!", names[n]);
5394 if (ImGui::Button(label: "Close"))
5395 ImGui::CloseCurrentPopup();
5396 ImGui::EndPopup();
5397 }
5398 ImGui::SetItemTooltip("Right-click to open popup");
5399 }
5400 }
5401
5402 // Example 2
5403 // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().
5404 // Using an explicit identifier is also convenient if you want to activate the popups from different locations.
5405 {
5406 HelpMarker(desc: "Text() elements don't have stable identifiers so we need to provide one.");
5407 static float value = 0.5f;
5408 ImGui::Text(fmt: "Value = %.3f <-- (1) right-click this text", value);
5409 if (ImGui::BeginPopupContextItem(str_id: "my popup"))
5410 {
5411 if (ImGui::Selectable(label: "Set to zero")) value = 0.0f;
5412 if (ImGui::Selectable(label: "Set to PI")) value = 3.1415f;
5413 ImGui::SetNextItemWidth(-FLT_MIN);
5414 ImGui::DragFloat(label: "##Value", v: &value, v_speed: 0.1f, v_min: 0.0f, v_max: 0.0f);
5415 ImGui::EndPopup();
5416 }
5417
5418 // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
5419 // Here we make it that right-clicking this other text element opens the same popup as above.
5420 // The popup itself will be submitted by the code above.
5421 ImGui::Text(fmt: "(2) Or right-click this text");
5422 ImGui::OpenPopupOnItemClick(str_id: "my popup", popup_flags: ImGuiPopupFlags_MouseButtonRight);
5423
5424 // Back to square one: manually open the same popup.
5425 if (ImGui::Button(label: "(3) Or click this button"))
5426 ImGui::OpenPopup(str_id: "my popup");
5427 }
5428
5429 // Example 3
5430 // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),
5431 // we need to make sure your item identifier is stable.
5432 // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).
5433 {
5434 HelpMarker(desc: "Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.");
5435 static char name[32] = "Label1";
5436 char buf[64];
5437 sprintf(s: buf, format: "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
5438 ImGui::Button(label: buf);
5439 if (ImGui::BeginPopupContextItem())
5440 {
5441 ImGui::Text(fmt: "Edit name:");
5442 ImGui::InputText(label: "##edit", buf: name, IM_ARRAYSIZE(name));
5443 if (ImGui::Button(label: "Close"))
5444 ImGui::CloseCurrentPopup();
5445 ImGui::EndPopup();
5446 }
5447 ImGui::SameLine(); ImGui::Text(fmt: "(<-- right-click here)");
5448 }
5449
5450 ImGui::TreePop();
5451 }
5452
5453 IMGUI_DEMO_MARKER("Popups/Modals");
5454 if (ImGui::TreeNode(label: "Modals"))
5455 {
5456 ImGui::TextWrapped(fmt: "Modal windows are like popups but the user cannot close them by clicking outside.");
5457
5458 if (ImGui::Button(label: "Delete.."))
5459 ImGui::OpenPopup(str_id: "Delete?");
5460
5461 // Always center this window when appearing
5462 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
5463 ImGui::SetNextWindowPos(pos: center, cond: ImGuiCond_Appearing, pivot: ImVec2(0.5f, 0.5f));
5464
5465 if (ImGui::BeginPopupModal(name: "Delete?", NULL, flags: ImGuiWindowFlags_AlwaysAutoResize))
5466 {
5467 ImGui::Text(fmt: "All those beautiful files will be deleted.\nThis operation cannot be undone!");
5468 ImGui::Separator();
5469
5470 //static int unused_i = 0;
5471 //ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0");
5472
5473 static bool dont_ask_me_next_time = false;
5474 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(0, 0));
5475 ImGui::Checkbox(label: "Don't ask me next time", v: &dont_ask_me_next_time);
5476 ImGui::PopStyleVar();
5477
5478 if (ImGui::Button(label: "OK", size: ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5479 ImGui::SetItemDefaultFocus();
5480 ImGui::SameLine();
5481 if (ImGui::Button(label: "Cancel", size: ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5482 ImGui::EndPopup();
5483 }
5484
5485 if (ImGui::Button(label: "Stacked modals.."))
5486 ImGui::OpenPopup(str_id: "Stacked 1");
5487 if (ImGui::BeginPopupModal(name: "Stacked 1", NULL, flags: ImGuiWindowFlags_MenuBar))
5488 {
5489 if (ImGui::BeginMenuBar())
5490 {
5491 if (ImGui::BeginMenu(label: "File"))
5492 {
5493 if (ImGui::MenuItem(label: "Some menu item")) {}
5494 ImGui::EndMenu();
5495 }
5496 ImGui::EndMenuBar();
5497 }
5498 ImGui::Text(fmt: "Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.");
5499
5500 // Testing behavior of widgets stacking their own regular popups over the modal.
5501 static int item = 1;
5502 static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
5503 ImGui::Combo(label: "Combo", current_item: &item, items_separated_by_zeros: "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
5504 ImGui::ColorEdit4(label: "Color", col: color);
5505
5506 if (ImGui::Button(label: "Add another modal.."))
5507 ImGui::OpenPopup(str_id: "Stacked 2");
5508
5509 // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
5510 // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
5511 // of the bool actually doesn't matter here.
5512 bool unused_open = true;
5513 if (ImGui::BeginPopupModal(name: "Stacked 2", p_open: &unused_open))
5514 {
5515 ImGui::Text(fmt: "Hello from Stacked The Second!");
5516 ImGui::ColorEdit4(label: "Color", col: color); // Allow opening another nested popup
5517 if (ImGui::Button(label: "Close"))
5518 ImGui::CloseCurrentPopup();
5519 ImGui::EndPopup();
5520 }
5521
5522 if (ImGui::Button(label: "Close"))
5523 ImGui::CloseCurrentPopup();
5524 ImGui::EndPopup();
5525 }
5526
5527 ImGui::TreePop();
5528 }
5529
5530 IMGUI_DEMO_MARKER("Popups/Menus inside a regular window");
5531 if (ImGui::TreeNode(label: "Menus inside a regular window"))
5532 {
5533 ImGui::TextWrapped(fmt: "Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
5534 ImGui::Separator();
5535
5536 ImGui::MenuItem(label: "Menu item", shortcut: "CTRL+M");
5537 if (ImGui::BeginMenu(label: "Menu inside a regular window"))
5538 {
5539 ShowExampleMenuFile();
5540 ImGui::EndMenu();
5541 }
5542 ImGui::Separator();
5543 ImGui::TreePop();
5544 }
5545}
5546
5547// Dummy data structure that we use for the Table demo.
5548// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)
5549namespace
5550{
5551// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.
5552// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.
5553// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)
5554// If you don't use sorting, you will generally never care about giving column an ID!
5555enum MyItemColumnID
5556{
5557 MyItemColumnID_ID,
5558 MyItemColumnID_Name,
5559 MyItemColumnID_Action,
5560 MyItemColumnID_Quantity,
5561 MyItemColumnID_Description
5562};
5563
5564struct MyItem
5565{
5566 int ID;
5567 const char* Name;
5568 int Quantity;
5569
5570 // We have a problem which is affecting _only this demo_ and should not affect your code:
5571 // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
5572 // however qsort doesn't allow passing user data to comparing function.
5573 // As a workaround, we are storing the sort specs in a static/global for the comparing function to access.
5574 // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.
5575 // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called
5576 // very often by the sorting algorithm it would be a little wasteful.
5577 static const ImGuiTableSortSpecs* s_current_sort_specs;
5578
5579 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count)
5580 {
5581 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
5582 if (items_count > 1)
5583 qsort(base: items, nmemb: (size_t)items_count, size: sizeof(items[0]), compar: MyItem::CompareWithSortSpecs);
5584 s_current_sort_specs = NULL;
5585 }
5586
5587 // Compare function to be used by qsort()
5588 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
5589 {
5590 const MyItem* a = (const MyItem*)lhs;
5591 const MyItem* b = (const MyItem*)rhs;
5592 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
5593 {
5594 // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()
5595 // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!
5596 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
5597 int delta = 0;
5598 switch (sort_spec->ColumnUserID)
5599 {
5600 case MyItemColumnID_ID: delta = (a->ID - b->ID); break;
5601 case MyItemColumnID_Name: delta = (strcmp(s1: a->Name, s2: b->Name)); break;
5602 case MyItemColumnID_Quantity: delta = (a->Quantity - b->Quantity); break;
5603 case MyItemColumnID_Description: delta = (strcmp(s1: a->Name, s2: b->Name)); break;
5604 default: IM_ASSERT(0); break;
5605 }
5606 if (delta > 0)
5607 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
5608 if (delta < 0)
5609 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
5610 }
5611
5612 // qsort() is instable so always return a way to differentiate items.
5613 // Your own compare function may want to avoid fallback on implicit sort specs.
5614 // e.g. a Name compare if it wasn't already part of the sort specs.
5615 return (a->ID - b->ID);
5616 }
5617};
5618const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;
5619}
5620
5621// Make the UI compact because there are so many fields
5622static void PushStyleCompact()
5623{
5624 ImGuiStyle& style = ImGui::GetStyle();
5625 ImGui::PushStyleVarY(idx: ImGuiStyleVar_FramePadding, val_y: (float)(int)(style.FramePadding.y * 0.60f));
5626 ImGui::PushStyleVarY(idx: ImGuiStyleVar_ItemSpacing, val_y: (float)(int)(style.ItemSpacing.y * 0.60f));
5627}
5628
5629static void PopStyleCompact()
5630{
5631 ImGui::PopStyleVar(count: 2);
5632}
5633
5634// Show a combo box with a choice of sizing policies
5635static void EditTableSizingFlags(ImGuiTableFlags* p_flags)
5636{
5637 struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };
5638 static const EnumDesc policies[] =
5639 {
5640 { .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." },
5641 { .Value: ImGuiTableFlags_SizingFixedFit, .Name: "ImGuiTableFlags_SizingFixedFit", .Tooltip: "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." },
5642 { .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." },
5643 { .Value: ImGuiTableFlags_SizingStretchProp, .Name: "ImGuiTableFlags_SizingStretchProp", .Tooltip: "Columns default to _WidthStretch with weights proportional to their widths." },
5644 { .Value: ImGuiTableFlags_SizingStretchSame, .Name: "ImGuiTableFlags_SizingStretchSame", .Tooltip: "Columns default to _WidthStretch with same weights." }
5645 };
5646 int idx;
5647 for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++)
5648 if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))
5649 break;
5650 const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen(s: "ImGuiTableFlags") : 0) : "";
5651 if (ImGui::BeginCombo(label: "Sizing Policy", preview_value: preview_text))
5652 {
5653 for (int n = 0; n < IM_ARRAYSIZE(policies); n++)
5654 if (ImGui::Selectable(label: policies[n].Name, selected: idx == n))
5655 *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;
5656 ImGui::EndCombo();
5657 }
5658 ImGui::SameLine();
5659 ImGui::TextDisabled(fmt: "(?)");
5660 if (ImGui::BeginItemTooltip())
5661 {
5662 ImGui::PushTextWrapPos(wrap_local_pos_x: ImGui::GetFontSize() * 50.0f);
5663 for (int m = 0; m < IM_ARRAYSIZE(policies); m++)
5664 {
5665 ImGui::Separator();
5666 ImGui::Text(fmt: "%s:", policies[m].Name);
5667 ImGui::Separator();
5668 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);
5669 ImGui::TextUnformatted(text: policies[m].Tooltip);
5670 }
5671 ImGui::PopTextWrapPos();
5672 ImGui::EndTooltip();
5673 }
5674}
5675
5676static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)
5677{
5678 ImGui::CheckboxFlags(label: "_Disabled", flags: p_flags, flags_value: ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker(desc: "Master disable flag (also hide from context menu)");
5679 ImGui::CheckboxFlags(label: "_DefaultHide", flags: p_flags, flags_value: ImGuiTableColumnFlags_DefaultHide);
5680 ImGui::CheckboxFlags(label: "_DefaultSort", flags: p_flags, flags_value: ImGuiTableColumnFlags_DefaultSort);
5681 if (ImGui::CheckboxFlags(label: "_WidthStretch", flags: p_flags, flags_value: ImGuiTableColumnFlags_WidthStretch))
5682 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);
5683 if (ImGui::CheckboxFlags(label: "_WidthFixed", flags: p_flags, flags_value: ImGuiTableColumnFlags_WidthFixed))
5684 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);
5685 ImGui::CheckboxFlags(label: "_NoResize", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoResize);
5686 ImGui::CheckboxFlags(label: "_NoReorder", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoReorder);
5687 ImGui::CheckboxFlags(label: "_NoHide", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoHide);
5688 ImGui::CheckboxFlags(label: "_NoClip", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoClip);
5689 ImGui::CheckboxFlags(label: "_NoSort", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoSort);
5690 ImGui::CheckboxFlags(label: "_NoSortAscending", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoSortAscending);
5691 ImGui::CheckboxFlags(label: "_NoSortDescending", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoSortDescending);
5692 ImGui::CheckboxFlags(label: "_NoHeaderLabel", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoHeaderLabel);
5693 ImGui::CheckboxFlags(label: "_NoHeaderWidth", flags: p_flags, flags_value: ImGuiTableColumnFlags_NoHeaderWidth);
5694 ImGui::CheckboxFlags(label: "_PreferSortAscending", flags: p_flags, flags_value: ImGuiTableColumnFlags_PreferSortAscending);
5695 ImGui::CheckboxFlags(label: "_PreferSortDescending", flags: p_flags, flags_value: ImGuiTableColumnFlags_PreferSortDescending);
5696 ImGui::CheckboxFlags(label: "_IndentEnable", flags: p_flags, flags_value: ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker(desc: "Default for column 0");
5697 ImGui::CheckboxFlags(label: "_IndentDisable", flags: p_flags, flags_value: ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker(desc: "Default for column >0");
5698 ImGui::CheckboxFlags(label: "_AngledHeader", flags: p_flags, flags_value: ImGuiTableColumnFlags_AngledHeader);
5699}
5700
5701static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)
5702{
5703 ImGui::CheckboxFlags(label: "_IsEnabled", flags: &flags, flags_value: ImGuiTableColumnFlags_IsEnabled);
5704 ImGui::CheckboxFlags(label: "_IsVisible", flags: &flags, flags_value: ImGuiTableColumnFlags_IsVisible);
5705 ImGui::CheckboxFlags(label: "_IsSorted", flags: &flags, flags_value: ImGuiTableColumnFlags_IsSorted);
5706 ImGui::CheckboxFlags(label: "_IsHovered", flags: &flags, flags_value: ImGuiTableColumnFlags_IsHovered);
5707}
5708
5709//-----------------------------------------------------------------------------
5710// [SECTION] DemoWindowTables()
5711//-----------------------------------------------------------------------------
5712
5713static void DemoWindowTables()
5714{
5715 //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
5716 IMGUI_DEMO_MARKER("Tables");
5717 if (!ImGui::CollapsingHeader(label: "Tables & Columns"))
5718 return;
5719
5720 // Using those as a base value to create width/height that are factor of the size of our font
5721 const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(text: "A").x;
5722 const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
5723
5724 ImGui::PushID(str_id: "Tables");
5725
5726 int open_action = -1;
5727 if (ImGui::Button(label: "Expand all"))
5728 open_action = 1;
5729 ImGui::SameLine();
5730 if (ImGui::Button(label: "Collapse all"))
5731 open_action = 0;
5732 ImGui::SameLine();
5733
5734 // Options
5735 static bool disable_indent = false;
5736 ImGui::Checkbox(label: "Disable tree indentation", v: &disable_indent);
5737 ImGui::SameLine();
5738 HelpMarker(desc: "Disable the indenting of tree nodes so demo tables can use the full window width.");
5739 ImGui::Separator();
5740 if (disable_indent)
5741 ImGui::PushStyleVar(idx: ImGuiStyleVar_IndentSpacing, val: 0.0f);
5742
5743 // About Styling of tables
5744 // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.
5745 // There are however a few settings that a shared and part of the ImGuiStyle structure:
5746 // style.CellPadding // Padding within each cell
5747 // style.Colors[ImGuiCol_TableHeaderBg] // Table header background
5748 // style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders
5749 // style.Colors[ImGuiCol_TableBorderLight] // Table inner borders
5750 // style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even rows)
5751 // style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)
5752
5753 // Demos
5754 if (open_action != -1)
5755 ImGui::SetNextItemOpen(is_open: open_action != 0);
5756 IMGUI_DEMO_MARKER("Tables/Basic");
5757 if (ImGui::TreeNode(label: "Basic"))
5758 {
5759 // Here we will showcase three different ways to output a table.
5760 // They are very simple variations of a same thing!
5761
5762 // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
5763 // In many situations, this is the most flexible and easy to use pattern.
5764 HelpMarker(desc: "Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
5765 if (ImGui::BeginTable(str_id: "table1", columns: 3))
5766 {
5767 for (int row = 0; row < 4; row++)
5768 {
5769 ImGui::TableNextRow();
5770 for (int column = 0; column < 3; column++)
5771 {
5772 ImGui::TableSetColumnIndex(column_n: column);
5773 ImGui::Text(fmt: "Row %d Column %d", row, column);
5774 }
5775 }
5776 ImGui::EndTable();
5777 }
5778
5779 // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().
5780 // This is generally more convenient when you have code manually submitting the contents of each column.
5781 HelpMarker(desc: "Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.");
5782 if (ImGui::BeginTable(str_id: "table2", columns: 3))
5783 {
5784 for (int row = 0; row < 4; row++)
5785 {
5786 ImGui::TableNextRow();
5787 ImGui::TableNextColumn();
5788 ImGui::Text(fmt: "Row %d", row);
5789 ImGui::TableNextColumn();
5790 ImGui::Text(fmt: "Some contents");
5791 ImGui::TableNextColumn();
5792 ImGui::Text(fmt: "123.456");
5793 }
5794 ImGui::EndTable();
5795 }
5796
5797 // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),
5798 // as TableNextColumn() will automatically wrap around and create new rows as needed.
5799 // This is generally more convenient when your cells all contains the same type of data.
5800 HelpMarker(
5801 desc: "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains "
5802 "the same type of contents.\n This is also more similar to the old NextColumn() function of the "
5803 "Columns API, and provided to facilitate the Columns->Tables API transition.");
5804 if (ImGui::BeginTable(str_id: "table3", columns: 3))
5805 {
5806 for (int item = 0; item < 14; item++)
5807 {
5808 ImGui::TableNextColumn();
5809 ImGui::Text(fmt: "Item %d", item);
5810 }
5811 ImGui::EndTable();
5812 }
5813
5814 ImGui::TreePop();
5815 }
5816
5817 if (open_action != -1)
5818 ImGui::SetNextItemOpen(is_open: open_action != 0);
5819 IMGUI_DEMO_MARKER("Tables/Borders, background");
5820 if (ImGui::TreeNode(label: "Borders, background"))
5821 {
5822 // Expose a few Borders related flags interactively
5823 enum ContentsType { CT_Text, CT_FillButton };
5824 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
5825 static bool display_headers = false;
5826 static int contents_type = CT_Text;
5827
5828 PushStyleCompact();
5829 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags, flags_value: ImGuiTableFlags_RowBg);
5830 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Borders", flags: &flags, flags_value: ImGuiTableFlags_Borders);
5831 ImGui::SameLine(); HelpMarker(desc: "ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH");
5832 ImGui::Indent();
5833
5834 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersH", flags: &flags, flags_value: ImGuiTableFlags_BordersH);
5835 ImGui::Indent();
5836 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterH", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterH);
5837 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerH", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerH);
5838 ImGui::Unindent();
5839
5840 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags, flags_value: ImGuiTableFlags_BordersV);
5841 ImGui::Indent();
5842 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterV);
5843 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerV);
5844 ImGui::Unindent();
5845
5846 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuter", flags: &flags, flags_value: ImGuiTableFlags_BordersOuter);
5847 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInner", flags: &flags, flags_value: ImGuiTableFlags_BordersInner);
5848 ImGui::Unindent();
5849
5850 ImGui::AlignTextToFramePadding(); ImGui::Text(fmt: "Cell contents:");
5851 ImGui::SameLine(); ImGui::RadioButton(label: "Text", v: &contents_type, v_button: CT_Text);
5852 ImGui::SameLine(); ImGui::RadioButton(label: "FillButton", v: &contents_type, v_button: CT_FillButton);
5853 ImGui::Checkbox(label: "Display headers", v: &display_headers);
5854 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");
5855 PopStyleCompact();
5856
5857 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5858 {
5859 // Display headers so we can inspect their interaction with borders
5860 // (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)
5861 if (display_headers)
5862 {
5863 ImGui::TableSetupColumn(label: "One");
5864 ImGui::TableSetupColumn(label: "Two");
5865 ImGui::TableSetupColumn(label: "Three");
5866 ImGui::TableHeadersRow();
5867 }
5868
5869 for (int row = 0; row < 5; row++)
5870 {
5871 ImGui::TableNextRow();
5872 for (int column = 0; column < 3; column++)
5873 {
5874 ImGui::TableSetColumnIndex(column_n: column);
5875 char buf[32];
5876 sprintf(s: buf, format: "Hello %d,%d", column, row);
5877 if (contents_type == CT_Text)
5878 ImGui::TextUnformatted(text: buf);
5879 else if (contents_type == CT_FillButton)
5880 ImGui::Button(label: buf, size: ImVec2(-FLT_MIN, 0.0f));
5881 }
5882 }
5883 ImGui::EndTable();
5884 }
5885 ImGui::TreePop();
5886 }
5887
5888 if (open_action != -1)
5889 ImGui::SetNextItemOpen(is_open: open_action != 0);
5890 IMGUI_DEMO_MARKER("Tables/Resizable, stretch");
5891 if (ImGui::TreeNode(label: "Resizable, stretch"))
5892 {
5893 // By default, if we don't enable ScrollX the sizing policy for each column is "Stretch"
5894 // All columns maintain a sizing weight, and they will occupy all available width.
5895 static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5896 PushStyleCompact();
5897 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
5898 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags, flags_value: ImGuiTableFlags_BordersV);
5899 ImGui::SameLine(); HelpMarker(
5900 desc: "Using the _Resizable flag automatically enables the _BordersInnerV flag as well, "
5901 "this is why the resize borders are still showing when unchecking this.");
5902 PopStyleCompact();
5903
5904 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5905 {
5906 for (int row = 0; row < 5; row++)
5907 {
5908 ImGui::TableNextRow();
5909 for (int column = 0; column < 3; column++)
5910 {
5911 ImGui::TableSetColumnIndex(column_n: column);
5912 ImGui::Text(fmt: "Hello %d,%d", column, row);
5913 }
5914 }
5915 ImGui::EndTable();
5916 }
5917 ImGui::TreePop();
5918 }
5919
5920 if (open_action != -1)
5921 ImGui::SetNextItemOpen(is_open: open_action != 0);
5922 IMGUI_DEMO_MARKER("Tables/Resizable, fixed");
5923 if (ImGui::TreeNode(label: "Resizable, fixed"))
5924 {
5925 // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)
5926 // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small)
5927 // If there is not enough available width to fit all columns, they will however be resized down.
5928 // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings
5929 HelpMarker(
5930 desc: "Using _Resizable + _SizingFixedFit flags.\n"
5931 "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n"
5932 "Double-click a column border to auto-fit the column to its contents.");
5933 PushStyleCompact();
5934 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5935 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendX);
5936 PopStyleCompact();
5937
5938 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5939 {
5940 for (int row = 0; row < 5; row++)
5941 {
5942 ImGui::TableNextRow();
5943 for (int column = 0; column < 3; column++)
5944 {
5945 ImGui::TableSetColumnIndex(column_n: column);
5946 ImGui::Text(fmt: "Hello %d,%d", column, row);
5947 }
5948 }
5949 ImGui::EndTable();
5950 }
5951 ImGui::TreePop();
5952 }
5953
5954 if (open_action != -1)
5955 ImGui::SetNextItemOpen(is_open: open_action != 0);
5956 IMGUI_DEMO_MARKER("Tables/Resizable, mixed");
5957 if (ImGui::TreeNode(label: "Resizable, mixed"))
5958 {
5959 HelpMarker(
5960 desc: "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n"
5961 "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.");
5962 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
5963
5964 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
5965 {
5966 ImGui::TableSetupColumn(label: "AAA", flags: ImGuiTableColumnFlags_WidthFixed);
5967 ImGui::TableSetupColumn(label: "BBB", flags: ImGuiTableColumnFlags_WidthFixed);
5968 ImGui::TableSetupColumn(label: "CCC", flags: ImGuiTableColumnFlags_WidthStretch);
5969 ImGui::TableHeadersRow();
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 ImGui::Text(fmt: "%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row);
5977 }
5978 }
5979 ImGui::EndTable();
5980 }
5981 if (ImGui::BeginTable(str_id: "table2", columns: 6, flags))
5982 {
5983 ImGui::TableSetupColumn(label: "AAA", flags: ImGuiTableColumnFlags_WidthFixed);
5984 ImGui::TableSetupColumn(label: "BBB", flags: ImGuiTableColumnFlags_WidthFixed);
5985 ImGui::TableSetupColumn(label: "CCC", flags: ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);
5986 ImGui::TableSetupColumn(label: "DDD", flags: ImGuiTableColumnFlags_WidthStretch);
5987 ImGui::TableSetupColumn(label: "EEE", flags: ImGuiTableColumnFlags_WidthStretch);
5988 ImGui::TableSetupColumn(label: "FFF", flags: ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);
5989 ImGui::TableHeadersRow();
5990 for (int row = 0; row < 5; row++)
5991 {
5992 ImGui::TableNextRow();
5993 for (int column = 0; column < 6; column++)
5994 {
5995 ImGui::TableSetColumnIndex(column_n: column);
5996 ImGui::Text(fmt: "%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row);
5997 }
5998 }
5999 ImGui::EndTable();
6000 }
6001 ImGui::TreePop();
6002 }
6003
6004 if (open_action != -1)
6005 ImGui::SetNextItemOpen(is_open: open_action != 0);
6006 IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers");
6007 if (ImGui::TreeNode(label: "Reorderable, hideable, with headers"))
6008 {
6009 HelpMarker(
6010 desc: "Click and drag column headers to reorder columns.\n\n"
6011 "Right-click on a header to open a context menu.");
6012 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;
6013 PushStyleCompact();
6014 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
6015 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Reorderable", flags: &flags, flags_value: ImGuiTableFlags_Reorderable);
6016 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Hideable", flags: &flags, flags_value: ImGuiTableFlags_Hideable);
6017 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBody", flags: &flags, flags_value: ImGuiTableFlags_NoBordersInBody);
6018 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)");
6019 ImGui::CheckboxFlags(label: "ImGuiTableFlags_HighlightHoveredColumn", flags: &flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
6020 PopStyleCompact();
6021
6022 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags))
6023 {
6024 // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.
6025 // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)
6026 ImGui::TableSetupColumn(label: "One");
6027 ImGui::TableSetupColumn(label: "Two");
6028 ImGui::TableSetupColumn(label: "Three");
6029 ImGui::TableHeadersRow();
6030 for (int row = 0; row < 6; row++)
6031 {
6032 ImGui::TableNextRow();
6033 for (int column = 0; column < 3; column++)
6034 {
6035 ImGui::TableSetColumnIndex(column_n: column);
6036 ImGui::Text(fmt: "Hello %d,%d", column, row);
6037 }
6038 }
6039 ImGui::EndTable();
6040 }
6041
6042 // Use outer_size.x == 0.0f instead of default to make the table as tight as possible
6043 // (only valid when no scrolling and no stretch column)
6044 if (ImGui::BeginTable(str_id: "table2", columns: 3, flags: flags | ImGuiTableFlags_SizingFixedFit, outer_size: ImVec2(0.0f, 0.0f)))
6045 {
6046 ImGui::TableSetupColumn(label: "One");
6047 ImGui::TableSetupColumn(label: "Two");
6048 ImGui::TableSetupColumn(label: "Three");
6049 ImGui::TableHeadersRow();
6050 for (int row = 0; row < 6; row++)
6051 {
6052 ImGui::TableNextRow();
6053 for (int column = 0; column < 3; column++)
6054 {
6055 ImGui::TableSetColumnIndex(column_n: column);
6056 ImGui::Text(fmt: "Fixed %d,%d", column, row);
6057 }
6058 }
6059 ImGui::EndTable();
6060 }
6061 ImGui::TreePop();
6062 }
6063
6064 if (open_action != -1)
6065 ImGui::SetNextItemOpen(is_open: open_action != 0);
6066 IMGUI_DEMO_MARKER("Tables/Padding");
6067 if (ImGui::TreeNode(label: "Padding"))
6068 {
6069 // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.
6070 // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.
6071 HelpMarker(
6072 desc: "We often want outer padding activated when any using features which makes the edges of a column visible:\n"
6073 "e.g.:\n"
6074 "- BorderOuterV\n"
6075 "- any form of row selection\n"
6076 "Because of this, activating BorderOuterV sets the default to PadOuterX. "
6077 "Using PadOuterX or NoPadOuterX you can override the default.\n\n"
6078 "Actual padding values are using style.CellPadding.\n\n"
6079 "In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.");
6080
6081 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;
6082 PushStyleCompact();
6083 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PadOuterX", flags: &flags1, flags_value: ImGuiTableFlags_PadOuterX);
6084 ImGui::SameLine(); HelpMarker(desc: "Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)");
6085 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadOuterX", flags: &flags1, flags_value: ImGuiTableFlags_NoPadOuterX);
6086 ImGui::SameLine(); HelpMarker(desc: "Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)");
6087 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadInnerX", flags: &flags1, flags_value: ImGuiTableFlags_NoPadInnerX);
6088 ImGui::SameLine(); HelpMarker(desc: "Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)");
6089 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags1, flags_value: ImGuiTableFlags_BordersOuterV);
6090 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags1, flags_value: ImGuiTableFlags_BordersInnerV);
6091 static bool show_headers = false;
6092 ImGui::Checkbox(label: "show_headers", v: &show_headers);
6093 PopStyleCompact();
6094
6095 if (ImGui::BeginTable(str_id: "table_padding", columns: 3, flags: flags1))
6096 {
6097 if (show_headers)
6098 {
6099 ImGui::TableSetupColumn(label: "One");
6100 ImGui::TableSetupColumn(label: "Two");
6101 ImGui::TableSetupColumn(label: "Three");
6102 ImGui::TableHeadersRow();
6103 }
6104
6105 for (int row = 0; row < 5; row++)
6106 {
6107 ImGui::TableNextRow();
6108 for (int column = 0; column < 3; column++)
6109 {
6110 ImGui::TableSetColumnIndex(column_n: column);
6111 if (row == 0)
6112 {
6113 ImGui::Text(fmt: "Avail %.2f", ImGui::GetContentRegionAvail().x);
6114 }
6115 else
6116 {
6117 char buf[32];
6118 sprintf(s: buf, format: "Hello %d,%d", column, row);
6119 ImGui::Button(label: buf, size: ImVec2(-FLT_MIN, 0.0f));
6120 }
6121 //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)
6122 // ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));
6123 }
6124 }
6125 ImGui::EndTable();
6126 }
6127
6128 // Second example: set style.CellPadding to (0.0) or a custom value.
6129 // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...
6130 HelpMarker(desc: "Setting style.CellPadding to (0,0) or a custom value.");
6131 static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
6132 static ImVec2 cell_padding(0.0f, 0.0f);
6133 static bool show_widget_frame_bg = true;
6134
6135 PushStyleCompact();
6136 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Borders", flags: &flags2, flags_value: ImGuiTableFlags_Borders);
6137 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersH", flags: &flags2, flags_value: ImGuiTableFlags_BordersH);
6138 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags2, flags_value: ImGuiTableFlags_BordersV);
6139 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInner", flags: &flags2, flags_value: ImGuiTableFlags_BordersInner);
6140 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuter", flags: &flags2, flags_value: ImGuiTableFlags_BordersOuter);
6141 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags2, flags_value: ImGuiTableFlags_RowBg);
6142 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags2, flags_value: ImGuiTableFlags_Resizable);
6143 ImGui::Checkbox(label: "show_widget_frame_bg", v: &show_widget_frame_bg);
6144 ImGui::SliderFloat2(label: "CellPadding", v: &cell_padding.x, v_min: 0.0f, v_max: 10.0f, format: "%.0f");
6145 PopStyleCompact();
6146
6147 ImGui::PushStyleVar(idx: ImGuiStyleVar_CellPadding, val: cell_padding);
6148 if (ImGui::BeginTable(str_id: "table_padding_2", columns: 3, flags: flags2))
6149 {
6150 static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells
6151 static bool init = true;
6152 if (!show_widget_frame_bg)
6153 ImGui::PushStyleColor(idx: ImGuiCol_FrameBg, col: 0);
6154 for (int cell = 0; cell < 3 * 5; cell++)
6155 {
6156 ImGui::TableNextColumn();
6157 if (init)
6158 strcpy(dest: text_bufs[cell], src: "edit me");
6159 ImGui::SetNextItemWidth(-FLT_MIN);
6160 ImGui::PushID(int_id: cell);
6161 ImGui::InputText(label: "##cell", buf: text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell]));
6162 ImGui::PopID();
6163 }
6164 if (!show_widget_frame_bg)
6165 ImGui::PopStyleColor();
6166 init = false;
6167 ImGui::EndTable();
6168 }
6169 ImGui::PopStyleVar();
6170
6171 ImGui::TreePop();
6172 }
6173
6174 if (open_action != -1)
6175 ImGui::SetNextItemOpen(is_open: open_action != 0);
6176 IMGUI_DEMO_MARKER("Tables/Explicit widths");
6177 if (ImGui::TreeNode(label: "Sizing policies"))
6178 {
6179 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6180 PushStyleCompact();
6181 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags1, flags_value: ImGuiTableFlags_Resizable);
6182 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags1, flags_value: ImGuiTableFlags_NoHostExtendX);
6183 PopStyleCompact();
6184
6185 static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame };
6186 for (int table_n = 0; table_n < 4; table_n++)
6187 {
6188 ImGui::PushID(int_id: table_n);
6189 ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);
6190 EditTableSizingFlags(p_flags: &sizing_policy_flags[table_n]);
6191
6192 // To make it easier to understand the different sizing policy,
6193 // For each policy: we display one table where the columns have equal contents width,
6194 // and one where the columns have different contents width.
6195 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags: sizing_policy_flags[table_n] | flags1))
6196 {
6197 for (int row = 0; row < 3; row++)
6198 {
6199 ImGui::TableNextRow();
6200 ImGui::TableNextColumn(); ImGui::Text(fmt: "Oh dear");
6201 ImGui::TableNextColumn(); ImGui::Text(fmt: "Oh dear");
6202 ImGui::TableNextColumn(); ImGui::Text(fmt: "Oh dear");
6203 }
6204 ImGui::EndTable();
6205 }
6206 if (ImGui::BeginTable(str_id: "table2", columns: 3, flags: sizing_policy_flags[table_n] | flags1))
6207 {
6208 for (int row = 0; row < 3; row++)
6209 {
6210 ImGui::TableNextRow();
6211 ImGui::TableNextColumn(); ImGui::Text(fmt: "AAAA");
6212 ImGui::TableNextColumn(); ImGui::Text(fmt: "BBBBBBBB");
6213 ImGui::TableNextColumn(); ImGui::Text(fmt: "CCCCCCCCCCCC");
6214 }
6215 ImGui::EndTable();
6216 }
6217 ImGui::PopID();
6218 }
6219
6220 ImGui::Spacing();
6221 ImGui::TextUnformatted(text: "Advanced");
6222 ImGui::SameLine();
6223 HelpMarker(
6224 desc: "This section allows you to interact and see the effect of various sizing policies "
6225 "depending on whether Scroll is enabled and the contents of your columns.");
6226
6227 enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };
6228 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
6229 static int contents_type = CT_ShowWidth;
6230 static int column_count = 3;
6231
6232 PushStyleCompact();
6233 ImGui::PushID(str_id: "Advanced");
6234 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 30);
6235 EditTableSizingFlags(p_flags: &flags);
6236 ImGui::Combo(label: "Contents", current_item: &contents_type, items_separated_by_zeros: "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0");
6237 if (contents_type == CT_FillButton)
6238 {
6239 ImGui::SameLine();
6240 HelpMarker(
6241 desc: "Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop "
6242 "where contents width can feed into auto-column width can feed into contents width.");
6243 }
6244 ImGui::DragInt(label: "Columns", v: &column_count, v_speed: 0.1f, v_min: 1, v_max: 64, format: "%d", flags: ImGuiSliderFlags_AlwaysClamp);
6245 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
6246 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PreciseWidths", flags: &flags, flags_value: ImGuiTableFlags_PreciseWidths);
6247 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.");
6248 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags, flags_value: ImGuiTableFlags_ScrollX);
6249 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
6250 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoClip", flags: &flags, flags_value: ImGuiTableFlags_NoClip);
6251 ImGui::PopItemWidth();
6252 ImGui::PopID();
6253 PopStyleCompact();
6254
6255 if (ImGui::BeginTable(str_id: "table2", columns: column_count, flags, outer_size: ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))
6256 {
6257 for (int cell = 0; cell < 10 * column_count; cell++)
6258 {
6259 ImGui::TableNextColumn();
6260 int column = ImGui::TableGetColumnIndex();
6261 int row = ImGui::TableGetRowIndex();
6262
6263 ImGui::PushID(int_id: cell);
6264 char label[32];
6265 static char text_buf[32] = "";
6266 sprintf(s: label, format: "Hello %d,%d", column, row);
6267 switch (contents_type)
6268 {
6269 case CT_ShortText: ImGui::TextUnformatted(text: label); break;
6270 case CT_LongText: ImGui::Text(fmt: "Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break;
6271 case CT_ShowWidth: ImGui::Text(fmt: "W: %.1f", ImGui::GetContentRegionAvail().x); break;
6272 case CT_Button: ImGui::Button(label); break;
6273 case CT_FillButton: ImGui::Button(label, size: ImVec2(-FLT_MIN, 0.0f)); break;
6274 case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText(label: "##", buf: text_buf, IM_ARRAYSIZE(text_buf)); break;
6275 }
6276 ImGui::PopID();
6277 }
6278 ImGui::EndTable();
6279 }
6280 ImGui::TreePop();
6281 }
6282
6283 if (open_action != -1)
6284 ImGui::SetNextItemOpen(is_open: open_action != 0);
6285 IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping");
6286 if (ImGui::TreeNode(label: "Vertical scrolling, with clipping"))
6287 {
6288 HelpMarker(
6289 desc: "Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n"
6290 "We also demonstrate using ImGuiListClipper to virtualize the submission of many items.");
6291 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6292
6293 PushStyleCompact();
6294 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
6295 PopStyleCompact();
6296
6297 // When using ScrollX or ScrollY we need to specify a size for our table container!
6298 // Otherwise by default the table will fit all available space, like a BeginChild() call.
6299 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6300 if (ImGui::BeginTable(str_id: "table_scrolly", columns: 3, flags, outer_size))
6301 {
6302 ImGui::TableSetupScrollFreeze(cols: 0, rows: 1); // Make top row always visible
6303 ImGui::TableSetupColumn(label: "One", flags: ImGuiTableColumnFlags_None);
6304 ImGui::TableSetupColumn(label: "Two", flags: ImGuiTableColumnFlags_None);
6305 ImGui::TableSetupColumn(label: "Three", flags: ImGuiTableColumnFlags_None);
6306 ImGui::TableHeadersRow();
6307
6308 // Demonstrate using clipper for large vertical lists
6309 ImGuiListClipper clipper;
6310 clipper.Begin(items_count: 1000);
6311 while (clipper.Step())
6312 {
6313 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)
6314 {
6315 ImGui::TableNextRow();
6316 for (int column = 0; column < 3; column++)
6317 {
6318 ImGui::TableSetColumnIndex(column_n: column);
6319 ImGui::Text(fmt: "Hello %d,%d", column, row);
6320 }
6321 }
6322 }
6323 ImGui::EndTable();
6324 }
6325 ImGui::TreePop();
6326 }
6327
6328 if (open_action != -1)
6329 ImGui::SetNextItemOpen(is_open: open_action != 0);
6330 IMGUI_DEMO_MARKER("Tables/Horizontal scrolling");
6331 if (ImGui::TreeNode(label: "Horizontal scrolling"))
6332 {
6333 HelpMarker(
6334 desc: "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, "
6335 "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n"
6336 "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, "
6337 "because the container window won't automatically extend vertically to fix contents "
6338 "(this may be improved in future versions).");
6339 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6340 static int freeze_cols = 1;
6341 static int freeze_rows = 1;
6342
6343 PushStyleCompact();
6344 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
6345 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags, flags_value: ImGuiTableFlags_ScrollX);
6346 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
6347 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6348 ImGui::DragInt(label: "freeze_cols", v: &freeze_cols, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
6349 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6350 ImGui::DragInt(label: "freeze_rows", v: &freeze_rows, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
6351 PopStyleCompact();
6352
6353 // When using ScrollX or ScrollY we need to specify a size for our table container!
6354 // Otherwise by default the table will fit all available space, like a BeginChild() call.
6355 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6356 if (ImGui::BeginTable(str_id: "table_scrollx", columns: 7, flags, outer_size))
6357 {
6358 ImGui::TableSetupScrollFreeze(cols: freeze_cols, rows: freeze_rows);
6359 ImGui::TableSetupColumn(label: "Line #", flags: ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()
6360 ImGui::TableSetupColumn(label: "One");
6361 ImGui::TableSetupColumn(label: "Two");
6362 ImGui::TableSetupColumn(label: "Three");
6363 ImGui::TableSetupColumn(label: "Four");
6364 ImGui::TableSetupColumn(label: "Five");
6365 ImGui::TableSetupColumn(label: "Six");
6366 ImGui::TableHeadersRow();
6367 for (int row = 0; row < 20; row++)
6368 {
6369 ImGui::TableNextRow();
6370 for (int column = 0; column < 7; column++)
6371 {
6372 // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.
6373 // Because here we know that:
6374 // - A) all our columns are contributing the same to row height
6375 // - B) column 0 is always visible,
6376 // We only always submit this one column and can skip others.
6377 // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().
6378 if (!ImGui::TableSetColumnIndex(column_n: column) && column > 0)
6379 continue;
6380 if (column == 0)
6381 ImGui::Text(fmt: "Line %d", row);
6382 else
6383 ImGui::Text(fmt: "Hello world %d,%d", column, row);
6384 }
6385 }
6386 ImGui::EndTable();
6387 }
6388
6389 ImGui::Spacing();
6390 ImGui::TextUnformatted(text: "Stretch + ScrollX");
6391 ImGui::SameLine();
6392 HelpMarker(
6393 desc: "Showcase using Stretch columns + ScrollX together: "
6394 "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n"
6395 "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns "
6396 "along with ScrollX doesn't make sense.");
6397 static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6398 static float inner_width = 1000.0f;
6399 PushStyleCompact();
6400 ImGui::PushID(str_id: "flags3");
6401 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 30);
6402 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags2, flags_value: ImGuiTableFlags_ScrollX);
6403 ImGui::DragFloat(label: "inner_width", v: &inner_width, v_speed: 1.0f, v_min: 0.0f, FLT_MAX, format: "%.1f");
6404 ImGui::PopItemWidth();
6405 ImGui::PopID();
6406 PopStyleCompact();
6407 if (ImGui::BeginTable(str_id: "table2", columns: 7, flags: flags2, outer_size, inner_width))
6408 {
6409 for (int cell = 0; cell < 20 * 7; cell++)
6410 {
6411 ImGui::TableNextColumn();
6412 ImGui::Text(fmt: "Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());
6413 }
6414 ImGui::EndTable();
6415 }
6416 ImGui::TreePop();
6417 }
6418
6419 if (open_action != -1)
6420 ImGui::SetNextItemOpen(is_open: open_action != 0);
6421 IMGUI_DEMO_MARKER("Tables/Columns flags");
6422 if (ImGui::TreeNode(label: "Columns flags"))
6423 {
6424 // Create a first table just to show all the options/flags we want to make visible in our example!
6425 const int column_count = 3;
6426 const char* column_names[column_count] = { "One", "Two", "Three" };
6427 static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide };
6428 static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags()
6429
6430 if (ImGui::BeginTable(str_id: "table_columns_flags_checkboxes", columns: column_count, flags: ImGuiTableFlags_None))
6431 {
6432 PushStyleCompact();
6433 for (int column = 0; column < column_count; column++)
6434 {
6435 ImGui::TableNextColumn();
6436 ImGui::PushID(int_id: column);
6437 ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns
6438 ImGui::Text(fmt: "'%s'", column_names[column]);
6439 ImGui::Spacing();
6440 ImGui::Text(fmt: "Input flags:");
6441 EditTableColumnsFlags(p_flags: &column_flags[column]);
6442 ImGui::Spacing();
6443 ImGui::Text(fmt: "Output flags:");
6444 ImGui::BeginDisabled();
6445 ShowTableColumnsStatusFlags(flags: column_flags_out[column]);
6446 ImGui::EndDisabled();
6447 ImGui::PopID();
6448 }
6449 PopStyleCompact();
6450 ImGui::EndTable();
6451 }
6452
6453 // Create the real table we care about for the example!
6454 // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above,
6455 // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible
6456 // + resizing the parent window down).
6457 const ImGuiTableFlags flags
6458 = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
6459 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV
6460 | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;
6461 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);
6462 if (ImGui::BeginTable(str_id: "table_columns_flags", columns: column_count, flags, outer_size))
6463 {
6464 bool has_angled_header = false;
6465 for (int column = 0; column < column_count; column++)
6466 {
6467 has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;
6468 ImGui::TableSetupColumn(label: column_names[column], flags: column_flags[column]);
6469 }
6470 if (has_angled_header)
6471 ImGui::TableAngledHeadersRow();
6472 ImGui::TableHeadersRow();
6473 for (int column = 0; column < column_count; column++)
6474 column_flags_out[column] = ImGui::TableGetColumnFlags(column_n: column);
6475 float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);
6476 for (int row = 0; row < 8; row++)
6477 {
6478 // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.
6479 ImGui::Indent(indent_w: indent_step);
6480 ImGui::TableNextRow();
6481 for (int column = 0; column < column_count; column++)
6482 {
6483 ImGui::TableSetColumnIndex(column_n: column);
6484 ImGui::Text(fmt: "%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column_n: column));
6485 }
6486 }
6487 ImGui::Unindent(indent_w: indent_step * 8.0f);
6488
6489 ImGui::EndTable();
6490 }
6491 ImGui::TreePop();
6492 }
6493
6494 if (open_action != -1)
6495 ImGui::SetNextItemOpen(is_open: open_action != 0);
6496 IMGUI_DEMO_MARKER("Tables/Columns widths");
6497 if (ImGui::TreeNode(label: "Columns widths"))
6498 {
6499 HelpMarker(desc: "Using TableSetupColumn() to setup default width.");
6500
6501 static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;
6502 PushStyleCompact();
6503 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags1, flags_value: ImGuiTableFlags_Resizable);
6504 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoBordersInBodyUntilResize", flags: &flags1, flags_value: ImGuiTableFlags_NoBordersInBodyUntilResize);
6505 PopStyleCompact();
6506 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags: flags1))
6507 {
6508 // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.
6509 ImGui::TableSetupColumn(label: "one", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 100.0f); // Default to 100.0f
6510 ImGui::TableSetupColumn(label: "two", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 200.0f); // Default to 200.0f
6511 ImGui::TableSetupColumn(label: "three", flags: ImGuiTableColumnFlags_WidthFixed); // Default to auto
6512 ImGui::TableHeadersRow();
6513 for (int row = 0; row < 4; row++)
6514 {
6515 ImGui::TableNextRow();
6516 for (int column = 0; column < 3; column++)
6517 {
6518 ImGui::TableSetColumnIndex(column_n: column);
6519 if (row == 0)
6520 ImGui::Text(fmt: "(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6521 else
6522 ImGui::Text(fmt: "Hello %d,%d", column, row);
6523 }
6524 }
6525 ImGui::EndTable();
6526 }
6527
6528 HelpMarker(
6529 desc: "Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, "
6530 "fixed columns with set width may still be shrunk down if there's not enough space in the host.");
6531
6532 static ImGuiTableFlags flags2 = ImGuiTableFlags_None;
6533 PushStyleCompact();
6534 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoKeepColumnsVisible", flags: &flags2, flags_value: ImGuiTableFlags_NoKeepColumnsVisible);
6535 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags2, flags_value: ImGuiTableFlags_BordersInnerV);
6536 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags2, flags_value: ImGuiTableFlags_BordersOuterV);
6537 PopStyleCompact();
6538 if (ImGui::BeginTable(str_id: "table2", columns: 4, flags: flags2))
6539 {
6540 // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns
6541 // will default to ImGuiTableColumnFlags_WidthFixed.
6542 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 100.0f);
6543 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 15.0f);
6544 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 30.0f);
6545 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 15.0f);
6546 for (int row = 0; row < 5; row++)
6547 {
6548 ImGui::TableNextRow();
6549 for (int column = 0; column < 4; column++)
6550 {
6551 ImGui::TableSetColumnIndex(column_n: column);
6552 if (row == 0)
6553 ImGui::Text(fmt: "(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6554 else
6555 ImGui::Text(fmt: "Hello %d,%d", column, row);
6556 }
6557 }
6558 ImGui::EndTable();
6559 }
6560 ImGui::TreePop();
6561 }
6562
6563 if (open_action != -1)
6564 ImGui::SetNextItemOpen(is_open: open_action != 0);
6565 IMGUI_DEMO_MARKER("Tables/Nested tables");
6566 if (ImGui::TreeNode(label: "Nested tables"))
6567 {
6568 HelpMarker(desc: "This demonstrates embedding a table into another table cell.");
6569
6570 if (ImGui::BeginTable(str_id: "table_nested1", columns: 2, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6571 {
6572 ImGui::TableSetupColumn(label: "A0");
6573 ImGui::TableSetupColumn(label: "A1");
6574 ImGui::TableHeadersRow();
6575
6576 ImGui::TableNextColumn();
6577 ImGui::Text(fmt: "A0 Row 0");
6578 {
6579 float rows_height = TEXT_BASE_HEIGHT * 2;
6580 if (ImGui::BeginTable(str_id: "table_nested2", columns: 2, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6581 {
6582 ImGui::TableSetupColumn(label: "B0");
6583 ImGui::TableSetupColumn(label: "B1");
6584 ImGui::TableHeadersRow();
6585
6586 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height: rows_height);
6587 ImGui::TableNextColumn();
6588 ImGui::Text(fmt: "B0 Row 0");
6589 ImGui::TableNextColumn();
6590 ImGui::Text(fmt: "B1 Row 0");
6591 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height: rows_height);
6592 ImGui::TableNextColumn();
6593 ImGui::Text(fmt: "B0 Row 1");
6594 ImGui::TableNextColumn();
6595 ImGui::Text(fmt: "B1 Row 1");
6596
6597 ImGui::EndTable();
6598 }
6599 }
6600 ImGui::TableNextColumn(); ImGui::Text(fmt: "A1 Row 0");
6601 ImGui::TableNextColumn(); ImGui::Text(fmt: "A0 Row 1");
6602 ImGui::TableNextColumn(); ImGui::Text(fmt: "A1 Row 1");
6603 ImGui::EndTable();
6604 }
6605 ImGui::TreePop();
6606 }
6607
6608 if (open_action != -1)
6609 ImGui::SetNextItemOpen(is_open: open_action != 0);
6610 IMGUI_DEMO_MARKER("Tables/Row height");
6611 if (ImGui::TreeNode(label: "Row height"))
6612 {
6613 HelpMarker(
6614 desc: "You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, "
6615 "so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n"
6616 "We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.");
6617 if (ImGui::BeginTable(str_id: "table_row_height", columns: 1, flags: ImGuiTableFlags_Borders))
6618 {
6619 for (int row = 0; row < 8; row++)
6620 {
6621 float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row);
6622 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height);
6623 ImGui::TableNextColumn();
6624 ImGui::Text(fmt: "min_row_height = %.2f", min_row_height);
6625 }
6626 ImGui::EndTable();
6627 }
6628
6629 HelpMarker(
6630 desc: "Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n"
6631 "Please note that Tables Row Height is not the same thing as Current Line Height, "
6632 "as a table cell may contains multiple lines.");
6633 if (ImGui::BeginTable(str_id: "table_share_lineheight", columns: 2, flags: ImGuiTableFlags_Borders))
6634 {
6635 ImGui::TableNextRow();
6636 ImGui::TableNextColumn();
6637 ImGui::ColorButton(desc_id: "##1", col: ImVec4(0.13f, 0.26f, 0.40f, 1.0f), flags: ImGuiColorEditFlags_None, size: ImVec2(40, 40));
6638 ImGui::TableNextColumn();
6639 ImGui::Text(fmt: "Line 1");
6640 ImGui::Text(fmt: "Line 2");
6641
6642 ImGui::TableNextRow();
6643 ImGui::TableNextColumn();
6644 ImGui::ColorButton(desc_id: "##2", col: ImVec4(0.13f, 0.26f, 0.40f, 1.0f), flags: ImGuiColorEditFlags_None, size: ImVec2(40, 40));
6645 ImGui::TableNextColumn();
6646 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: 0.0f); // Reuse line height from previous column
6647 ImGui::Text(fmt: "Line 1, with SameLine(0,0)");
6648 ImGui::Text(fmt: "Line 2");
6649
6650 ImGui::EndTable();
6651 }
6652
6653 HelpMarker(desc: "Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.");
6654 if (ImGui::BeginTable(str_id: "table_changing_cellpadding_y", columns: 1, flags: ImGuiTableFlags_Borders))
6655 {
6656 ImGuiStyle& style = ImGui::GetStyle();
6657 for (int row = 0; row < 8; row++)
6658 {
6659 if ((row % 3) == 2)
6660 ImGui::PushStyleVarY(idx: ImGuiStyleVar_CellPadding, val_y: 20.0f);
6661 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None);
6662 ImGui::TableNextColumn();
6663 ImGui::Text(fmt: "CellPadding.y = %.2f", style.CellPadding.y);
6664 if ((row % 3) == 2)
6665 ImGui::PopStyleVar();
6666 }
6667 ImGui::EndTable();
6668 }
6669
6670 ImGui::TreePop();
6671 }
6672
6673 if (open_action != -1)
6674 ImGui::SetNextItemOpen(is_open: open_action != 0);
6675 IMGUI_DEMO_MARKER("Tables/Outer size");
6676 if (ImGui::TreeNode(label: "Outer size"))
6677 {
6678 // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY
6679 // Important to that note how the two flags have slightly different behaviors!
6680 ImGui::Text(fmt: "Using NoHostExtendX and NoHostExtendY:");
6681 PushStyleCompact();
6682 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
6683 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendX);
6684 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.");
6685 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendY", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendY);
6686 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.");
6687 PopStyleCompact();
6688
6689 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
6690 if (ImGui::BeginTable(str_id: "table1", columns: 3, flags, outer_size))
6691 {
6692 for (int row = 0; row < 10; row++)
6693 {
6694 ImGui::TableNextRow();
6695 for (int column = 0; column < 3; column++)
6696 {
6697 ImGui::TableNextColumn();
6698 ImGui::Text(fmt: "Cell %d,%d", column, row);
6699 }
6700 }
6701 ImGui::EndTable();
6702 }
6703 ImGui::SameLine();
6704 ImGui::Text(fmt: "Hello!");
6705
6706 ImGui::Spacing();
6707
6708 ImGui::Text(fmt: "Using explicit size:");
6709 if (ImGui::BeginTable(str_id: "table2", columns: 3, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, outer_size: ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6710 {
6711 for (int row = 0; row < 5; row++)
6712 {
6713 ImGui::TableNextRow();
6714 for (int column = 0; column < 3; column++)
6715 {
6716 ImGui::TableNextColumn();
6717 ImGui::Text(fmt: "Cell %d,%d", column, row);
6718 }
6719 }
6720 ImGui::EndTable();
6721 }
6722 ImGui::SameLine();
6723 if (ImGui::BeginTable(str_id: "table3", columns: 3, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, outer_size: ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6724 {
6725 for (int row = 0; row < 3; row++)
6726 {
6727 ImGui::TableNextRow(row_flags: 0, min_row_height: TEXT_BASE_HEIGHT * 1.5f);
6728 for (int column = 0; column < 3; column++)
6729 {
6730 ImGui::TableNextColumn();
6731 ImGui::Text(fmt: "Cell %d,%d", column, row);
6732 }
6733 }
6734 ImGui::EndTable();
6735 }
6736
6737 ImGui::TreePop();
6738 }
6739
6740 if (open_action != -1)
6741 ImGui::SetNextItemOpen(is_open: open_action != 0);
6742 IMGUI_DEMO_MARKER("Tables/Background color");
6743 if (ImGui::TreeNode(label: "Background color"))
6744 {
6745 static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;
6746 static int row_bg_type = 1;
6747 static int row_bg_target = 1;
6748 static int cell_bg_type = 1;
6749
6750 PushStyleCompact();
6751 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Borders", flags: &flags, flags_value: ImGuiTableFlags_Borders);
6752 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags, flags_value: ImGuiTableFlags_RowBg);
6753 ImGui::SameLine(); HelpMarker(desc: "ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.");
6754 ImGui::Combo(label: "row bg type", current_item: (int*)&row_bg_type, items_separated_by_zeros: "None\0Red\0Gradient\0");
6755 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.");
6756 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.");
6757 IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);
6758 IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);
6759 IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);
6760 PopStyleCompact();
6761
6762 if (ImGui::BeginTable(str_id: "table1", columns: 5, flags))
6763 {
6764 for (int row = 0; row < 6; row++)
6765 {
6766 ImGui::TableNextRow();
6767
6768 // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'
6769 // 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.
6770 if (row_bg_type != 0)
6771 {
6772 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?
6773 ImGui::TableSetBgColor(target: ImGuiTableBgTarget_RowBg0 + row_bg_target, color: row_bg_color);
6774 }
6775
6776 // Fill cells
6777 for (int column = 0; column < 5; column++)
6778 {
6779 ImGui::TableSetColumnIndex(column_n: column);
6780 ImGui::Text(fmt: "%c%c", 'A' + row, '0' + column);
6781
6782 // Change background of Cells B1->C2
6783 // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'
6784 // (the CellBg color will be blended over the RowBg and ColumnBg colors)
6785 // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.
6786 if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)
6787 {
6788 ImU32 cell_bg_color = ImGui::GetColorU32(col: ImVec4(0.3f, 0.3f, 0.7f, 0.65f));
6789 ImGui::TableSetBgColor(target: ImGuiTableBgTarget_CellBg, color: cell_bg_color);
6790 }
6791 }
6792 }
6793 ImGui::EndTable();
6794 }
6795 ImGui::TreePop();
6796 }
6797
6798 if (open_action != -1)
6799 ImGui::SetNextItemOpen(is_open: open_action != 0);
6800 IMGUI_DEMO_MARKER("Tables/Tree view");
6801 if (ImGui::TreeNode(label: "Tree view"))
6802 {
6803 static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
6804
6805 static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull;
6806 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanFullWidth", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_SpanFullWidth);
6807 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanLabelWidth", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_SpanLabelWidth);
6808 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_SpanAllColumns", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_SpanAllColumns);
6809 ImGui::CheckboxFlags(label: "ImGuiTreeNodeFlags_LabelSpanAllColumns", flags: &tree_node_flags_base, flags_value: ImGuiTreeNodeFlags_LabelSpanAllColumns);
6810 ImGui::SameLine(); HelpMarker(desc: "Useful if you know that you aren't displaying contents in other columns");
6811
6812 HelpMarker(desc: "See \"Columns flags\" section to configure how indentation is applied to individual columns.");
6813 if (ImGui::BeginTable(str_id: "3ways", columns: 3, flags: table_flags))
6814 {
6815 // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
6816 ImGui::TableSetupColumn(label: "Name", flags: ImGuiTableColumnFlags_NoHide);
6817 ImGui::TableSetupColumn(label: "Size", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 12.0f);
6818 ImGui::TableSetupColumn(label: "Type", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: TEXT_BASE_WIDTH * 18.0f);
6819 ImGui::TableHeadersRow();
6820
6821 // Simple storage to output a dummy file-system.
6822 struct MyTreeNode
6823 {
6824 const char* Name;
6825 const char* Type;
6826 int Size;
6827 int ChildIdx;
6828 int ChildCount;
6829 static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
6830 {
6831 ImGui::TableNextRow();
6832 ImGui::TableNextColumn();
6833 const bool is_folder = (node->ChildCount > 0);
6834
6835 ImGuiTreeNodeFlags node_flags = tree_node_flags_base;
6836 if (node != &all_nodes[0])
6837 node_flags &= ~ImGuiTreeNodeFlags_LabelSpanAllColumns; // Only demonstrate this on the root node.
6838
6839 if (is_folder)
6840 {
6841 bool open = ImGui::TreeNodeEx(label: node->Name, flags: node_flags);
6842 if ((node_flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) == 0)
6843 {
6844 ImGui::TableNextColumn();
6845 ImGui::TextDisabled(fmt: "--");
6846 ImGui::TableNextColumn();
6847 ImGui::TextUnformatted(text: node->Type);
6848 }
6849 if (open)
6850 {
6851 for (int child_n = 0; child_n < node->ChildCount; child_n++)
6852 DisplayNode(node: &all_nodes[node->ChildIdx + child_n], all_nodes);
6853 ImGui::TreePop();
6854 }
6855 }
6856 else
6857 {
6858 ImGui::TreeNodeEx(label: node->Name, flags: node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen);
6859 ImGui::TableNextColumn();
6860 ImGui::Text(fmt: "%d", node->Size);
6861 ImGui::TableNextColumn();
6862 ImGui::TextUnformatted(text: node->Type);
6863 }
6864 }
6865 };
6866 static const MyTreeNode nodes[] =
6867 {
6868 { .Name: "Root with Long Name", .Type: "Folder", .Size: -1, .ChildIdx: 1, .ChildCount: 3 }, // 0
6869 { .Name: "Music", .Type: "Folder", .Size: -1, .ChildIdx: 4, .ChildCount: 2 }, // 1
6870 { .Name: "Textures", .Type: "Folder", .Size: -1, .ChildIdx: 6, .ChildCount: 3 }, // 2
6871 { .Name: "desktop.ini", .Type: "System file", .Size: 1024, .ChildIdx: -1,.ChildCount: -1 }, // 3
6872 { .Name: "File1_a.wav", .Type: "Audio file", .Size: 123000, .ChildIdx: -1,.ChildCount: -1 }, // 4
6873 { .Name: "File1_b.wav", .Type: "Audio file", .Size: 456000, .ChildIdx: -1,.ChildCount: -1 }, // 5
6874 { .Name: "Image001.png", .Type: "Image file", .Size: 203128, .ChildIdx: -1,.ChildCount: -1 }, // 6
6875 { .Name: "Copy of Image001.png", .Type: "Image file", .Size: 203256, .ChildIdx: -1,.ChildCount: -1 }, // 7
6876 { .Name: "Copy of Image001 (Final2).png",.Type: "Image file", .Size: 203512, .ChildIdx: -1,.ChildCount: -1 }, // 8
6877 };
6878
6879 MyTreeNode::DisplayNode(node: &nodes[0], all_nodes: nodes);
6880
6881 ImGui::EndTable();
6882 }
6883 ImGui::TreePop();
6884 }
6885
6886 if (open_action != -1)
6887 ImGui::SetNextItemOpen(is_open: open_action != 0);
6888 IMGUI_DEMO_MARKER("Tables/Item width");
6889 if (ImGui::TreeNode(label: "Item width"))
6890 {
6891 HelpMarker(
6892 desc: "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n"
6893 "Note that on auto-resizing non-resizable fixed columns, querying the content width for "
6894 "e.g. right-alignment doesn't make sense.");
6895 if (ImGui::BeginTable(str_id: "table_item_width", columns: 3, flags: ImGuiTableFlags_Borders))
6896 {
6897 ImGui::TableSetupColumn(label: "small");
6898 ImGui::TableSetupColumn(label: "half");
6899 ImGui::TableSetupColumn(label: "right-align");
6900 ImGui::TableHeadersRow();
6901
6902 for (int row = 0; row < 3; row++)
6903 {
6904 ImGui::TableNextRow();
6905 if (row == 0)
6906 {
6907 // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)
6908 ImGui::TableSetColumnIndex(column_n: 0);
6909 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 3.0f); // Small
6910 ImGui::TableSetColumnIndex(column_n: 1);
6911 ImGui::PushItemWidth(item_width: -ImGui::GetContentRegionAvail().x * 0.5f);
6912 ImGui::TableSetColumnIndex(column_n: 2);
6913 ImGui::PushItemWidth(item_width: -FLT_MIN); // Right-aligned
6914 }
6915
6916 // Draw our contents
6917 static float dummy_f = 0.0f;
6918 ImGui::PushID(int_id: row);
6919 ImGui::TableSetColumnIndex(column_n: 0);
6920 ImGui::SliderFloat(label: "float0", v: &dummy_f, v_min: 0.0f, v_max: 1.0f);
6921 ImGui::TableSetColumnIndex(column_n: 1);
6922 ImGui::SliderFloat(label: "float1", v: &dummy_f, v_min: 0.0f, v_max: 1.0f);
6923 ImGui::TableSetColumnIndex(column_n: 2);
6924 ImGui::SliderFloat(label: "##float2", v: &dummy_f, v_min: 0.0f, v_max: 1.0f); // No visible label since right-aligned
6925 ImGui::PopID();
6926 }
6927 ImGui::EndTable();
6928 }
6929 ImGui::TreePop();
6930 }
6931
6932 // Demonstrate using TableHeader() calls instead of TableHeadersRow()
6933 if (open_action != -1)
6934 ImGui::SetNextItemOpen(is_open: open_action != 0);
6935 IMGUI_DEMO_MARKER("Tables/Custom headers");
6936 if (ImGui::TreeNode(label: "Custom headers"))
6937 {
6938 const int COLUMNS_COUNT = 3;
6939 if (ImGui::BeginTable(str_id: "table_custom_headers", columns: COLUMNS_COUNT, flags: ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6940 {
6941 ImGui::TableSetupColumn(label: "Apricot");
6942 ImGui::TableSetupColumn(label: "Banana");
6943 ImGui::TableSetupColumn(label: "Cherry");
6944
6945 // Dummy entire-column selection storage
6946 // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
6947 static bool column_selected[3] = {};
6948
6949 // Instead of calling TableHeadersRow() we'll submit custom headers ourselves.
6950 // (A different approach is also possible:
6951 // - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.
6952 // - Call TableHeadersRow() normally. This will submit TableHeader() with no name.
6953 // - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)
6954 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_Headers);
6955 for (int column = 0; column < COLUMNS_COUNT; column++)
6956 {
6957 ImGui::TableSetColumnIndex(column_n: column);
6958 const char* column_name = ImGui::TableGetColumnName(column_n: column); // Retrieve name passed to TableSetupColumn()
6959 ImGui::PushID(int_id: column);
6960 ImGui::PushStyleVar(idx: ImGuiStyleVar_FramePadding, val: ImVec2(0, 0));
6961 ImGui::Checkbox(label: "##checkall", v: &column_selected[column]);
6962 ImGui::PopStyleVar();
6963 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
6964 ImGui::TableHeader(label: column_name);
6965 ImGui::PopID();
6966 }
6967
6968 // Submit table contents
6969 for (int row = 0; row < 5; row++)
6970 {
6971 ImGui::TableNextRow();
6972 for (int column = 0; column < 3; column++)
6973 {
6974 char buf[32];
6975 sprintf(s: buf, format: "Cell %d,%d", column, row);
6976 ImGui::TableSetColumnIndex(column_n: column);
6977 ImGui::Selectable(label: buf, selected: column_selected[column]);
6978 }
6979 }
6980 ImGui::EndTable();
6981 }
6982 ImGui::TreePop();
6983 }
6984
6985 // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers
6986 if (open_action != -1)
6987 ImGui::SetNextItemOpen(is_open: open_action != 0);
6988 IMGUI_DEMO_MARKER("Tables/Angled headers");
6989 if (ImGui::TreeNode(label: "Angled headers"))
6990 {
6991 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" };
6992 const int columns_count = IM_ARRAYSIZE(column_names);
6993 const int rows_count = 12;
6994
6995 static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;
6996 static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed;
6997 static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage
6998 static int frozen_cols = 1;
6999 static int frozen_rows = 2;
7000 ImGui::CheckboxFlags(label: "_ScrollX", flags: &table_flags, flags_value: ImGuiTableFlags_ScrollX);
7001 ImGui::CheckboxFlags(label: "_ScrollY", flags: &table_flags, flags_value: ImGuiTableFlags_ScrollY);
7002 ImGui::CheckboxFlags(label: "_Resizable", flags: &table_flags, flags_value: ImGuiTableFlags_Resizable);
7003 ImGui::CheckboxFlags(label: "_Sortable", flags: &table_flags, flags_value: ImGuiTableFlags_Sortable);
7004 ImGui::CheckboxFlags(label: "_NoBordersInBody", flags: &table_flags, flags_value: ImGuiTableFlags_NoBordersInBody);
7005 ImGui::CheckboxFlags(label: "_HighlightHoveredColumn", flags: &table_flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
7006 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7007 ImGui::SliderInt(label: "Frozen columns", v: &frozen_cols, v_min: 0, v_max: 2);
7008 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7009 ImGui::SliderInt(label: "Frozen rows", v: &frozen_rows, v_min: 0, v_max: 2);
7010 ImGui::CheckboxFlags(label: "Disable header contributing to column width", flags: &column_flags, flags_value: ImGuiTableColumnFlags_NoHeaderWidth);
7011
7012 if (ImGui::TreeNode(label: "Style settings"))
7013 {
7014 ImGui::SameLine();
7015 HelpMarker(desc: "Giving access to some ImGuiStyle value in this demo for convenience.");
7016 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7017 ImGui::SliderAngle(label: "style.TableAngledHeadersAngle", v_rad: &ImGui::GetStyle().TableAngledHeadersAngle, v_degrees_min: -50.0f, v_degrees_max: +50.0f);
7018 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7019 ImGui::SliderFloat2(label: "style.TableAngledHeadersTextAlign", v: (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
7020 ImGui::TreePop();
7021 }
7022
7023 if (ImGui::BeginTable(str_id: "table_angled_headers", columns: columns_count, flags: table_flags, outer_size: ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))
7024 {
7025 ImGui::TableSetupColumn(label: column_names[0], flags: ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
7026 for (int n = 1; n < columns_count; n++)
7027 ImGui::TableSetupColumn(label: column_names[n], flags: column_flags);
7028 ImGui::TableSetupScrollFreeze(cols: frozen_cols, rows: frozen_rows);
7029
7030 ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.
7031 ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions.
7032 for (int row = 0; row < rows_count; row++)
7033 {
7034 ImGui::PushID(int_id: row);
7035 ImGui::TableNextRow();
7036 ImGui::TableSetColumnIndex(column_n: 0);
7037 ImGui::AlignTextToFramePadding();
7038 ImGui::Text(fmt: "Track %d", row);
7039 for (int column = 1; column < columns_count; column++)
7040 if (ImGui::TableSetColumnIndex(column_n: column))
7041 {
7042 ImGui::PushID(int_id: column);
7043 ImGui::Checkbox(label: "", v: &bools[row * columns_count + column]);
7044 ImGui::PopID();
7045 }
7046 ImGui::PopID();
7047 }
7048 ImGui::EndTable();
7049 }
7050 ImGui::TreePop();
7051 }
7052
7053 // Demonstrate creating custom context menus inside columns,
7054 // while playing it nice with context menus provided by TableHeadersRow()/TableHeader()
7055 if (open_action != -1)
7056 ImGui::SetNextItemOpen(is_open: open_action != 0);
7057 IMGUI_DEMO_MARKER("Tables/Context menus");
7058 if (ImGui::TreeNode(label: "Context menus"))
7059 {
7060 HelpMarker(
7061 desc: "By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n"
7062 "Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.");
7063 static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody;
7064
7065 PushStyleCompact();
7066 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ContextMenuInBody", flags: &flags1, flags_value: ImGuiTableFlags_ContextMenuInBody);
7067 PopStyleCompact();
7068
7069 // Context Menus: first example
7070 // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7071 // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)
7072 const int COLUMNS_COUNT = 3;
7073 if (ImGui::BeginTable(str_id: "table_context_menu", columns: COLUMNS_COUNT, flags: flags1))
7074 {
7075 ImGui::TableSetupColumn(label: "One");
7076 ImGui::TableSetupColumn(label: "Two");
7077 ImGui::TableSetupColumn(label: "Three");
7078
7079 // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.
7080 ImGui::TableHeadersRow();
7081
7082 // Submit dummy contents
7083 for (int row = 0; row < 4; row++)
7084 {
7085 ImGui::TableNextRow();
7086 for (int column = 0; column < COLUMNS_COUNT; column++)
7087 {
7088 ImGui::TableSetColumnIndex(column_n: column);
7089 ImGui::Text(fmt: "Cell %d,%d", column, row);
7090 }
7091 }
7092 ImGui::EndTable();
7093 }
7094
7095 // Context Menus: second example
7096 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7097 // [2.2] Right-click on the ".." to open a custom popup
7098 // [2.3] Right-click in columns to open another custom popup
7099 HelpMarker(
7100 desc: "Demonstrate mixing table context menu (over header), item context button (over button) "
7101 "and custom per-colunm context menu (over column body).");
7102 ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
7103 if (ImGui::BeginTable(str_id: "table_context_menu_2", columns: COLUMNS_COUNT, flags: flags2))
7104 {
7105 ImGui::TableSetupColumn(label: "One");
7106 ImGui::TableSetupColumn(label: "Two");
7107 ImGui::TableSetupColumn(label: "Three");
7108
7109 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7110 ImGui::TableHeadersRow();
7111 for (int row = 0; row < 4; row++)
7112 {
7113 ImGui::TableNextRow();
7114 for (int column = 0; column < COLUMNS_COUNT; column++)
7115 {
7116 // Submit dummy contents
7117 ImGui::TableSetColumnIndex(column_n: column);
7118 ImGui::Text(fmt: "Cell %d,%d", column, row);
7119 ImGui::SameLine();
7120
7121 // [2.2] Right-click on the ".." to open a custom popup
7122 ImGui::PushID(int_id: row * COLUMNS_COUNT + column);
7123 ImGui::SmallButton(label: "..");
7124 if (ImGui::BeginPopupContextItem())
7125 {
7126 ImGui::Text(fmt: "This is the popup for Button(\"..\") in Cell %d,%d", column, row);
7127 if (ImGui::Button(label: "Close"))
7128 ImGui::CloseCurrentPopup();
7129 ImGui::EndPopup();
7130 }
7131 ImGui::PopID();
7132 }
7133 }
7134
7135 // [2.3] Right-click anywhere in columns to open another custom popup
7136 // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup
7137 // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping)
7138 int hovered_column = -1;
7139 for (int column = 0; column < COLUMNS_COUNT + 1; column++)
7140 {
7141 ImGui::PushID(int_id: column);
7142 if (ImGui::TableGetColumnFlags(column_n: column) & ImGuiTableColumnFlags_IsHovered)
7143 hovered_column = column;
7144 if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(button: 1))
7145 ImGui::OpenPopup(str_id: "MyPopup");
7146 if (ImGui::BeginPopup(str_id: "MyPopup"))
7147 {
7148 if (column == COLUMNS_COUNT)
7149 ImGui::Text(fmt: "This is a custom popup for unused space after the last column.");
7150 else
7151 ImGui::Text(fmt: "This is a custom popup for Column %d", column);
7152 if (ImGui::Button(label: "Close"))
7153 ImGui::CloseCurrentPopup();
7154 ImGui::EndPopup();
7155 }
7156 ImGui::PopID();
7157 }
7158
7159 ImGui::EndTable();
7160 ImGui::Text(fmt: "Hovered column: %d", hovered_column);
7161 }
7162 ImGui::TreePop();
7163 }
7164
7165 // Demonstrate creating multiple tables with the same ID
7166 if (open_action != -1)
7167 ImGui::SetNextItemOpen(is_open: open_action != 0);
7168 IMGUI_DEMO_MARKER("Tables/Synced instances");
7169 if (ImGui::TreeNode(label: "Synced instances"))
7170 {
7171 HelpMarker(desc: "Multiple tables with the same identifier will share their settings, width, visibility, order etc.");
7172
7173 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;
7174 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
7175 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
7176 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SizingFixedFit", flags: &flags, flags_value: ImGuiTableFlags_SizingFixedFit);
7177 ImGui::CheckboxFlags(label: "ImGuiTableFlags_HighlightHoveredColumn", flags: &flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
7178 for (int n = 0; n < 3; n++)
7179 {
7180 char buf[32];
7181 sprintf(s: buf, format: "Synced Table %d", n);
7182 bool open = ImGui::CollapsingHeader(label: buf, flags: ImGuiTreeNodeFlags_DefaultOpen);
7183 if (open && ImGui::BeginTable(str_id: "Table", columns: 3, flags, outer_size: ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5)))
7184 {
7185 ImGui::TableSetupColumn(label: "One");
7186 ImGui::TableSetupColumn(label: "Two");
7187 ImGui::TableSetupColumn(label: "Three");
7188 ImGui::TableHeadersRow();
7189 const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions.
7190 for (int cell = 0; cell < cell_count; cell++)
7191 {
7192 ImGui::TableNextColumn();
7193 ImGui::Text(fmt: "this cell %d", cell);
7194 }
7195 ImGui::EndTable();
7196 }
7197 }
7198 ImGui::TreePop();
7199 }
7200
7201 // Demonstrate using Sorting facilities
7202 // This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting.
7203 // Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified)
7204 static const char* template_items_names[] =
7205 {
7206 "Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango",
7207 "Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot"
7208 };
7209 if (open_action != -1)
7210 ImGui::SetNextItemOpen(is_open: open_action != 0);
7211 IMGUI_DEMO_MARKER("Tables/Sorting");
7212 if (ImGui::TreeNode(label: "Sorting"))
7213 {
7214 // Create item list
7215 static ImVector<MyItem> items;
7216 if (items.Size == 0)
7217 {
7218 items.resize(new_size: 50, v: MyItem());
7219 for (int n = 0; n < items.Size; n++)
7220 {
7221 const int template_n = n % IM_ARRAYSIZE(template_items_names);
7222 MyItem& item = items[n];
7223 item.ID = n;
7224 item.Name = template_items_names[template_n];
7225 item.Quantity = (n * n - n) % 20; // Assign default quantities
7226 }
7227 }
7228
7229 // Options
7230 static ImGuiTableFlags flags =
7231 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7232 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody
7233 | ImGuiTableFlags_ScrollY;
7234 PushStyleCompact();
7235 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortMulti", flags: &flags, flags_value: ImGuiTableFlags_SortMulti);
7236 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).");
7237 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortTristate", flags: &flags, flags_value: ImGuiTableFlags_SortTristate);
7238 ImGui::SameLine(); HelpMarker(desc: "When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7239 PopStyleCompact();
7240
7241 if (ImGui::BeginTable(str_id: "table_sorting", columns: 4, flags, outer_size: ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), inner_width: 0.0f))
7242 {
7243 // Declare columns
7244 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7245 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7246 // Demonstrate using a mixture of flags among available sort-related flags:
7247 // - ImGuiTableColumnFlags_DefaultSort
7248 // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending
7249 // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending
7250 ImGui::TableSetupColumn(label: "ID", flags: ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_ID);
7251 ImGui::TableSetupColumn(label: "Name", flags: ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Name);
7252 ImGui::TableSetupColumn(label: "Action", flags: ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Action);
7253 ImGui::TableSetupColumn(label: "Quantity", flags: ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Quantity);
7254 ImGui::TableSetupScrollFreeze(cols: 0, rows: 1); // Make row always visible
7255 ImGui::TableHeadersRow();
7256
7257 // Sort our data if sort specs have been changed!
7258 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
7259 if (sort_specs->SpecsDirty)
7260 {
7261 MyItem::SortWithSortSpecs(sort_specs, items: items.Data, items_count: items.Size);
7262 sort_specs->SpecsDirty = false;
7263 }
7264
7265 // Demonstrate using clipper for large vertical lists
7266 ImGuiListClipper clipper;
7267 clipper.Begin(items_count: items.Size);
7268 while (clipper.Step())
7269 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7270 {
7271 // Display a data item
7272 MyItem* item = &items[row_n];
7273 ImGui::PushID(int_id: item->ID);
7274 ImGui::TableNextRow();
7275 ImGui::TableNextColumn();
7276 ImGui::Text(fmt: "%04d", item->ID);
7277 ImGui::TableNextColumn();
7278 ImGui::TextUnformatted(text: item->Name);
7279 ImGui::TableNextColumn();
7280 ImGui::SmallButton(label: "None");
7281 ImGui::TableNextColumn();
7282 ImGui::Text(fmt: "%d", item->Quantity);
7283 ImGui::PopID();
7284 }
7285 ImGui::EndTable();
7286 }
7287 ImGui::TreePop();
7288 }
7289
7290 // In this example we'll expose most table flags and settings.
7291 // For specific flags and settings refer to the corresponding section for more detailed explanation.
7292 // This section is mostly useful to experiment with combining certain flags or settings with each others.
7293 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]
7294 if (open_action != -1)
7295 ImGui::SetNextItemOpen(is_open: open_action != 0);
7296 IMGUI_DEMO_MARKER("Tables/Advanced");
7297 if (ImGui::TreeNode(label: "Advanced"))
7298 {
7299 static ImGuiTableFlags flags =
7300 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable
7301 | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7302 | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody
7303 | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
7304 | ImGuiTableFlags_SizingFixedFit;
7305 static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;
7306
7307 enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };
7308 static int contents_type = CT_SelectableSpanRow;
7309 const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" };
7310 static int freeze_cols = 1;
7311 static int freeze_rows = 1;
7312 static int items_count = IM_ARRAYSIZE(template_items_names) * 2;
7313 static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);
7314 static float row_min_height = 0.0f; // Auto
7315 static float inner_width_with_scroll = 0.0f; // Auto-extend
7316 static bool outer_size_enabled = true;
7317 static bool show_headers = true;
7318 static bool show_wrapped_text = false;
7319 //static ImGuiTextFilter filter;
7320 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing
7321 if (ImGui::TreeNode(label: "Options"))
7322 {
7323 // Make the UI compact because there are so many fields
7324 PushStyleCompact();
7325 ImGui::PushItemWidth(item_width: TEXT_BASE_WIDTH * 28.0f);
7326
7327 if (ImGui::TreeNodeEx(label: "Features:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7328 {
7329 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Resizable", flags: &flags, flags_value: ImGuiTableFlags_Resizable);
7330 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Reorderable", flags: &flags, flags_value: ImGuiTableFlags_Reorderable);
7331 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Hideable", flags: &flags, flags_value: ImGuiTableFlags_Hideable);
7332 ImGui::CheckboxFlags(label: "ImGuiTableFlags_Sortable", flags: &flags, flags_value: ImGuiTableFlags_Sortable);
7333 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoSavedSettings", flags: &flags, flags_value: ImGuiTableFlags_NoSavedSettings);
7334 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ContextMenuInBody", flags: &flags, flags_value: ImGuiTableFlags_ContextMenuInBody);
7335 ImGui::TreePop();
7336 }
7337
7338 if (ImGui::TreeNodeEx(label: "Decorations:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7339 {
7340 ImGui::CheckboxFlags(label: "ImGuiTableFlags_RowBg", flags: &flags, flags_value: ImGuiTableFlags_RowBg);
7341 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersV", flags: &flags, flags_value: ImGuiTableFlags_BordersV);
7342 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterV", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterV);
7343 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerV", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerV);
7344 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersH", flags: &flags, flags_value: ImGuiTableFlags_BordersH);
7345 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersOuterH", flags: &flags, flags_value: ImGuiTableFlags_BordersOuterH);
7346 ImGui::CheckboxFlags(label: "ImGuiTableFlags_BordersInnerH", flags: &flags, flags_value: ImGuiTableFlags_BordersInnerH);
7347 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");
7348 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)");
7349 ImGui::TreePop();
7350 }
7351
7352 if (ImGui::TreeNodeEx(label: "Sizing:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7353 {
7354 EditTableSizingFlags(p_flags: &flags);
7355 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.");
7356 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendX", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendX);
7357 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.");
7358 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoHostExtendY", flags: &flags, flags_value: ImGuiTableFlags_NoHostExtendY);
7359 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.");
7360 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoKeepColumnsVisible", flags: &flags, flags_value: ImGuiTableFlags_NoKeepColumnsVisible);
7361 ImGui::SameLine(); HelpMarker(desc: "Only available if ScrollX is disabled.");
7362 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PreciseWidths", flags: &flags, flags_value: ImGuiTableFlags_PreciseWidths);
7363 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.");
7364 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoClip", flags: &flags, flags_value: ImGuiTableFlags_NoClip);
7365 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.");
7366 ImGui::TreePop();
7367 }
7368
7369 if (ImGui::TreeNodeEx(label: "Padding:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7370 {
7371 ImGui::CheckboxFlags(label: "ImGuiTableFlags_PadOuterX", flags: &flags, flags_value: ImGuiTableFlags_PadOuterX);
7372 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadOuterX", flags: &flags, flags_value: ImGuiTableFlags_NoPadOuterX);
7373 ImGui::CheckboxFlags(label: "ImGuiTableFlags_NoPadInnerX", flags: &flags, flags_value: ImGuiTableFlags_NoPadInnerX);
7374 ImGui::TreePop();
7375 }
7376
7377 if (ImGui::TreeNodeEx(label: "Scrolling:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7378 {
7379 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollX", flags: &flags, flags_value: ImGuiTableFlags_ScrollX);
7380 ImGui::SameLine();
7381 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7382 ImGui::DragInt(label: "freeze_cols", v: &freeze_cols, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
7383 ImGui::CheckboxFlags(label: "ImGuiTableFlags_ScrollY", flags: &flags, flags_value: ImGuiTableFlags_ScrollY);
7384 ImGui::SameLine();
7385 ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7386 ImGui::DragInt(label: "freeze_rows", v: &freeze_rows, v_speed: 0.2f, v_min: 0, v_max: 9, NULL, flags: ImGuiSliderFlags_NoInput);
7387 ImGui::TreePop();
7388 }
7389
7390 if (ImGui::TreeNodeEx(label: "Sorting:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7391 {
7392 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortMulti", flags: &flags, flags_value: ImGuiTableFlags_SortMulti);
7393 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).");
7394 ImGui::CheckboxFlags(label: "ImGuiTableFlags_SortTristate", flags: &flags, flags_value: ImGuiTableFlags_SortTristate);
7395 ImGui::SameLine(); HelpMarker(desc: "When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7396 ImGui::TreePop();
7397 }
7398
7399 if (ImGui::TreeNodeEx(label: "Headers:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7400 {
7401 ImGui::Checkbox(label: "show_headers", v: &show_headers);
7402 ImGui::CheckboxFlags(label: "ImGuiTableFlags_HighlightHoveredColumn", flags: &flags, flags_value: ImGuiTableFlags_HighlightHoveredColumn);
7403 ImGui::CheckboxFlags(label: "ImGuiTableColumnFlags_AngledHeader", flags: &columns_base_flags, flags_value: ImGuiTableColumnFlags_AngledHeader);
7404 ImGui::SameLine(); HelpMarker(desc: "Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo).");
7405 ImGui::TreePop();
7406 }
7407
7408 if (ImGui::TreeNodeEx(label: "Other:", flags: ImGuiTreeNodeFlags_DefaultOpen))
7409 {
7410 ImGui::Checkbox(label: "show_wrapped_text", v: &show_wrapped_text);
7411
7412 ImGui::DragFloat2(label: "##OuterSize", v: &outer_size_value.x);
7413 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
7414 ImGui::Checkbox(label: "outer_size", v: &outer_size_enabled);
7415 ImGui::SameLine();
7416 HelpMarker(desc: "If scrolling is disabled (ScrollX and ScrollY not set):\n"
7417 "- The table is output directly in the parent window.\n"
7418 "- OuterSize.x < 0.0f will right-align the table.\n"
7419 "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n"
7420 "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).");
7421
7422 // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.
7423 // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.
7424 ImGui::DragFloat(label: "inner_width (when ScrollX active)", v: &inner_width_with_scroll, v_speed: 1.0f, v_min: 0.0f, FLT_MAX);
7425
7426 ImGui::DragFloat(label: "row_min_height", v: &row_min_height, v_speed: 1.0f, v_min: 0.0f, FLT_MAX);
7427 ImGui::SameLine(); HelpMarker(desc: "Specify height of the Selectable item.");
7428
7429 ImGui::DragInt(label: "items_count", v: &items_count, v_speed: 0.1f, v_min: 0, v_max: 9999);
7430 ImGui::Combo(label: "items_type (first column)", current_item: &contents_type, items: contents_type_names, IM_ARRAYSIZE(contents_type_names));
7431 //filter.Draw("filter");
7432 ImGui::TreePop();
7433 }
7434
7435 ImGui::PopItemWidth();
7436 PopStyleCompact();
7437 ImGui::Spacing();
7438 ImGui::TreePop();
7439 }
7440
7441 // Update item list if we changed the number of items
7442 static ImVector<MyItem> items;
7443 static ImVector<int> selection;
7444 static bool items_need_sort = false;
7445 if (items.Size != items_count)
7446 {
7447 items.resize(new_size: items_count, v: MyItem());
7448 for (int n = 0; n < items_count; n++)
7449 {
7450 const int template_n = n % IM_ARRAYSIZE(template_items_names);
7451 MyItem& item = items[n];
7452 item.ID = n;
7453 item.Name = template_items_names[template_n];
7454 item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities
7455 }
7456 }
7457
7458 const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();
7459 const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;
7460 ImVec2 table_scroll_cur, table_scroll_max; // For debug display
7461 const ImDrawList* table_draw_list = NULL; // "
7462
7463 // Submit table
7464 const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;
7465 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))
7466 {
7467 // Declare columns
7468 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7469 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7470 ImGui::TableSetupColumn(label: "ID", flags: columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, init_width_or_weight: 0.0f, user_id: MyItemColumnID_ID);
7471 ImGui::TableSetupColumn(label: "Name", flags: columns_base_flags | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Name);
7472 ImGui::TableSetupColumn(label: "Action", flags: columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Action);
7473 ImGui::TableSetupColumn(label: "Quantity", flags: columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, init_width_or_weight: 0.0f, user_id: MyItemColumnID_Quantity);
7474 ImGui::TableSetupColumn(label: "Description", flags: columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), init_width_or_weight: 0.0f, user_id: MyItemColumnID_Description);
7475 ImGui::TableSetupColumn(label: "Hidden", flags: columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);
7476 ImGui::TableSetupScrollFreeze(cols: freeze_cols, rows: freeze_rows);
7477
7478 // Sort our data if sort specs have been changed!
7479 ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();
7480 if (sort_specs && sort_specs->SpecsDirty)
7481 items_need_sort = true;
7482 if (sort_specs && items_need_sort && items.Size > 1)
7483 {
7484 MyItem::SortWithSortSpecs(sort_specs, items: items.Data, items_count: items.Size);
7485 sort_specs->SpecsDirty = false;
7486 }
7487 items_need_sort = false;
7488
7489 // Take note of whether we are currently sorting based on the Quantity field,
7490 // we will use this to trigger sorting when we know the data of this column has been modified.
7491 const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(column_n: 3) & ImGuiTableColumnFlags_IsSorted) != 0;
7492
7493 // Show headers
7494 if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)
7495 ImGui::TableAngledHeadersRow();
7496 if (show_headers)
7497 ImGui::TableHeadersRow();
7498
7499 // Show data
7500 // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?
7501#if 1
7502 // Demonstrate using clipper for large vertical lists
7503 ImGuiListClipper clipper;
7504 clipper.Begin(items_count: items.Size);
7505 while (clipper.Step())
7506 {
7507 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7508#else
7509 // Without clipper
7510 {
7511 for (int row_n = 0; row_n < items.Size; row_n++)
7512#endif
7513 {
7514 MyItem* item = &items[row_n];
7515 //if (!filter.PassFilter(item->Name))
7516 // continue;
7517
7518 const bool item_is_selected = selection.contains(v: item->ID);
7519 ImGui::PushID(int_id: item->ID);
7520 ImGui::TableNextRow(row_flags: ImGuiTableRowFlags_None, min_row_height: row_min_height);
7521
7522 // For the demo purpose we can select among different type of items submitted in the first column
7523 ImGui::TableSetColumnIndex(column_n: 0);
7524 char label[32];
7525 sprintf(s: label, format: "%04d", item->ID);
7526 if (contents_type == CT_Text)
7527 ImGui::TextUnformatted(text: label);
7528 else if (contents_type == CT_Button)
7529 ImGui::Button(label);
7530 else if (contents_type == CT_SmallButton)
7531 ImGui::SmallButton(label);
7532 else if (contents_type == CT_FillButton)
7533 ImGui::Button(label, size: ImVec2(-FLT_MIN, 0.0f));
7534 else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)
7535 {
7536 ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None;
7537 if (ImGui::Selectable(label, selected: item_is_selected, flags: selectable_flags, size: ImVec2(0, row_min_height)))
7538 {
7539 if (ImGui::GetIO().KeyCtrl)
7540 {
7541 if (item_is_selected)
7542 selection.find_erase_unsorted(v: item->ID);
7543 else
7544 selection.push_back(v: item->ID);
7545 }
7546 else
7547 {
7548 selection.clear();
7549 selection.push_back(v: item->ID);
7550 }
7551 }
7552 }
7553
7554 if (ImGui::TableSetColumnIndex(column_n: 1))
7555 ImGui::TextUnformatted(text: item->Name);
7556
7557 // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
7558 // and we are currently sorting on the column showing the Quantity.
7559 // To avoid triggering a sort while holding the button, we only trigger it when the button has been released.
7560 // You will probably need some extra logic if you want to automatically sort when a specific entry changes.
7561 if (ImGui::TableSetColumnIndex(column_n: 2))
7562 {
7563 if (ImGui::SmallButton(label: "Chop")) { item->Quantity += 1; }
7564 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7565 ImGui::SameLine();
7566 if (ImGui::SmallButton(label: "Eat")) { item->Quantity -= 1; }
7567 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7568 }
7569
7570 if (ImGui::TableSetColumnIndex(column_n: 3))
7571 ImGui::Text(fmt: "%d", item->Quantity);
7572
7573 ImGui::TableSetColumnIndex(column_n: 4);
7574 if (show_wrapped_text)
7575 ImGui::TextWrapped(fmt: "Lorem ipsum dolor sit amet");
7576 else
7577 ImGui::Text(fmt: "Lorem ipsum dolor sit amet");
7578
7579 if (ImGui::TableSetColumnIndex(column_n: 5))
7580 ImGui::Text(fmt: "1234");
7581
7582 ImGui::PopID();
7583 }
7584 }
7585
7586 // Store some info to display debug details below
7587 table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());
7588 table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());
7589 table_draw_list = ImGui::GetWindowDrawList();
7590 ImGui::EndTable();
7591 }
7592 static bool show_debug_details = false;
7593 ImGui::Checkbox(label: "Debug details", v: &show_debug_details);
7594 if (show_debug_details && table_draw_list)
7595 {
7596 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: 0.0f);
7597 const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;
7598 if (table_draw_list == parent_draw_list)
7599 ImGui::Text(fmt: ": DrawCmd: +%d (in same window)",
7600 table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);
7601 else
7602 ImGui::Text(fmt: ": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)",
7603 table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y);
7604 }
7605 ImGui::TreePop();
7606 }
7607
7608 ImGui::PopID();
7609
7610 DemoWindowColumns();
7611
7612 if (disable_indent)
7613 ImGui::PopStyleVar();
7614}
7615
7616// Demonstrate old/legacy Columns API!
7617// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]
7618static void DemoWindowColumns()
7619{
7620 IMGUI_DEMO_MARKER("Columns (legacy API)");
7621 bool open = ImGui::TreeNode(label: "Legacy Columns API");
7622 ImGui::SameLine();
7623 HelpMarker(desc: "Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!");
7624 if (!open)
7625 return;
7626
7627 // Basic columns
7628 IMGUI_DEMO_MARKER("Columns (legacy API)/Basic");
7629 if (ImGui::TreeNode(label: "Basic"))
7630 {
7631 ImGui::Text(fmt: "Without border:");
7632 ImGui::Columns(count: 3, id: "mycolumns3", borders: false); // 3-ways, no border
7633 ImGui::Separator();
7634 for (int n = 0; n < 14; n++)
7635 {
7636 char label[32];
7637 sprintf(s: label, format: "Item %d", n);
7638 if (ImGui::Selectable(label)) {}
7639 //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}
7640 ImGui::NextColumn();
7641 }
7642 ImGui::Columns(count: 1);
7643 ImGui::Separator();
7644
7645 ImGui::Text(fmt: "With border:");
7646 ImGui::Columns(count: 4, id: "mycolumns"); // 4-ways, with border
7647 ImGui::Separator();
7648 ImGui::Text(fmt: "ID"); ImGui::NextColumn();
7649 ImGui::Text(fmt: "Name"); ImGui::NextColumn();
7650 ImGui::Text(fmt: "Path"); ImGui::NextColumn();
7651 ImGui::Text(fmt: "Hovered"); ImGui::NextColumn();
7652 ImGui::Separator();
7653 const char* names[3] = { "One", "Two", "Three" };
7654 const char* paths[3] = { "/path/one", "/path/two", "/path/three" };
7655 static int selected = -1;
7656 for (int i = 0; i < 3; i++)
7657 {
7658 char label[32];
7659 sprintf(s: label, format: "%04d", i);
7660 if (ImGui::Selectable(label, selected: selected == i, flags: ImGuiSelectableFlags_SpanAllColumns))
7661 selected = i;
7662 bool hovered = ImGui::IsItemHovered();
7663 ImGui::NextColumn();
7664 ImGui::Text(fmt: names[i]); ImGui::NextColumn();
7665 ImGui::Text(fmt: paths[i]); ImGui::NextColumn();
7666 ImGui::Text(fmt: "%d", hovered); ImGui::NextColumn();
7667 }
7668 ImGui::Columns(count: 1);
7669 ImGui::Separator();
7670 ImGui::TreePop();
7671 }
7672
7673 IMGUI_DEMO_MARKER("Columns (legacy API)/Borders");
7674 if (ImGui::TreeNode(label: "Borders"))
7675 {
7676 // NB: Future columns API should allow automatic horizontal borders.
7677 static bool h_borders = true;
7678 static bool v_borders = true;
7679 static int columns_count = 4;
7680 const int lines_count = 3;
7681 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7682 ImGui::DragInt(label: "##columns_count", v: &columns_count, v_speed: 0.1f, v_min: 2, v_max: 10, format: "%d columns");
7683 if (columns_count < 2)
7684 columns_count = 2;
7685 ImGui::SameLine();
7686 ImGui::Checkbox(label: "horizontal", v: &h_borders);
7687 ImGui::SameLine();
7688 ImGui::Checkbox(label: "vertical", v: &v_borders);
7689 ImGui::Columns(count: columns_count, NULL, borders: v_borders);
7690 for (int i = 0; i < columns_count * lines_count; i++)
7691 {
7692 if (h_borders && ImGui::GetColumnIndex() == 0)
7693 ImGui::Separator();
7694 ImGui::PushID(int_id: i);
7695 ImGui::Text(fmt: "%c%c%c", 'a' + i, 'a' + i, 'a' + i);
7696 ImGui::Text(fmt: "Width %.2f", ImGui::GetColumnWidth());
7697 ImGui::Text(fmt: "Avail %.2f", ImGui::GetContentRegionAvail().x);
7698 ImGui::Text(fmt: "Offset %.2f", ImGui::GetColumnOffset());
7699 ImGui::Text(fmt: "Long text that is likely to clip");
7700 ImGui::Button(label: "Button", size: ImVec2(-FLT_MIN, 0.0f));
7701 ImGui::PopID();
7702 ImGui::NextColumn();
7703 }
7704 ImGui::Columns(count: 1);
7705 if (h_borders)
7706 ImGui::Separator();
7707 ImGui::TreePop();
7708 }
7709
7710 // Create multiple items in a same cell before switching to next column
7711 IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items");
7712 if (ImGui::TreeNode(label: "Mixed items"))
7713 {
7714 ImGui::Columns(count: 3, id: "mixed");
7715 ImGui::Separator();
7716
7717 ImGui::Text(fmt: "Hello");
7718 ImGui::Button(label: "Banana");
7719 ImGui::NextColumn();
7720
7721 ImGui::Text(fmt: "ImGui");
7722 ImGui::Button(label: "Apple");
7723 static float foo = 1.0f;
7724 ImGui::InputFloat(label: "red", v: &foo, step: 0.05f, step_fast: 0, format: "%.3f");
7725 ImGui::Text(fmt: "An extra line here.");
7726 ImGui::NextColumn();
7727
7728 ImGui::Text(fmt: "Sailor");
7729 ImGui::Button(label: "Corniflower");
7730 static float bar = 1.0f;
7731 ImGui::InputFloat(label: "blue", v: &bar, step: 0.05f, step_fast: 0, format: "%.3f");
7732 ImGui::NextColumn();
7733
7734 if (ImGui::CollapsingHeader(label: "Category A")) { ImGui::Text(fmt: "Blah blah blah"); } ImGui::NextColumn();
7735 if (ImGui::CollapsingHeader(label: "Category B")) { ImGui::Text(fmt: "Blah blah blah"); } ImGui::NextColumn();
7736 if (ImGui::CollapsingHeader(label: "Category C")) { ImGui::Text(fmt: "Blah blah blah"); } ImGui::NextColumn();
7737 ImGui::Columns(count: 1);
7738 ImGui::Separator();
7739 ImGui::TreePop();
7740 }
7741
7742 // Word wrapping
7743 IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping");
7744 if (ImGui::TreeNode(label: "Word-wrapping"))
7745 {
7746 ImGui::Columns(count: 2, id: "word-wrapping");
7747 ImGui::Separator();
7748 ImGui::TextWrapped(fmt: "The quick brown fox jumps over the lazy dog.");
7749 ImGui::TextWrapped(fmt: "Hello Left");
7750 ImGui::NextColumn();
7751 ImGui::TextWrapped(fmt: "The quick brown fox jumps over the lazy dog.");
7752 ImGui::TextWrapped(fmt: "Hello Right");
7753 ImGui::Columns(count: 1);
7754 ImGui::Separator();
7755 ImGui::TreePop();
7756 }
7757
7758 IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling");
7759 if (ImGui::TreeNode(label: "Horizontal Scrolling"))
7760 {
7761 ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));
7762 ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);
7763 ImGui::BeginChild(str_id: "##ScrollingRegion", size: child_size, child_flags: ImGuiChildFlags_None, window_flags: ImGuiWindowFlags_HorizontalScrollbar);
7764 ImGui::Columns(count: 10);
7765
7766 // Also demonstrate using clipper for large vertical lists
7767 int ITEMS_COUNT = 2000;
7768 ImGuiListClipper clipper;
7769 clipper.Begin(items_count: ITEMS_COUNT);
7770 while (clipper.Step())
7771 {
7772 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
7773 for (int j = 0; j < 10; j++)
7774 {
7775 ImGui::Text(fmt: "Line %d Column %d...", i, j);
7776 ImGui::NextColumn();
7777 }
7778 }
7779 ImGui::Columns(count: 1);
7780 ImGui::EndChild();
7781 ImGui::TreePop();
7782 }
7783
7784 IMGUI_DEMO_MARKER("Columns (legacy API)/Tree");
7785 if (ImGui::TreeNode(label: "Tree"))
7786 {
7787 ImGui::Columns(count: 2, id: "tree", borders: true);
7788 for (int x = 0; x < 3; x++)
7789 {
7790 bool open1 = ImGui::TreeNode(ptr_id: (void*)(intptr_t)x, fmt: "Node%d", x);
7791 ImGui::NextColumn();
7792 ImGui::Text(fmt: "Node contents");
7793 ImGui::NextColumn();
7794 if (open1)
7795 {
7796 for (int y = 0; y < 3; y++)
7797 {
7798 bool open2 = ImGui::TreeNode(ptr_id: (void*)(intptr_t)y, fmt: "Node%d.%d", x, y);
7799 ImGui::NextColumn();
7800 ImGui::Text(fmt: "Node contents");
7801 if (open2)
7802 {
7803 ImGui::Text(fmt: "Even more contents");
7804 if (ImGui::TreeNode(label: "Tree in column"))
7805 {
7806 ImGui::Text(fmt: "The quick brown fox jumps over the lazy dog");
7807 ImGui::TreePop();
7808 }
7809 }
7810 ImGui::NextColumn();
7811 if (open2)
7812 ImGui::TreePop();
7813 }
7814 ImGui::TreePop();
7815 }
7816 }
7817 ImGui::Columns(count: 1);
7818 ImGui::TreePop();
7819 }
7820
7821 ImGui::TreePop();
7822}
7823
7824//-----------------------------------------------------------------------------
7825// [SECTION] DemoWindowInputs()
7826//-----------------------------------------------------------------------------
7827
7828static void DemoWindowInputs()
7829{
7830 IMGUI_DEMO_MARKER("Inputs & Focus");
7831 if (ImGui::CollapsingHeader(label: "Inputs & Focus"))
7832 {
7833 ImGuiIO& io = ImGui::GetIO();
7834
7835 // Display inputs submitted to ImGuiIO
7836 IMGUI_DEMO_MARKER("Inputs & Focus/Inputs");
7837 ImGui::SetNextItemOpen(is_open: true, cond: ImGuiCond_Once);
7838 bool inputs_opened = ImGui::TreeNode(label: "Inputs");
7839 ImGui::SameLine();
7840 HelpMarker(
7841 desc: "This is a simplified view. See more detailed input state:\n"
7842 "- in 'Tools->Metrics/Debugger->Inputs'.\n"
7843 "- in 'Tools->Debug Log->IO'.");
7844 if (inputs_opened)
7845 {
7846 if (ImGui::IsMousePosValid())
7847 ImGui::Text(fmt: "Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
7848 else
7849 ImGui::Text(fmt: "Mouse pos: <INVALID>");
7850 ImGui::Text(fmt: "Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
7851 ImGui::Text(fmt: "Mouse down:");
7852 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]); }
7853 ImGui::Text(fmt: "Mouse wheel: %.1f", io.MouseWheel);
7854 ImGui::Text(fmt: "Mouse clicked count:");
7855 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]); }
7856
7857 // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows
7858 // displaying the data for old/new backends.
7859 // User code should never have to go through such hoops!
7860 // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
7861 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
7862 ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;
7863 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); }
7864 ImGui::Text(fmt: "Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
7865 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.
7866
7867 ImGui::TreePop();
7868 }
7869
7870 // Display ImGuiIO output flags
7871 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs");
7872 ImGui::SetNextItemOpen(is_open: true, cond: ImGuiCond_Once);
7873 bool outputs_opened = ImGui::TreeNode(label: "Outputs");
7874 ImGui::SameLine();
7875 HelpMarker(
7876 desc: "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui "
7877 "to instruct your application of how to route inputs. Typically, when a value is true, it means "
7878 "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n"
7879 "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, "
7880 "and underlying application should ignore mouse inputs (in practice there are many and more subtle "
7881 "rules leading to how those flags are set).");
7882 if (outputs_opened)
7883 {
7884 ImGui::Text(fmt: "io.WantCaptureMouse: %d", io.WantCaptureMouse);
7885 ImGui::Text(fmt: "io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
7886 ImGui::Text(fmt: "io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
7887 ImGui::Text(fmt: "io.WantTextInput: %d", io.WantTextInput);
7888 ImGui::Text(fmt: "io.WantSetMousePos: %d", io.WantSetMousePos);
7889 ImGui::Text(fmt: "io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible);
7890
7891 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override");
7892 if (ImGui::TreeNode(label: "WantCapture override"))
7893 {
7894 HelpMarker(
7895 desc: "Hovering the colored canvas will override io.WantCaptureXXX fields.\n"
7896 "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering "
7897 "and true when clicking.");
7898 static int capture_override_mouse = -1;
7899 static int capture_override_keyboard = -1;
7900 const char* capture_override_desc[] = { "None", "Set to false", "Set to true" };
7901 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7902 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);
7903 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7904 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);
7905
7906 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
7907 if (ImGui::IsItemHovered() && capture_override_mouse != -1)
7908 ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1);
7909 if (ImGui::IsItemHovered() && capture_override_keyboard != -1)
7910 ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1);
7911
7912 ImGui::TreePop();
7913 }
7914 ImGui::TreePop();
7915 }
7916
7917 // Demonstrate using Shortcut() and Routing Policies.
7918 // The general flow is:
7919 // - Code interested in a chord (e.g. "Ctrl+A") declares their intent.
7920 // - Multiple locations may be interested in same chord! Routing helps find a winner.
7921 // - Every frame, we resolve all claims and assign one owner if the modifiers are matching.
7922 // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route.
7923 // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags.
7924 // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused.
7925 // TL;DR: Most uses will simply be:
7926 // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy.
7927 IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts");
7928 if (ImGui::TreeNode(label: "Shortcuts"))
7929 {
7930 static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat;
7931 static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused;
7932 ImGui::CheckboxFlags(label: "ImGuiInputFlags_Repeat", flags: &route_options, flags_value: ImGuiInputFlags_Repeat);
7933 ImGui::RadioButton(label: "ImGuiInputFlags_RouteActive", v: &route_type, v_button: ImGuiInputFlags_RouteActive);
7934 ImGui::RadioButton(label: "ImGuiInputFlags_RouteFocused (default)", v: &route_type, v_button: ImGuiInputFlags_RouteFocused);
7935 ImGui::RadioButton(label: "ImGuiInputFlags_RouteGlobal", v: &route_type, v_button: ImGuiInputFlags_RouteGlobal);
7936 ImGui::Indent();
7937 ImGui::BeginDisabled(disabled: route_type != ImGuiInputFlags_RouteGlobal);
7938 ImGui::CheckboxFlags(label: "ImGuiInputFlags_RouteOverFocused", flags: &route_options, flags_value: ImGuiInputFlags_RouteOverFocused);
7939 ImGui::CheckboxFlags(label: "ImGuiInputFlags_RouteOverActive", flags: &route_options, flags_value: ImGuiInputFlags_RouteOverActive);
7940 ImGui::CheckboxFlags(label: "ImGuiInputFlags_RouteUnlessBgFocused", flags: &route_options, flags_value: ImGuiInputFlags_RouteUnlessBgFocused);
7941 ImGui::EndDisabled();
7942 ImGui::Unindent();
7943 ImGui::RadioButton(label: "ImGuiInputFlags_RouteAlways", v: &route_type, v_button: ImGuiInputFlags_RouteAlways);
7944 ImGuiInputFlags flags = route_type | route_options; // Merged flags
7945 if (route_type != ImGuiInputFlags_RouteGlobal)
7946 flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused);
7947
7948 ImGui::SeparatorText(label: "Using SetNextItemShortcut()");
7949 ImGui::Text(fmt: "Ctrl+S");
7950 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_S, flags: flags | ImGuiInputFlags_Tooltip);
7951 ImGui::Button(label: "Save");
7952 ImGui::Text(fmt: "Alt+F");
7953 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Alt | ImGuiKey_F, flags: flags | ImGuiInputFlags_Tooltip);
7954 static float f = 0.5f;
7955 ImGui::SliderFloat(label: "Factor", v: &f, v_min: 0.0f, v_max: 1.0f);
7956
7957 ImGui::SeparatorText(label: "Using Shortcut()");
7958 const float line_height = ImGui::GetTextLineHeightWithSpacing();
7959 const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A;
7960
7961 ImGui::Text(fmt: "Ctrl+A");
7962 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7963
7964 ImGui::PushStyleColor(idx: ImGuiCol_ChildBg, col: ImVec4(1.0f, 0.0f, 1.0f, 0.1f));
7965
7966 ImGui::BeginChild(str_id: "WindowA", size: ImVec2(-FLT_MIN, line_height * 14), child_flags: true);
7967 ImGui::Text(fmt: "Press CTRL+A and see who receives it!");
7968 ImGui::Separator();
7969
7970 // 1: Window polling for CTRL+A
7971 ImGui::Text(fmt: "(in WindowA)");
7972 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7973
7974 // 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active)
7975 // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
7976 //char str[16] = "Press CTRL+A";
7977 //ImGui::Spacing();
7978 //ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
7979 //ImGuiID item_id = ImGui::GetItemID();
7980 //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused");
7981 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "...");
7982
7983 // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA
7984 ImGui::BeginChild(str_id: "ChildD", size: ImVec2(-FLT_MIN, line_height * 4), child_flags: true);
7985 ImGui::Text(fmt: "(in ChildD: not using same Shortcut)");
7986 ImGui::Text(fmt: "IsWindowFocused: %d", ImGui::IsWindowFocused());
7987 ImGui::EndChild();
7988
7989 // 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused.
7990 ImGui::BeginChild(str_id: "ChildE", size: ImVec2(-FLT_MIN, line_height * 4), child_flags: true);
7991 ImGui::Text(fmt: "(in ChildE: using same Shortcut)");
7992 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7993 ImGui::EndChild();
7994
7995 // 5: In a popup
7996 if (ImGui::Button(label: "Open Popup"))
7997 ImGui::OpenPopup(str_id: "PopupF");
7998 if (ImGui::BeginPopup(str_id: "PopupF"))
7999 {
8000 ImGui::Text(fmt: "(in PopupF)");
8001 ImGui::Text(fmt: "IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
8002 // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
8003 //ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
8004 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "...");
8005 ImGui::EndPopup();
8006 }
8007 ImGui::EndChild();
8008 ImGui::PopStyleColor();
8009
8010 ImGui::TreePop();
8011 }
8012
8013 // Display mouse cursors
8014 IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors");
8015 if (ImGui::TreeNode(label: "Mouse Cursors"))
8016 {
8017 const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" };
8018 IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
8019
8020 ImGuiMouseCursor current = ImGui::GetMouseCursor();
8021 const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A";
8022 ImGui::Text(fmt: "Current mouse cursor = %d: %s", current, cursor_name);
8023 ImGui::BeginDisabled(disabled: true);
8024 ImGui::CheckboxFlags(label: "io.BackendFlags: HasMouseCursors", flags: &io.BackendFlags, flags_value: ImGuiBackendFlags_HasMouseCursors);
8025 ImGui::EndDisabled();
8026
8027 ImGui::Text(fmt: "Hover to see mouse cursors:");
8028 ImGui::SameLine(); HelpMarker(
8029 desc: "Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. "
8030 "If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, "
8031 "otherwise your backend needs to handle it.");
8032 for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)
8033 {
8034 char label[32];
8035 sprintf(s: label, format: "Mouse cursor %d: %s", i, mouse_cursors_names[i]);
8036 ImGui::Bullet(); ImGui::Selectable(label, selected: false);
8037 if (ImGui::IsItemHovered())
8038 ImGui::SetMouseCursor(i);
8039 }
8040 ImGui::TreePop();
8041 }
8042
8043 IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing");
8044 if (ImGui::TreeNode(label: "Tabbing"))
8045 {
8046 ImGui::Text(fmt: "Use TAB/SHIFT+TAB to cycle through keyboard editable fields.");
8047 static char buf[32] = "hello";
8048 ImGui::InputText(label: "1", buf, IM_ARRAYSIZE(buf));
8049 ImGui::InputText(label: "2", buf, IM_ARRAYSIZE(buf));
8050 ImGui::InputText(label: "3", buf, IM_ARRAYSIZE(buf));
8051 ImGui::PushItemFlag(option: ImGuiItemFlags_NoTabStop, enabled: true);
8052 ImGui::InputText(label: "4 (tab skip)", buf, IM_ARRAYSIZE(buf));
8053 ImGui::SameLine(); HelpMarker(desc: "Item won't be cycled through when using TAB or Shift+Tab.");
8054 ImGui::PopItemFlag();
8055 ImGui::InputText(label: "5", buf, IM_ARRAYSIZE(buf));
8056 ImGui::TreePop();
8057 }
8058
8059 IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code");
8060 if (ImGui::TreeNode(label: "Focus from code"))
8061 {
8062 bool focus_1 = ImGui::Button(label: "Focus on 1"); ImGui::SameLine();
8063 bool focus_2 = ImGui::Button(label: "Focus on 2"); ImGui::SameLine();
8064 bool focus_3 = ImGui::Button(label: "Focus on 3");
8065 int has_focus = 0;
8066 static char buf[128] = "click on a button to set focus";
8067
8068 if (focus_1) ImGui::SetKeyboardFocusHere();
8069 ImGui::InputText(label: "1", buf, IM_ARRAYSIZE(buf));
8070 if (ImGui::IsItemActive()) has_focus = 1;
8071
8072 if (focus_2) ImGui::SetKeyboardFocusHere();
8073 ImGui::InputText(label: "2", buf, IM_ARRAYSIZE(buf));
8074 if (ImGui::IsItemActive()) has_focus = 2;
8075
8076 ImGui::PushItemFlag(option: ImGuiItemFlags_NoTabStop, enabled: true);
8077 if (focus_3) ImGui::SetKeyboardFocusHere();
8078 ImGui::InputText(label: "3 (tab skip)", buf, IM_ARRAYSIZE(buf));
8079 if (ImGui::IsItemActive()) has_focus = 3;
8080 ImGui::SameLine(); HelpMarker(desc: "Item won't be cycled through when using TAB or Shift+Tab.");
8081 ImGui::PopItemFlag();
8082
8083 if (has_focus)
8084 ImGui::Text(fmt: "Item with focus: %d", has_focus);
8085 else
8086 ImGui::Text(fmt: "Item with focus: <none>");
8087
8088 // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item
8089 static float f3[3] = { 0.0f, 0.0f, 0.0f };
8090 int focus_ahead = -1;
8091 if (ImGui::Button(label: "Focus on X")) { focus_ahead = 0; } ImGui::SameLine();
8092 if (ImGui::Button(label: "Focus on Y")) { focus_ahead = 1; } ImGui::SameLine();
8093 if (ImGui::Button(label: "Focus on Z")) { focus_ahead = 2; }
8094 if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead);
8095 ImGui::SliderFloat3(label: "Float3", v: &f3[0], v_min: 0.0f, v_max: 1.0f);
8096
8097 ImGui::TextWrapped(fmt: "NB: Cursor & selection are preserved when refocusing last used item in code.");
8098 ImGui::TreePop();
8099 }
8100
8101 IMGUI_DEMO_MARKER("Inputs & Focus/Dragging");
8102 if (ImGui::TreeNode(label: "Dragging"))
8103 {
8104 ImGui::TextWrapped(fmt: "You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");
8105 for (int button = 0; button < 3; button++)
8106 {
8107 ImGui::Text(fmt: "IsMouseDragging(%d):", button);
8108 ImGui::Text(fmt: " w/ default threshold: %d,", ImGui::IsMouseDragging(button));
8109 ImGui::Text(fmt: " w/ zero threshold: %d,", ImGui::IsMouseDragging(button, lock_threshold: 0.0f));
8110 ImGui::Text(fmt: " w/ large threshold: %d,", ImGui::IsMouseDragging(button, lock_threshold: 20.0f));
8111 }
8112
8113 ImGui::Button(label: "Drag Me");
8114 if (ImGui::IsItemActive())
8115 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
8116
8117 // Drag operations gets "unlocked" when the mouse has moved past a certain threshold
8118 // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
8119 // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
8120 ImVec2 value_raw = ImGui::GetMouseDragDelta(button: 0, lock_threshold: 0.0f);
8121 ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(button: 0);
8122 ImVec2 mouse_delta = io.MouseDelta;
8123 ImGui::Text(fmt: "GetMouseDragDelta(0):");
8124 ImGui::Text(fmt: " w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y);
8125 ImGui::Text(fmt: " w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y);
8126 ImGui::Text(fmt: "io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y);
8127 ImGui::TreePop();
8128 }
8129 }
8130}
8131
8132//-----------------------------------------------------------------------------
8133// [SECTION] About Window / ShowAboutWindow()
8134// Access from Dear ImGui Demo -> Tools -> About
8135//-----------------------------------------------------------------------------
8136
8137void ImGui::ShowAboutWindow(bool* p_open)
8138{
8139 if (!ImGui::Begin(name: "About Dear ImGui", p_open, flags: ImGuiWindowFlags_AlwaysAutoResize))
8140 {
8141 ImGui::End();
8142 return;
8143 }
8144 IMGUI_DEMO_MARKER("Tools/About Dear ImGui");
8145 ImGui::Text(fmt: "Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8146
8147 ImGui::TextLinkOpenURL(label: "Homepage", url: "https://github.com/ocornut/imgui");
8148 ImGui::SameLine();
8149 ImGui::TextLinkOpenURL(label: "FAQ", url: "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md");
8150 ImGui::SameLine();
8151 ImGui::TextLinkOpenURL(label: "Wiki", url: "https://github.com/ocornut/imgui/wiki");
8152 ImGui::SameLine();
8153 ImGui::TextLinkOpenURL(label: "Extensions", url: "https://github.com/ocornut/imgui/wiki/Useful-Extensions");
8154 ImGui::SameLine();
8155 ImGui::TextLinkOpenURL(label: "Releases", url: "https://github.com/ocornut/imgui/releases");
8156 ImGui::SameLine();
8157 ImGui::TextLinkOpenURL(label: "Funding", url: "https://github.com/ocornut/imgui/wiki/Funding");
8158
8159 ImGui::Separator();
8160 ImGui::Text(fmt: "(c) 2014-2025 Omar Cornut");
8161 ImGui::Text(fmt: "Developed by Omar Cornut and all Dear ImGui contributors.");
8162 ImGui::Text(fmt: "Dear ImGui is licensed under the MIT License, see LICENSE for more information.");
8163 ImGui::Text(fmt: "If your company uses this, please consider funding the project.");
8164
8165 static bool show_config_info = false;
8166 ImGui::Checkbox(label: "Config/Build Information", v: &show_config_info);
8167 if (show_config_info)
8168 {
8169 ImGuiIO& io = ImGui::GetIO();
8170 ImGuiStyle& style = ImGui::GetStyle();
8171
8172 bool copy_to_clipboard = ImGui::Button(label: "Copy to clipboard");
8173 ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);
8174 ImGui::BeginChild(id: ImGui::GetID(str_id: "cfg_infos"), size: child_size, child_flags: ImGuiChildFlags_FrameStyle);
8175 if (copy_to_clipboard)
8176 {
8177 ImGui::LogToClipboard();
8178 ImGui::LogText(fmt: "```cpp\n"); // Back quotes will make text appears without formatting when pasting on GitHub
8179 }
8180
8181 ImGui::Text(fmt: "Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8182 ImGui::Separator();
8183 ImGui::Text(fmt: "sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));
8184 ImGui::Text(fmt: "define: __cplusplus=%d", (int)__cplusplus);
8185#ifdef IMGUI_ENABLE_TEST_ENGINE
8186 ImGui::Text("define: IMGUI_ENABLE_TEST_ENGINE");
8187#endif
8188#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8189 ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
8190#endif
8191#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
8192 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
8193#endif
8194#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
8195 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS");
8196#endif
8197#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS
8198 ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS");
8199#endif
8200#ifdef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
8201 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS");
8202#endif
8203#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
8204 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS");
8205#endif
8206#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
8207 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS");
8208#endif
8209#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
8210 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS");
8211#endif
8212#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
8213 ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS");
8214#endif
8215#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS
8216 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS");
8217#endif
8218#ifdef IMGUI_USE_BGRA_PACKED_COLOR
8219 ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR");
8220#endif
8221#ifdef _WIN32
8222 ImGui::Text("define: _WIN32");
8223#endif
8224#ifdef _WIN64
8225 ImGui::Text("define: _WIN64");
8226#endif
8227#ifdef __linux__
8228 ImGui::Text(fmt: "define: __linux__");
8229#endif
8230#ifdef __APPLE__
8231 ImGui::Text("define: __APPLE__");
8232#endif
8233#ifdef _MSC_VER
8234 ImGui::Text("define: _MSC_VER=%d", _MSC_VER);
8235#endif
8236#ifdef _MSVC_LANG
8237 ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG);
8238#endif
8239#ifdef __MINGW32__
8240 ImGui::Text("define: __MINGW32__");
8241#endif
8242#ifdef __MINGW64__
8243 ImGui::Text("define: __MINGW64__");
8244#endif
8245#ifdef __GNUC__
8246 ImGui::Text(fmt: "define: __GNUC__=%d", (int)__GNUC__);
8247#endif
8248#ifdef __clang_version__
8249 ImGui::Text(fmt: "define: __clang_version__=%s", __clang_version__);
8250#endif
8251#ifdef __EMSCRIPTEN__
8252 ImGui::Text("define: __EMSCRIPTEN__");
8253 ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
8254#endif
8255#ifdef IMGUI_HAS_VIEWPORT
8256 ImGui::Text(fmt: "define: IMGUI_HAS_VIEWPORT");
8257#endif
8258#ifdef IMGUI_HAS_DOCK
8259 ImGui::Text(fmt: "define: IMGUI_HAS_DOCK");
8260#endif
8261 ImGui::Separator();
8262 ImGui::Text(fmt: "io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
8263 ImGui::Text(fmt: "io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
8264 ImGui::Text(fmt: "io.ConfigFlags: 0x%08X", io.ConfigFlags);
8265 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(fmt: " NavEnableKeyboard");
8266 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(fmt: " NavEnableGamepad");
8267 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(fmt: " NoMouse");
8268 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(fmt: " NoMouseCursorChange");
8269 if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) ImGui::Text(fmt: " NoKeyboard");
8270 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) ImGui::Text(fmt: " DockingEnable");
8271 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(fmt: " ViewportsEnable");
8272 if (io.MouseDrawCursor) ImGui::Text(fmt: "io.MouseDrawCursor");
8273 if (io.ConfigDpiScaleFonts) ImGui::Text(fmt: "io.ConfigDpiScaleFonts");
8274 if (io.ConfigDpiScaleViewports) ImGui::Text(fmt: "io.ConfigDpiScaleViewports");
8275 if (io.ConfigViewportsNoAutoMerge) ImGui::Text(fmt: "io.ConfigViewportsNoAutoMerge");
8276 if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text(fmt: "io.ConfigViewportsNoTaskBarIcon");
8277 if (io.ConfigViewportsNoDecoration) ImGui::Text(fmt: "io.ConfigViewportsNoDecoration");
8278 if (io.ConfigViewportsNoDefaultParent) ImGui::Text(fmt: "io.ConfigViewportsNoDefaultParent");
8279 if (io.ConfigDockingNoSplit) ImGui::Text(fmt: "io.ConfigDockingNoSplit");
8280 if (io.ConfigDockingWithShift) ImGui::Text(fmt: "io.ConfigDockingWithShift");
8281 if (io.ConfigDockingAlwaysTabBar) ImGui::Text(fmt: "io.ConfigDockingAlwaysTabBar");
8282 if (io.ConfigDockingTransparentPayload) ImGui::Text(fmt: "io.ConfigDockingTransparentPayload");
8283 if (io.ConfigMacOSXBehaviors) ImGui::Text(fmt: "io.ConfigMacOSXBehaviors");
8284 if (io.ConfigNavMoveSetMousePos) ImGui::Text(fmt: "io.ConfigNavMoveSetMousePos");
8285 if (io.ConfigNavCaptureKeyboard) ImGui::Text(fmt: "io.ConfigNavCaptureKeyboard");
8286 if (io.ConfigInputTextCursorBlink) ImGui::Text(fmt: "io.ConfigInputTextCursorBlink");
8287 if (io.ConfigWindowsResizeFromEdges) ImGui::Text(fmt: "io.ConfigWindowsResizeFromEdges");
8288 if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text(fmt: "io.ConfigWindowsMoveFromTitleBarOnly");
8289 if (io.ConfigMemoryCompactTimer >= 0.0f) ImGui::Text(fmt: "io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer);
8290 ImGui::Text(fmt: "io.BackendFlags: 0x%08X", io.BackendFlags);
8291 if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(fmt: " HasGamepad");
8292 if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(fmt: " HasMouseCursors");
8293 if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(fmt: " HasSetMousePos");
8294 if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) ImGui::Text(fmt: " PlatformHasViewports");
8295 if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(fmt: " HasMouseHoveredViewport");
8296 if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(fmt: " RendererHasVtxOffset");
8297 if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) ImGui::Text(fmt: " RendererHasTextures");
8298 if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports) ImGui::Text(fmt: " RendererHasViewports");
8299 ImGui::Separator();
8300 ImGui::Text(fmt: "io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height);
8301 ImGui::Text(fmt: "io.Fonts->FontLoaderName: %s", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL");
8302 ImGui::Text(fmt: "io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
8303 ImGui::Text(fmt: "io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
8304 ImGui::Separator();
8305 ImGui::Text(fmt: "style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y);
8306 ImGui::Text(fmt: "style.WindowBorderSize: %.2f", style.WindowBorderSize);
8307 ImGui::Text(fmt: "style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y);
8308 ImGui::Text(fmt: "style.FrameRounding: %.2f", style.FrameRounding);
8309 ImGui::Text(fmt: "style.FrameBorderSize: %.2f", style.FrameBorderSize);
8310 ImGui::Text(fmt: "style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y);
8311 ImGui::Text(fmt: "style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);
8312
8313 if (copy_to_clipboard)
8314 {
8315 ImGui::LogText(fmt: "\n```\n");
8316 ImGui::LogFinish();
8317 }
8318 ImGui::EndChild();
8319 }
8320 ImGui::End();
8321}
8322
8323//-----------------------------------------------------------------------------
8324// [SECTION] Style Editor / ShowStyleEditor()
8325//-----------------------------------------------------------------------------
8326// - ShowStyleSelector()
8327// - ShowStyleEditor()
8328//-----------------------------------------------------------------------------
8329
8330// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
8331// Here we use the simplified Combo() api that packs items into a single literal string.
8332// Useful for quick combo boxes where the choices are known locally.
8333bool ImGui::ShowStyleSelector(const char* label)
8334{
8335 static int style_idx = -1;
8336 if (ImGui::Combo(label, current_item: &style_idx, items_separated_by_zeros: "Dark\0Light\0Classic\0"))
8337 {
8338 switch (style_idx)
8339 {
8340 case 0: ImGui::StyleColorsDark(); break;
8341 case 1: ImGui::StyleColorsLight(); break;
8342 case 2: ImGui::StyleColorsClassic(); break;
8343 }
8344 return true;
8345 }
8346 return false;
8347}
8348
8349static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
8350{
8351 if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return "DrawLinesNone";
8352 if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return "DrawLinesFull";
8353 if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return "DrawLinesToNodes";
8354 return "";
8355}
8356
8357// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8358void ImGui::ShowStyleEditor(ImGuiStyle* ref)
8359{
8360 IMGUI_DEMO_MARKER("Tools/Style Editor");
8361 // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
8362 // (without a reference style pointer, we will use one compared locally as a reference)
8363 ImGuiStyle& style = GetStyle();
8364 static ImGuiStyle ref_saved_style;
8365
8366 // Default to using internal storage as reference
8367 static bool init = true;
8368 if (init && ref == NULL)
8369 ref_saved_style = style;
8370 init = false;
8371 if (ref == NULL)
8372 ref = &ref_saved_style;
8373
8374 PushItemWidth(item_width: GetWindowWidth() * 0.50f);
8375
8376 {
8377 // General
8378 SeparatorText(label: "General");
8379 if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
8380 {
8381 BulletText(fmt: "Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
8382 BulletText(fmt: "For instructions, see:");
8383 SameLine();
8384 TextLinkOpenURL(label: "docs/BACKENDS.md", url: "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md");
8385 }
8386
8387 if (ShowStyleSelector(label: "Colors##Selector"))
8388 ref_saved_style = style;
8389 ShowFontSelector(label: "Fonts##Selector");
8390 if (DragFloat(label: "FontSizeBase", v: &style.FontSizeBase, v_speed: 0.20f, v_min: 5.0f, v_max: 100.0f, format: "%.0f"))
8391 style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
8392 SameLine(offset_from_start_x: 0.0f, spacing: 0.0f); Text(fmt: " (out %.2f)", GetFontSize());
8393 DragFloat(label: "FontScaleMain", v: &style.FontScaleMain, v_speed: 0.02f, v_min: 0.5f, v_max: 4.0f);
8394 //BeginDisabled(GetIO().ConfigDpiScaleFonts);
8395 DragFloat(label: "FontScaleDpi", v: &style.FontScaleDpi, v_speed: 0.02f, v_min: 0.5f, v_max: 4.0f);
8396 //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
8397 //EndDisabled();
8398
8399 // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
8400 if (SliderFloat(label: "FrameRounding", v: &style.FrameRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f"))
8401 style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
8402 { bool border = (style.WindowBorderSize > 0.0f); if (Checkbox(label: "WindowBorder", v: &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
8403 SameLine();
8404 { bool border = (style.FrameBorderSize > 0.0f); if (Checkbox(label: "FrameBorder", v: &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
8405 SameLine();
8406 { bool border = (style.PopupBorderSize > 0.0f); if (Checkbox(label: "PopupBorder", v: &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
8407 }
8408
8409 // Save/Revert button
8410 if (Button(label: "Save Ref"))
8411 *ref = ref_saved_style = style;
8412 SameLine();
8413 if (Button(label: "Revert Ref"))
8414 style = *ref;
8415 SameLine();
8416 HelpMarker(
8417 desc: "Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
8418 "Use \"Export\" below to save them somewhere.");
8419
8420 SeparatorText(label: "Details");
8421 if (BeginTabBar(str_id: "##tabs", flags: ImGuiTabBarFlags_None))
8422 {
8423 if (BeginTabItem(label: "Sizes"))
8424 {
8425 SeparatorText(label: "Main");
8426 SliderFloat2(label: "WindowPadding", v: (float*)&style.WindowPadding, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8427 SliderFloat2(label: "FramePadding", v: (float*)&style.FramePadding, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8428 SliderFloat2(label: "ItemSpacing", v: (float*)&style.ItemSpacing, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8429 SliderFloat2(label: "ItemInnerSpacing", v: (float*)&style.ItemInnerSpacing, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8430 SliderFloat2(label: "TouchExtraPadding", v: (float*)&style.TouchExtraPadding, v_min: 0.0f, v_max: 10.0f, format: "%.0f");
8431 SliderFloat(label: "IndentSpacing", v: &style.IndentSpacing, v_min: 0.0f, v_max: 30.0f, format: "%.0f");
8432 SliderFloat(label: "ScrollbarSize", v: &style.ScrollbarSize, v_min: 1.0f, v_max: 20.0f, format: "%.0f");
8433 SliderFloat(label: "GrabMinSize", v: &style.GrabMinSize, v_min: 1.0f, v_max: 20.0f, format: "%.0f");
8434
8435 SeparatorText(label: "Borders");
8436 SliderFloat(label: "WindowBorderSize", v: &style.WindowBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8437 SliderFloat(label: "ChildBorderSize", v: &style.ChildBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8438 SliderFloat(label: "PopupBorderSize", v: &style.PopupBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8439 SliderFloat(label: "FrameBorderSize", v: &style.FrameBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8440
8441 SeparatorText(label: "Rounding");
8442 SliderFloat(label: "WindowRounding", v: &style.WindowRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8443 SliderFloat(label: "ChildRounding", v: &style.ChildRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8444 SliderFloat(label: "FrameRounding", v: &style.FrameRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8445 SliderFloat(label: "PopupRounding", v: &style.PopupRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8446 SliderFloat(label: "ScrollbarRounding", v: &style.ScrollbarRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8447 SliderFloat(label: "GrabRounding", v: &style.GrabRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8448
8449 SeparatorText(label: "Tabs");
8450 SliderFloat(label: "TabBorderSize", v: &style.TabBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8451 SliderFloat(label: "TabBarBorderSize", v: &style.TabBarBorderSize, v_min: 0.0f, v_max: 2.0f, format: "%.0f");
8452 SliderFloat(label: "TabBarOverlineSize", v: &style.TabBarOverlineSize, v_min: 0.0f, v_max: 3.0f, format: "%.0f");
8453 SameLine(); HelpMarker(desc: "Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
8454 DragFloat(label: "TabMinWidthBase", v: &style.TabMinWidthBase, v_speed: 0.5f, v_min: 1.0f, v_max: 500.0f, format: "%.0f");
8455 DragFloat(label: "TabMinWidthShrink", v: &style.TabMinWidthShrink, v_speed: 0.5f, v_min: 1.0f, v_max: 500.0f, format: "%0.f");
8456 DragFloat(label: "TabCloseButtonMinWidthSelected", v: &style.TabCloseButtonMinWidthSelected, v_speed: 0.5f, v_min: -1.0f, v_max: 100.0f, format: (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
8457 DragFloat(label: "TabCloseButtonMinWidthUnselected", v: &style.TabCloseButtonMinWidthUnselected, v_speed: 0.5f, v_min: -1.0f, v_max: 100.0f, format: (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
8458 SliderFloat(label: "TabRounding", v: &style.TabRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8459
8460 SeparatorText(label: "Tables");
8461 SliderFloat2(label: "CellPadding", v: (float*)&style.CellPadding, v_min: 0.0f, v_max: 20.0f, format: "%.0f");
8462 SliderAngle(label: "TableAngledHeadersAngle", v_rad: &style.TableAngledHeadersAngle, v_degrees_min: -50.0f, v_degrees_max: +50.0f);
8463 SliderFloat2(label: "TableAngledHeadersTextAlign", v: (float*)&style.TableAngledHeadersTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8464
8465 SeparatorText(label: "Trees");
8466 bool combo_open = BeginCombo(label: "TreeLinesFlags", preview_value: GetTreeLinesFlagsName(flags: style.TreeLinesFlags));
8467 SameLine();
8468 HelpMarker(desc: "[Experimental] Tree lines may not work in all situations (e.g. using a clipper) and may incurs slight traversal overhead.\n\nImGuiTreeNodeFlags_DrawLinesFull is faster than ImGuiTreeNodeFlags_DrawLinesToNode.");
8469 if (combo_open)
8470 {
8471 const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes };
8472 for (ImGuiTreeNodeFlags option : options)
8473 if (Selectable(label: GetTreeLinesFlagsName(flags: option), selected: style.TreeLinesFlags == option))
8474 style.TreeLinesFlags = option;
8475 EndCombo();
8476 }
8477 SliderFloat(label: "TreeLinesSize", v: &style.TreeLinesSize, v_min: 0.0f, v_max: 2.0f, format: "%.0f");
8478 SliderFloat(label: "TreeLinesRounding", v: &style.TreeLinesRounding, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8479
8480 SeparatorText(label: "Windows");
8481 SliderFloat2(label: "WindowTitleAlign", v: (float*)&style.WindowTitleAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8482 SliderFloat(label: "WindowBorderHoverPadding", v: &style.WindowBorderHoverPadding, v_min: 1.0f, v_max: 20.0f, format: "%.0f");
8483 int window_menu_button_position = style.WindowMenuButtonPosition + 1;
8484 if (Combo(label: "WindowMenuButtonPosition", current_item: (int*)&window_menu_button_position, items_separated_by_zeros: "None\0Left\0Right\0"))
8485 style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);
8486
8487 SeparatorText(label: "Widgets");
8488 Combo(label: "ColorButtonPosition", current_item: (int*)&style.ColorButtonPosition, items_separated_by_zeros: "Left\0Right\0");
8489 SliderFloat2(label: "ButtonTextAlign", v: (float*)&style.ButtonTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8490 SameLine(); HelpMarker(desc: "Alignment applies when a button is larger than its text content.");
8491 SliderFloat2(label: "SelectableTextAlign", v: (float*)&style.SelectableTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8492 SameLine(); HelpMarker(desc: "Alignment applies when a selectable is larger than its text content.");
8493 SliderFloat(label: "SeparatorTextBorderSize", v: &style.SeparatorTextBorderSize, v_min: 0.0f, v_max: 10.0f, format: "%.0f");
8494 SliderFloat2(label: "SeparatorTextAlign", v: (float*)&style.SeparatorTextAlign, v_min: 0.0f, v_max: 1.0f, format: "%.2f");
8495 SliderFloat2(label: "SeparatorTextPadding", v: (float*)&style.SeparatorTextPadding, v_min: 0.0f, v_max: 40.0f, format: "%.0f");
8496 SliderFloat(label: "LogSliderDeadzone", v: &style.LogSliderDeadzone, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8497 SliderFloat(label: "ImageBorderSize", v: &style.ImageBorderSize, v_min: 0.0f, v_max: 1.0f, format: "%.0f");
8498
8499 SeparatorText(label: "Docking");
8500 SliderFloat(label: "DockingSeparatorSize", v: &style.DockingSeparatorSize, v_min: 0.0f, v_max: 12.0f, format: "%.0f");
8501
8502 SeparatorText(label: "Tooltips");
8503 for (int n = 0; n < 2; n++)
8504 if (TreeNodeEx(label: n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav"))
8505 {
8506 ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;
8507 CheckboxFlags(label: "ImGuiHoveredFlags_DelayNone", flags: p, flags_value: ImGuiHoveredFlags_DelayNone);
8508 CheckboxFlags(label: "ImGuiHoveredFlags_DelayShort", flags: p, flags_value: ImGuiHoveredFlags_DelayShort);
8509 CheckboxFlags(label: "ImGuiHoveredFlags_DelayNormal", flags: p, flags_value: ImGuiHoveredFlags_DelayNormal);
8510 CheckboxFlags(label: "ImGuiHoveredFlags_Stationary", flags: p, flags_value: ImGuiHoveredFlags_Stationary);
8511 CheckboxFlags(label: "ImGuiHoveredFlags_NoSharedDelay", flags: p, flags_value: ImGuiHoveredFlags_NoSharedDelay);
8512 TreePop();
8513 }
8514
8515 SeparatorText(label: "Misc");
8516 SliderFloat2(label: "DisplayWindowPadding", v: (float*)&style.DisplayWindowPadding, v_min: 0.0f, v_max: 30.0f, format: "%.0f"); SameLine(); HelpMarker(desc: "Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.");
8517 SliderFloat2(label: "DisplaySafeAreaPadding", v: (float*)&style.DisplaySafeAreaPadding, v_min: 0.0f, v_max: 30.0f, format: "%.0f"); 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).");
8518
8519 EndTabItem();
8520 }
8521
8522 if (BeginTabItem(label: "Colors"))
8523 {
8524 static int output_dest = 0;
8525 static bool output_only_modified = true;
8526 if (Button(label: "Export"))
8527 {
8528 if (output_dest == 0)
8529 LogToClipboard();
8530 else
8531 LogToTTY();
8532 LogText(fmt: "ImVec4* colors = GetStyle().Colors;" IM_NEWLINE);
8533 for (int i = 0; i < ImGuiCol_COUNT; i++)
8534 {
8535 const ImVec4& col = style.Colors[i];
8536 const char* name = GetStyleColorName(idx: i);
8537 if (!output_only_modified || memcmp(s1: &col, s2: &ref->Colors[i], n: sizeof(ImVec4)) != 0)
8538 LogText(fmt: "colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
8539 name, 23 - (int)strlen(s: name), "", col.x, col.y, col.z, col.w);
8540 }
8541 LogFinish();
8542 }
8543 SameLine(); SetNextItemWidth(120); Combo(label: "##output_type", current_item: &output_dest, items_separated_by_zeros: "To Clipboard\0To TTY\0");
8544 SameLine(); Checkbox(label: "Only Modified Colors", v: &output_only_modified);
8545
8546 static ImGuiTextFilter filter;
8547 filter.Draw(label: "Filter colors", width: GetFontSize() * 16);
8548
8549 static ImGuiColorEditFlags alpha_flags = 0;
8550 if (RadioButton(label: "Opaque", active: alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine();
8551 if (RadioButton(label: "Alpha", active: alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } SameLine();
8552 if (RadioButton(label: "Both", active: alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } SameLine();
8553 HelpMarker(
8554 desc: "In the color list:\n"
8555 "Left-click on color square to open color picker,\n"
8556 "Right-click to open edit options menu.");
8557
8558 SetNextWindowSizeConstraints(size_min: ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), size_max: ImVec2(FLT_MAX, FLT_MAX));
8559 BeginChild(str_id: "##colors", size: ImVec2(0, 0), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, window_flags: ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
8560 PushItemWidth(item_width: GetFontSize() * -12);
8561 for (int i = 0; i < ImGuiCol_COUNT; i++)
8562 {
8563 const char* name = GetStyleColorName(idx: i);
8564 if (!filter.PassFilter(text: name))
8565 continue;
8566 PushID(int_id: i);
8567#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8568 if (Button(label: "?"))
8569 DebugFlashStyleColor(idx: (ImGuiCol)i);
8570 SetItemTooltip("Flash given color to identify places where it is used.");
8571 SameLine();
8572#endif
8573 ColorEdit4(label: "##color", col: (float*)&style.Colors[i], flags: ImGuiColorEditFlags_AlphaBar | alpha_flags);
8574 if (memcmp(s1: &style.Colors[i], s2: &ref->Colors[i], n: sizeof(ImVec4)) != 0)
8575 {
8576 // Tips: in a real user application, you may want to merge and use an icon font into the main font,
8577 // so instead of "Save"/"Revert" you'd use icons!
8578 // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
8579 SameLine(offset_from_start_x: 0.0f, spacing: style.ItemInnerSpacing.x); if (Button(label: "Save")) { ref->Colors[i] = style.Colors[i]; }
8580 SameLine(offset_from_start_x: 0.0f, spacing: style.ItemInnerSpacing.x); if (Button(label: "Revert")) { style.Colors[i] = ref->Colors[i]; }
8581 }
8582 SameLine(offset_from_start_x: 0.0f, spacing: style.ItemInnerSpacing.x);
8583 TextUnformatted(text: name);
8584 PopID();
8585 }
8586 PopItemWidth();
8587 EndChild();
8588
8589 EndTabItem();
8590 }
8591
8592 if (BeginTabItem(label: "Fonts"))
8593 {
8594 ImGuiIO& io = GetIO();
8595 ImFontAtlas* atlas = io.Fonts;
8596 ShowFontAtlas(atlas);
8597
8598 // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
8599 // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
8600 /*
8601 SeparatorText("Legacy Scaling");
8602 const float MIN_SCALE = 0.3f;
8603 const float MAX_SCALE = 2.0f;
8604 HelpMarker(
8605 "Those are old settings provided for convenience.\n"
8606 "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
8607 "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
8608 "Using those settings here will give you poor quality results.");
8609 PushItemWidth(GetFontSize() * 8);
8610 DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
8611 //static float window_scale = 1.0f;
8612 //if (DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
8613 // SetWindowFontScale(window_scale);
8614 PopItemWidth();
8615 */
8616
8617 EndTabItem();
8618 }
8619
8620 if (BeginTabItem(label: "Rendering"))
8621 {
8622 Checkbox(label: "Anti-aliased lines", v: &style.AntiAliasedLines);
8623 SameLine();
8624 HelpMarker(desc: "When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
8625
8626 Checkbox(label: "Anti-aliased lines use texture", v: &style.AntiAliasedLinesUseTex);
8627 SameLine();
8628 HelpMarker(desc: "Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
8629
8630 Checkbox(label: "Anti-aliased fill", v: &style.AntiAliasedFill);
8631 PushItemWidth(item_width: GetFontSize() * 8);
8632 DragFloat(label: "Curve Tessellation Tolerance", v: &style.CurveTessellationTol, v_speed: 0.02f, v_min: 0.10f, v_max: 10.0f, format: "%.2f");
8633 if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
8634
8635 // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
8636 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);
8637 const bool show_samples = IsItemActive();
8638 if (show_samples)
8639 SetNextWindowPos(pos: GetCursorScreenPos());
8640 if (show_samples && BeginTooltip())
8641 {
8642 TextUnformatted(text: "(R = radius, N = approx number of segments)");
8643 Spacing();
8644 ImDrawList* draw_list = GetWindowDrawList();
8645 const float min_widget_width = CalcTextSize(text: "R: MMM\nN: MMM").x;
8646 for (int n = 0; n < 8; n++)
8647 {
8648 const float RAD_MIN = 5.0f;
8649 const float RAD_MAX = 70.0f;
8650 const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
8651
8652 BeginGroup();
8653
8654 // N is not always exact here due to how PathArcTo() function work internally
8655 Text(fmt: "R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(radius: rad));
8656
8657 const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
8658 const float offset_x = floorf(x: canvas_width * 0.5f);
8659 const float offset_y = floorf(x: RAD_MAX);
8660
8661 const ImVec2 p1 = GetCursorScreenPos();
8662 draw_list->AddCircle(center: ImVec2(p1.x + offset_x, p1.y + offset_y), radius: rad, col: GetColorU32(idx: ImGuiCol_Text));
8663 Dummy(size: ImVec2(canvas_width, RAD_MAX * 2));
8664
8665 /*
8666 const ImVec2 p2 = GetCursorScreenPos();
8667 draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
8668 Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8669 */
8670
8671 EndGroup();
8672 SameLine();
8673 }
8674 EndTooltip();
8675 }
8676 SameLine();
8677 HelpMarker(desc: "When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically.");
8678
8679 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.
8680 DragFloat(label: "Disabled Alpha", v: &style.DisabledAlpha, v_speed: 0.005f, v_min: 0.0f, v_max: 1.0f, format: "%.2f"); SameLine(); HelpMarker(desc: "Additional alpha multiplier for disabled items (multiply over current value of Alpha).");
8681 PopItemWidth();
8682
8683 EndTabItem();
8684 }
8685
8686 EndTabBar();
8687 }
8688 PopItemWidth();
8689}
8690
8691//-----------------------------------------------------------------------------
8692// [SECTION] User Guide / ShowUserGuide()
8693//-----------------------------------------------------------------------------
8694
8695// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8696void ImGui::ShowUserGuide()
8697{
8698 ImGuiIO& io = GetIO();
8699 BulletText(fmt: "Double-click on title bar to collapse window.");
8700 BulletText(
8701 fmt: "Click and drag on lower corner to resize window\n"
8702 "(double-click to auto fit window to its contents).");
8703 BulletText(fmt: "CTRL+Click on a slider or drag box to input value as text.");
8704 BulletText(fmt: "TAB/SHIFT+TAB to cycle through keyboard editable fields.");
8705 BulletText(fmt: "CTRL+Tab to select a window.");
8706 if (io.FontAllowUserScaling)
8707 BulletText(fmt: "CTRL+Mouse Wheel to zoom window contents.");
8708 BulletText(fmt: "While inputting text:\n");
8709 Indent();
8710 BulletText(fmt: "CTRL+Left/Right to word jump.");
8711 BulletText(fmt: "CTRL+A or double-click to select all.");
8712 BulletText(fmt: "CTRL+X/C/V to use clipboard cut/copy/paste.");
8713 BulletText(fmt: "CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.");
8714 BulletText(fmt: "ESCAPE to revert.");
8715 Unindent();
8716 BulletText(fmt: "With keyboard navigation enabled:");
8717 Indent();
8718 BulletText(fmt: "Arrow keys to navigate.");
8719 BulletText(fmt: "Space to activate a widget.");
8720 BulletText(fmt: "Return to input text into a widget.");
8721 BulletText(fmt: "Escape to deactivate a widget, close popup, exit child window.");
8722 BulletText(fmt: "Alt to jump to the menu layer of a window.");
8723 Unindent();
8724}
8725
8726//-----------------------------------------------------------------------------
8727// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
8728//-----------------------------------------------------------------------------
8729// - ShowExampleAppMainMenuBar()
8730// - ShowExampleMenuFile()
8731//-----------------------------------------------------------------------------
8732
8733// Demonstrate creating a "main" fullscreen menu bar and populating it.
8734// Note the difference between BeginMainMenuBar() and BeginMenuBar():
8735// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)
8736// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.
8737static void ShowExampleAppMainMenuBar()
8738{
8739 if (ImGui::BeginMainMenuBar())
8740 {
8741 if (ImGui::BeginMenu(label: "File"))
8742 {
8743 ShowExampleMenuFile();
8744 ImGui::EndMenu();
8745 }
8746 if (ImGui::BeginMenu(label: "Edit"))
8747 {
8748 if (ImGui::MenuItem(label: "Undo", shortcut: "CTRL+Z")) {}
8749 if (ImGui::MenuItem(label: "Redo", shortcut: "CTRL+Y", selected: false, enabled: false)) {} // Disabled item
8750 ImGui::Separator();
8751 if (ImGui::MenuItem(label: "Cut", shortcut: "CTRL+X")) {}
8752 if (ImGui::MenuItem(label: "Copy", shortcut: "CTRL+C")) {}
8753 if (ImGui::MenuItem(label: "Paste", shortcut: "CTRL+V")) {}
8754 ImGui::EndMenu();
8755 }
8756 ImGui::EndMainMenuBar();
8757 }
8758}
8759
8760// Note that shortcuts are currently provided for display only
8761// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
8762static void ShowExampleMenuFile()
8763{
8764 IMGUI_DEMO_MARKER("Examples/Menu");
8765 ImGui::MenuItem(label: "(demo menu)", NULL, selected: false, enabled: false);
8766 if (ImGui::MenuItem(label: "New")) {}
8767 if (ImGui::MenuItem(label: "Open", shortcut: "Ctrl+O")) {}
8768 if (ImGui::BeginMenu(label: "Open Recent"))
8769 {
8770 ImGui::MenuItem(label: "fish_hat.c");
8771 ImGui::MenuItem(label: "fish_hat.inl");
8772 ImGui::MenuItem(label: "fish_hat.h");
8773 if (ImGui::BeginMenu(label: "More.."))
8774 {
8775 ImGui::MenuItem(label: "Hello");
8776 ImGui::MenuItem(label: "Sailor");
8777 if (ImGui::BeginMenu(label: "Recurse.."))
8778 {
8779 ShowExampleMenuFile();
8780 ImGui::EndMenu();
8781 }
8782 ImGui::EndMenu();
8783 }
8784 ImGui::EndMenu();
8785 }
8786 if (ImGui::MenuItem(label: "Save", shortcut: "Ctrl+S")) {}
8787 if (ImGui::MenuItem(label: "Save As..")) {}
8788
8789 ImGui::Separator();
8790 IMGUI_DEMO_MARKER("Examples/Menu/Options");
8791 if (ImGui::BeginMenu(label: "Options"))
8792 {
8793 static bool enabled = true;
8794 ImGui::MenuItem(label: "Enabled", shortcut: "", p_selected: &enabled);
8795 ImGui::BeginChild(str_id: "child", size: ImVec2(0, 60), child_flags: ImGuiChildFlags_Borders);
8796 for (int i = 0; i < 10; i++)
8797 ImGui::Text(fmt: "Scrolling Text %d", i);
8798 ImGui::EndChild();
8799 static float f = 0.5f;
8800 static int n = 0;
8801 ImGui::SliderFloat(label: "Value", v: &f, v_min: 0.0f, v_max: 1.0f);
8802 ImGui::InputFloat(label: "Input", v: &f, step: 0.1f);
8803 ImGui::Combo(label: "Combo", current_item: &n, items_separated_by_zeros: "Yes\0No\0Maybe\0\0");
8804 ImGui::EndMenu();
8805 }
8806
8807 IMGUI_DEMO_MARKER("Examples/Menu/Colors");
8808 if (ImGui::BeginMenu(label: "Colors"))
8809 {
8810 float sz = ImGui::GetTextLineHeight();
8811 for (int i = 0; i < ImGuiCol_COUNT; i++)
8812 {
8813 const char* name = ImGui::GetStyleColorName(idx: (ImGuiCol)i);
8814 ImVec2 p = ImGui::GetCursorScreenPos();
8815 ImGui::GetWindowDrawList()->AddRectFilled(p_min: p, p_max: ImVec2(p.x + sz, p.y + sz), col: ImGui::GetColorU32(idx: (ImGuiCol)i));
8816 ImGui::Dummy(size: ImVec2(sz, sz));
8817 ImGui::SameLine();
8818 ImGui::MenuItem(label: name);
8819 }
8820 ImGui::EndMenu();
8821 }
8822
8823 // Here we demonstrate appending again to the "Options" menu (which we already created above)
8824 // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice.
8825 // In a real code-base using it would make senses to use this feature from very different code locations.
8826 if (ImGui::BeginMenu(label: "Options")) // <-- Append!
8827 {
8828 IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu");
8829 static bool b = true;
8830 ImGui::Checkbox(label: "SomeOption", v: &b);
8831 ImGui::EndMenu();
8832 }
8833
8834 if (ImGui::BeginMenu(label: "Disabled", enabled: false)) // Disabled
8835 {
8836 IM_ASSERT(0);
8837 }
8838 if (ImGui::MenuItem(label: "Checked", NULL, selected: true)) {}
8839 ImGui::Separator();
8840 if (ImGui::MenuItem(label: "Quit", shortcut: "Alt+F4")) {}
8841}
8842
8843//-----------------------------------------------------------------------------
8844// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
8845//-----------------------------------------------------------------------------
8846
8847// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
8848// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
8849struct ExampleAppConsole
8850{
8851 char InputBuf[256];
8852 ImVector<char*> Items;
8853 ImVector<const char*> Commands;
8854 ImVector<char*> History;
8855 int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
8856 ImGuiTextFilter Filter;
8857 bool AutoScroll;
8858 bool ScrollToBottom;
8859
8860 ExampleAppConsole()
8861 {
8862 IMGUI_DEMO_MARKER("Examples/Console");
8863 ClearLog();
8864 memset(s: InputBuf, c: 0, n: sizeof(InputBuf));
8865 HistoryPos = -1;
8866
8867 // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
8868 Commands.push_back(v: "HELP");
8869 Commands.push_back(v: "HISTORY");
8870 Commands.push_back(v: "CLEAR");
8871 Commands.push_back(v: "CLASSIFY");
8872 AutoScroll = true;
8873 ScrollToBottom = false;
8874 AddLog(fmt: "Welcome to Dear ImGui!");
8875 }
8876 ~ExampleAppConsole()
8877 {
8878 ClearLog();
8879 for (int i = 0; i < History.Size; i++)
8880 ImGui::MemFree(ptr: History[i]);
8881 }
8882
8883 // Portable helpers
8884 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; }
8885 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; }
8886 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); }
8887 static void Strtrim(char* s) { char* str_end = s + strlen(s: s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
8888
8889 void ClearLog()
8890 {
8891 for (int i = 0; i < Items.Size; i++)
8892 ImGui::MemFree(ptr: Items[i]);
8893 Items.clear();
8894 }
8895
8896 void AddLog(const char* fmt, ...) IM_FMTARGS(2)
8897 {
8898 // FIXME-OPT
8899 char buf[1024];
8900 va_list args;
8901 va_start(args, fmt);
8902 vsnprintf(s: buf, IM_ARRAYSIZE(buf), format: fmt, arg: args);
8903 buf[IM_ARRAYSIZE(buf)-1] = 0;
8904 va_end(args);
8905 Items.push_back(v: Strdup(s: buf));
8906 }
8907
8908 void Draw(const char* title, bool* p_open)
8909 {
8910 ImGui::SetNextWindowSize(size: ImVec2(520, 600), cond: ImGuiCond_FirstUseEver);
8911 if (!ImGui::Begin(name: title, p_open))
8912 {
8913 ImGui::End();
8914 return;
8915 }
8916
8917 // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
8918 // So e.g. IsItemHovered() will return true when hovering the title bar.
8919 // Here we create a context menu only available from the title bar.
8920 if (ImGui::BeginPopupContextItem())
8921 {
8922 if (ImGui::MenuItem(label: "Close Console"))
8923 *p_open = false;
8924 ImGui::EndPopup();
8925 }
8926
8927 ImGui::TextWrapped(
8928 fmt: "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate "
8929 "implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
8930 ImGui::TextWrapped(fmt: "Enter 'HELP' for help.");
8931
8932 // TODO: display items starting from the bottom
8933
8934 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!"); }
8935 ImGui::SameLine();
8936 if (ImGui::SmallButton(label: "Add Debug Error")) { AddLog(fmt: "[error] something went wrong"); }
8937 ImGui::SameLine();
8938 if (ImGui::SmallButton(label: "Clear")) { ClearLog(); }
8939 ImGui::SameLine();
8940 bool copy_to_clipboard = ImGui::SmallButton(label: "Copy");
8941 //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
8942
8943 ImGui::Separator();
8944
8945 // Options menu
8946 if (ImGui::BeginPopup(str_id: "Options"))
8947 {
8948 ImGui::Checkbox(label: "Auto-scroll", v: &AutoScroll);
8949 ImGui::EndPopup();
8950 }
8951
8952 // Options, Filter
8953 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_O, flags: ImGuiInputFlags_Tooltip);
8954 if (ImGui::Button(label: "Options"))
8955 ImGui::OpenPopup(str_id: "Options");
8956 ImGui::SameLine();
8957 Filter.Draw(label: "Filter (\"incl,-excl\") (\"error\")", width: 180);
8958 ImGui::Separator();
8959
8960 // Reserve enough left-over height for 1 separator + 1 input text
8961 const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
8962 if (ImGui::BeginChild(str_id: "ScrollingRegion", size: ImVec2(0, -footer_height_to_reserve), child_flags: ImGuiChildFlags_NavFlattened, window_flags: ImGuiWindowFlags_HorizontalScrollbar))
8963 {
8964 if (ImGui::BeginPopupContextWindow())
8965 {
8966 if (ImGui::Selectable(label: "Clear")) ClearLog();
8967 ImGui::EndPopup();
8968 }
8969
8970 // Display every line as a separate entry so we can change their color or add custom widgets.
8971 // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
8972 // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
8973 // to only process visible items. The clipper will automatically measure the height of your first item and then
8974 // "seek" to display only items in the visible area.
8975 // To use the clipper we can replace your standard loop:
8976 // for (int i = 0; i < Items.Size; i++)
8977 // With:
8978 // ImGuiListClipper clipper;
8979 // clipper.Begin(Items.Size);
8980 // while (clipper.Step())
8981 // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8982 // - That your items are evenly spaced (same height)
8983 // - That you have cheap random access to your elements (you can access them given their index,
8984 // without processing all the ones before)
8985 // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
8986 // We would need random-access on the post-filtered list.
8987 // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
8988 // or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
8989 // and appending newly elements as they are inserted. This is left as a task to the user until we can manage
8990 // to improve this example code!
8991 // If your items are of variable height:
8992 // - Split them into same height items would be simpler and facilitate random-seeking into your list.
8993 // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
8994 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(4, 1)); // Tighten spacing
8995 if (copy_to_clipboard)
8996 ImGui::LogToClipboard();
8997 for (const char* item : Items)
8998 {
8999 if (!Filter.PassFilter(text: item))
9000 continue;
9001
9002 // Normally you would store more information in your item than just a string.
9003 // (e.g. make Items[] an array of structure, store color/type etc.)
9004 ImVec4 color;
9005 bool has_color = false;
9006 if (strstr(haystack: item, needle: "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
9007 else if (strncmp(s1: item, s2: "# ", n: 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
9008 if (has_color)
9009 ImGui::PushStyleColor(idx: ImGuiCol_Text, col: color);
9010 ImGui::TextUnformatted(text: item);
9011 if (has_color)
9012 ImGui::PopStyleColor();
9013 }
9014 if (copy_to_clipboard)
9015 ImGui::LogFinish();
9016
9017 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
9018 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
9019 if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
9020 ImGui::SetScrollHereY(1.0f);
9021 ScrollToBottom = false;
9022
9023 ImGui::PopStyleVar();
9024 }
9025 ImGui::EndChild();
9026 ImGui::Separator();
9027
9028 // Command-line
9029 bool reclaim_focus = false;
9030 ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
9031 if (ImGui::InputText(label: "Input", buf: InputBuf, IM_ARRAYSIZE(InputBuf), flags: input_text_flags, callback: &TextEditCallbackStub, user_data: (void*)this))
9032 {
9033 char* s = InputBuf;
9034 Strtrim(s);
9035 if (s[0])
9036 ExecCommand(command_line: s);
9037 strcpy(dest: s, src: "");
9038 reclaim_focus = true;
9039 }
9040
9041 // Auto-focus on window apparition
9042 ImGui::SetItemDefaultFocus();
9043 if (reclaim_focus)
9044 ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
9045
9046 ImGui::End();
9047 }
9048
9049 void ExecCommand(const char* command_line)
9050 {
9051 AddLog(fmt: "# %s\n", command_line);
9052
9053 // Insert into history. First find match and delete it so it can be pushed to the back.
9054 // This isn't trying to be smart or optimal.
9055 HistoryPos = -1;
9056 for (int i = History.Size - 1; i >= 0; i--)
9057 if (Stricmp(s1: History[i], s2: command_line) == 0)
9058 {
9059 ImGui::MemFree(ptr: History[i]);
9060 History.erase(it: History.begin() + i);
9061 break;
9062 }
9063 History.push_back(v: Strdup(s: command_line));
9064
9065 // Process command
9066 if (Stricmp(s1: command_line, s2: "CLEAR") == 0)
9067 {
9068 ClearLog();
9069 }
9070 else if (Stricmp(s1: command_line, s2: "HELP") == 0)
9071 {
9072 AddLog(fmt: "Commands:");
9073 for (int i = 0; i < Commands.Size; i++)
9074 AddLog(fmt: "- %s", Commands[i]);
9075 }
9076 else if (Stricmp(s1: command_line, s2: "HISTORY") == 0)
9077 {
9078 int first = History.Size - 10;
9079 for (int i = first > 0 ? first : 0; i < History.Size; i++)
9080 AddLog(fmt: "%3d: %s\n", i, History[i]);
9081 }
9082 else
9083 {
9084 AddLog(fmt: "Unknown command: '%s'\n", command_line);
9085 }
9086
9087 // On command input, we scroll to bottom even if AutoScroll==false
9088 ScrollToBottom = true;
9089 }
9090
9091 // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
9092 static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)
9093 {
9094 ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;
9095 return console->TextEditCallback(data);
9096 }
9097
9098 int TextEditCallback(ImGuiInputTextCallbackData* data)
9099 {
9100 //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
9101 switch (data->EventFlag)
9102 {
9103 case ImGuiInputTextFlags_CallbackCompletion:
9104 {
9105 // Example of TEXT COMPLETION
9106
9107 // Locate beginning of current word
9108 const char* word_end = data->Buf + data->CursorPos;
9109 const char* word_start = word_end;
9110 while (word_start > data->Buf)
9111 {
9112 const char c = word_start[-1];
9113 if (c == ' ' || c == '\t' || c == ',' || c == ';')
9114 break;
9115 word_start--;
9116 }
9117
9118 // Build a list of candidates
9119 ImVector<const char*> candidates;
9120 for (int i = 0; i < Commands.Size; i++)
9121 if (Strnicmp(s1: Commands[i], s2: word_start, n: (int)(word_end - word_start)) == 0)
9122 candidates.push_back(v: Commands[i]);
9123
9124 if (candidates.Size == 0)
9125 {
9126 // No match
9127 AddLog(fmt: "No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
9128 }
9129 else if (candidates.Size == 1)
9130 {
9131 // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
9132 data->DeleteChars(pos: (int)(word_start - data->Buf), bytes_count: (int)(word_end - word_start));
9133 data->InsertChars(pos: data->CursorPos, text: candidates[0]);
9134 data->InsertChars(pos: data->CursorPos, text: " ");
9135 }
9136 else
9137 {
9138 // Multiple matches. Complete as much as we can..
9139 // So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
9140 int match_len = (int)(word_end - word_start);
9141 for (;;)
9142 {
9143 int c = 0;
9144 bool all_candidates_matches = true;
9145 for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
9146 if (i == 0)
9147 c = toupper(c: candidates[i][match_len]);
9148 else if (c == 0 || c != toupper(c: candidates[i][match_len]))
9149 all_candidates_matches = false;
9150 if (!all_candidates_matches)
9151 break;
9152 match_len++;
9153 }
9154
9155 if (match_len > 0)
9156 {
9157 data->DeleteChars(pos: (int)(word_start - data->Buf), bytes_count: (int)(word_end - word_start));
9158 data->InsertChars(pos: data->CursorPos, text: candidates[0], text_end: candidates[0] + match_len);
9159 }
9160
9161 // List matches
9162 AddLog(fmt: "Possible matches:\n");
9163 for (int i = 0; i < candidates.Size; i++)
9164 AddLog(fmt: "- %s\n", candidates[i]);
9165 }
9166
9167 break;
9168 }
9169 case ImGuiInputTextFlags_CallbackHistory:
9170 {
9171 // Example of HISTORY
9172 const int prev_history_pos = HistoryPos;
9173 if (data->EventKey == ImGuiKey_UpArrow)
9174 {
9175 if (HistoryPos == -1)
9176 HistoryPos = History.Size - 1;
9177 else if (HistoryPos > 0)
9178 HistoryPos--;
9179 }
9180 else if (data->EventKey == ImGuiKey_DownArrow)
9181 {
9182 if (HistoryPos != -1)
9183 if (++HistoryPos >= History.Size)
9184 HistoryPos = -1;
9185 }
9186
9187 // A better implementation would preserve the data on the current input line along with cursor position.
9188 if (prev_history_pos != HistoryPos)
9189 {
9190 const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
9191 data->DeleteChars(pos: 0, bytes_count: data->BufTextLen);
9192 data->InsertChars(pos: 0, text: history_str);
9193 }
9194 }
9195 }
9196 return 0;
9197 }
9198};
9199
9200static void ShowExampleAppConsole(bool* p_open)
9201{
9202 static ExampleAppConsole console;
9203 console.Draw(title: "Example: Console", p_open);
9204}
9205
9206//-----------------------------------------------------------------------------
9207// [SECTION] Example App: Debug Log / ShowExampleAppLog()
9208//-----------------------------------------------------------------------------
9209
9210// Usage:
9211// static ExampleAppLog my_log;
9212// my_log.AddLog("Hello %d world\n", 123);
9213// my_log.Draw("title");
9214struct ExampleAppLog
9215{
9216 ImGuiTextBuffer Buf;
9217 ImGuiTextFilter Filter;
9218 ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
9219 bool AutoScroll; // Keep scrolling if already at the bottom.
9220
9221 ExampleAppLog()
9222 {
9223 AutoScroll = true;
9224 Clear();
9225 }
9226
9227 void Clear()
9228 {
9229 Buf.clear();
9230 LineOffsets.clear();
9231 LineOffsets.push_back(v: 0);
9232 }
9233
9234 void AddLog(const char* fmt, ...) IM_FMTARGS(2)
9235 {
9236 int old_size = Buf.size();
9237 va_list args;
9238 va_start(args, fmt);
9239 Buf.appendfv(fmt, args);
9240 va_end(args);
9241 for (int new_size = Buf.size(); old_size < new_size; old_size++)
9242 if (Buf[old_size] == '\n')
9243 LineOffsets.push_back(v: old_size + 1);
9244 }
9245
9246 void Draw(const char* title, bool* p_open = NULL)
9247 {
9248 if (!ImGui::Begin(name: title, p_open))
9249 {
9250 ImGui::End();
9251 return;
9252 }
9253
9254 // Options menu
9255 if (ImGui::BeginPopup(str_id: "Options"))
9256 {
9257 ImGui::Checkbox(label: "Auto-scroll", v: &AutoScroll);
9258 ImGui::EndPopup();
9259 }
9260
9261 // Main window
9262 if (ImGui::Button(label: "Options"))
9263 ImGui::OpenPopup(str_id: "Options");
9264 ImGui::SameLine();
9265 bool clear = ImGui::Button(label: "Clear");
9266 ImGui::SameLine();
9267 bool copy = ImGui::Button(label: "Copy");
9268 ImGui::SameLine();
9269 Filter.Draw(label: "Filter", width: -100.0f);
9270
9271 ImGui::Separator();
9272
9273 if (ImGui::BeginChild(str_id: "scrolling", size: ImVec2(0, 0), child_flags: ImGuiChildFlags_None, window_flags: ImGuiWindowFlags_HorizontalScrollbar))
9274 {
9275 if (clear)
9276 Clear();
9277 if (copy)
9278 ImGui::LogToClipboard();
9279
9280 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
9281 const char* buf = Buf.begin();
9282 const char* buf_end = Buf.end();
9283 if (Filter.IsActive())
9284 {
9285 // In this example we don't use the clipper when Filter is enabled.
9286 // This is because we don't have random access to the result of our filter.
9287 // A real application processing logs with ten of thousands of entries may want to store the result of
9288 // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
9289 for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
9290 {
9291 const char* line_start = buf + LineOffsets[line_no];
9292 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9293 if (Filter.PassFilter(text: line_start, text_end: line_end))
9294 ImGui::TextUnformatted(text: line_start, text_end: line_end);
9295 }
9296 }
9297 else
9298 {
9299 // The simplest and easy way to display the entire buffer:
9300 // ImGui::TextUnformatted(buf_begin, buf_end);
9301 // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
9302 // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
9303 // within the visible area.
9304 // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
9305 // on your side is recommended. Using ImGuiListClipper requires
9306 // - A) random access into your data
9307 // - B) items all being the same height,
9308 // both of which we can handle since we have an array pointing to the beginning of each line of text.
9309 // When using the filter (in the block of code above) we don't have random access into the data to display
9310 // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
9311 // it possible (and would be recommended if you want to search through tens of thousands of entries).
9312 ImGuiListClipper clipper;
9313 clipper.Begin(items_count: LineOffsets.Size);
9314 while (clipper.Step())
9315 {
9316 for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
9317 {
9318 const char* line_start = buf + LineOffsets[line_no];
9319 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9320 ImGui::TextUnformatted(text: line_start, text_end: line_end);
9321 }
9322 }
9323 clipper.End();
9324 }
9325 ImGui::PopStyleVar();
9326
9327 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
9328 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
9329 if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
9330 ImGui::SetScrollHereY(1.0f);
9331 }
9332 ImGui::EndChild();
9333 ImGui::End();
9334 }
9335};
9336
9337// Demonstrate creating a simple log window with basic filtering.
9338static void ShowExampleAppLog(bool* p_open)
9339{
9340 static ExampleAppLog log;
9341
9342 // For the demo: add a debug button _BEFORE_ the normal log window contents
9343 // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.
9344 // Most of the contents of the window will be added by the log.Draw() call.
9345 ImGui::SetNextWindowSize(size: ImVec2(500, 400), cond: ImGuiCond_FirstUseEver);
9346 ImGui::Begin(name: "Example: Log", p_open);
9347 IMGUI_DEMO_MARKER("Examples/Log");
9348 if (ImGui::SmallButton(label: "[Debug] Add 5 entries"))
9349 {
9350 static int counter = 0;
9351 const char* categories[3] = { "info", "warn", "error" };
9352 const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
9353 for (int n = 0; n < 5; n++)
9354 {
9355 const char* category = categories[counter % IM_ARRAYSIZE(categories)];
9356 const char* word = words[counter % IM_ARRAYSIZE(words)];
9357 log.AddLog(fmt: "[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
9358 ImGui::GetFrameCount(), category, ImGui::GetTime(), word);
9359 counter++;
9360 }
9361 }
9362 ImGui::End();
9363
9364 // Actually call in the regular Log helper (which will Begin() into the same window as we just did)
9365 log.Draw(title: "Example: Log", p_open);
9366}
9367
9368//-----------------------------------------------------------------------------
9369// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
9370//-----------------------------------------------------------------------------
9371
9372// Demonstrate create a window with multiple child windows.
9373static void ShowExampleAppLayout(bool* p_open)
9374{
9375 ImGui::SetNextWindowSize(size: ImVec2(500, 440), cond: ImGuiCond_FirstUseEver);
9376 if (ImGui::Begin(name: "Example: Simple layout", p_open, flags: ImGuiWindowFlags_MenuBar))
9377 {
9378 IMGUI_DEMO_MARKER("Examples/Simple layout");
9379 if (ImGui::BeginMenuBar())
9380 {
9381 if (ImGui::BeginMenu(label: "File"))
9382 {
9383 if (ImGui::MenuItem(label: "Close", shortcut: "Ctrl+W")) { *p_open = false; }
9384 ImGui::EndMenu();
9385 }
9386 ImGui::EndMenuBar();
9387 }
9388
9389 // Left
9390 static int selected = 0;
9391 {
9392 ImGui::BeginChild(str_id: "left pane", size: ImVec2(150, 0), child_flags: ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
9393 for (int i = 0; i < 100; i++)
9394 {
9395 // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav
9396 char label[128];
9397 sprintf(s: label, format: "MyObject %d", i);
9398 if (ImGui::Selectable(label, selected: selected == i))
9399 selected = i;
9400 }
9401 ImGui::EndChild();
9402 }
9403 ImGui::SameLine();
9404
9405 // Right
9406 {
9407 ImGui::BeginGroup();
9408 ImGui::BeginChild(str_id: "item view", size: ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
9409 ImGui::Text(fmt: "MyObject: %d", selected);
9410 ImGui::Separator();
9411 if (ImGui::BeginTabBar(str_id: "##Tabs", flags: ImGuiTabBarFlags_None))
9412 {
9413 if (ImGui::BeginTabItem(label: "Description"))
9414 {
9415 ImGui::TextWrapped(fmt: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ");
9416 ImGui::EndTabItem();
9417 }
9418 if (ImGui::BeginTabItem(label: "Details"))
9419 {
9420 ImGui::Text(fmt: "ID: 0123456789");
9421 ImGui::EndTabItem();
9422 }
9423 ImGui::EndTabBar();
9424 }
9425 ImGui::EndChild();
9426 if (ImGui::Button(label: "Revert")) {}
9427 ImGui::SameLine();
9428 if (ImGui::Button(label: "Save")) {}
9429 ImGui::EndGroup();
9430 }
9431 }
9432 ImGui::End();
9433}
9434
9435//-----------------------------------------------------------------------------
9436// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
9437//-----------------------------------------------------------------------------
9438// Some of the interactions are a bit lack-luster:
9439// - We would want pressing validating or leaving the filter to somehow restore focus.
9440// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.
9441// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.
9442//-----------------------------------------------------------------------------
9443
9444struct ExampleAppPropertyEditor
9445{
9446 ImGuiTextFilter Filter;
9447 ExampleTreeNode* VisibleNode = NULL;
9448
9449 void Draw(ExampleTreeNode* root_node)
9450 {
9451 // Left side: draw tree
9452 // - Currently using a table to benefit from RowBg feature
9453 if (ImGui::BeginChild(str_id: "##tree", size: ImVec2(300, 0), child_flags: ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
9454 {
9455 ImGui::SetNextItemWidth(-FLT_MIN);
9456 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_F, flags: ImGuiInputFlags_Tooltip);
9457 ImGui::PushItemFlag(option: ImGuiItemFlags_NoNavDefaultFocus, enabled: true);
9458 if (ImGui::InputTextWithHint(label: "##Filter", hint: "incl,-excl", buf: Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), flags: ImGuiInputTextFlags_EscapeClearsAll))
9459 Filter.Build();
9460 ImGui::PopItemFlag();
9461
9462 if (ImGui::BeginTable(str_id: "##bg", columns: 1, flags: ImGuiTableFlags_RowBg))
9463 {
9464 for (ExampleTreeNode* node : root_node->Childs)
9465 if (Filter.PassFilter(text: node->Name)) // Filter root node
9466 DrawTreeNode(node);
9467 ImGui::EndTable();
9468 }
9469 }
9470 ImGui::EndChild();
9471
9472 // Right side: draw properties
9473 ImGui::SameLine();
9474
9475 ImGui::BeginGroup(); // Lock X position
9476 if (ExampleTreeNode* node = VisibleNode)
9477 {
9478 ImGui::Text(fmt: "%s", node->Name);
9479 ImGui::TextDisabled(fmt: "UID: 0x%08X", node->UID);
9480 ImGui::Separator();
9481 if (ImGui::BeginTable(str_id: "##properties", columns: 2, flags: ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))
9482 {
9483 // Push object ID after we entered the table, so table is shared for all objects
9484 ImGui::PushID(int_id: (int)node->UID);
9485 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthFixed);
9486 ImGui::TableSetupColumn(label: "", flags: ImGuiTableColumnFlags_WidthStretch, init_width_or_weight: 2.0f); // Default twice larger
9487 if (node->HasData)
9488 {
9489 // In a typical application, the structure description would be derived from a data-driven system.
9490 // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.
9491 // - Limits and some details are hard-coded to simplify the demo.
9492 for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)
9493 {
9494 ImGui::TableNextRow();
9495 ImGui::PushID(str_id: field_desc.Name);
9496 ImGui::TableNextColumn();
9497 ImGui::AlignTextToFramePadding();
9498 ImGui::TextUnformatted(text: field_desc.Name);
9499 ImGui::TableNextColumn();
9500 void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);
9501 switch (field_desc.DataType)
9502 {
9503 case ImGuiDataType_Bool:
9504 {
9505 IM_ASSERT(field_desc.DataCount == 1);
9506 ImGui::Checkbox(label: "##Editor", v: (bool*)field_ptr);
9507 break;
9508 }
9509 case ImGuiDataType_S32:
9510 {
9511 int v_min = INT_MIN, v_max = INT_MAX;
9512 ImGui::SetNextItemWidth(-FLT_MIN);
9513 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);
9514 break;
9515 }
9516 case ImGuiDataType_Float:
9517 {
9518 float v_min = 0.0f, v_max = 1.0f;
9519 ImGui::SetNextItemWidth(-FLT_MIN);
9520 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);
9521 break;
9522 }
9523 case ImGuiDataType_String:
9524 {
9525 ImGui::InputText(label: "##Editor", buf: reinterpret_cast<char*>(field_ptr), buf_size: 28);
9526 break;
9527 }
9528 }
9529 ImGui::PopID();
9530 }
9531 }
9532 ImGui::PopID();
9533 ImGui::EndTable();
9534 }
9535 }
9536 ImGui::EndGroup();
9537 }
9538
9539 void DrawTreeNode(ExampleTreeNode* node)
9540 {
9541 ImGui::TableNextRow();
9542 ImGui::TableNextColumn();
9543 ImGui::PushID(int_id: node->UID);
9544 ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
9545 tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;// Standard opening mode as we are likely to want to add selection afterwards
9546 tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support
9547 tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
9548 tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
9549 if (node == VisibleNode)
9550 tree_flags |= ImGuiTreeNodeFlags_Selected;
9551 if (node->Childs.Size == 0)
9552 tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
9553 if (node->DataMyBool == false)
9554 ImGui::PushStyleColor(idx: ImGuiCol_Text, col: ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
9555 bool node_open = ImGui::TreeNodeEx(str_id: "", flags: tree_flags, fmt: "%s", node->Name);
9556 if (node->DataMyBool == false)
9557 ImGui::PopStyleColor();
9558 if (ImGui::IsItemFocused())
9559 VisibleNode = node;
9560 if (node_open)
9561 {
9562 for (ExampleTreeNode* child : node->Childs)
9563 DrawTreeNode(node: child);
9564 ImGui::TreePop();
9565 }
9566 ImGui::PopID();
9567 }
9568};
9569
9570// Demonstrate creating a simple property editor.
9571static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data)
9572{
9573 ImGui::SetNextWindowSize(size: ImVec2(430, 450), cond: ImGuiCond_FirstUseEver);
9574 if (!ImGui::Begin(name: "Example: Property editor", p_open))
9575 {
9576 ImGui::End();
9577 return;
9578 }
9579
9580 IMGUI_DEMO_MARKER("Examples/Property Editor");
9581 static ExampleAppPropertyEditor property_editor;
9582 if (demo_data->DemoTree == NULL)
9583 demo_data->DemoTree = ExampleTree_CreateDemoTree();
9584 property_editor.Draw(root_node: demo_data->DemoTree);
9585
9586 ImGui::End();
9587}
9588
9589//-----------------------------------------------------------------------------
9590// [SECTION] Example App: Long Text / ShowExampleAppLongText()
9591//-----------------------------------------------------------------------------
9592
9593// Demonstrate/test rendering huge amount of text, and the incidence of clipping.
9594static void ShowExampleAppLongText(bool* p_open)
9595{
9596 ImGui::SetNextWindowSize(size: ImVec2(520, 600), cond: ImGuiCond_FirstUseEver);
9597 if (!ImGui::Begin(name: "Example: Long text display", p_open))
9598 {
9599 ImGui::End();
9600 return;
9601 }
9602 IMGUI_DEMO_MARKER("Examples/Long text display");
9603
9604 static int test_type = 0;
9605 static ImGuiTextBuffer log;
9606 static int lines = 0;
9607 ImGui::Text(fmt: "Printing unusually long amount of text.");
9608 ImGui::Combo(label: "Test type", current_item: &test_type,
9609 items_separated_by_zeros: "Single call to TextUnformatted()\0"
9610 "Multiple calls to Text(), clipped\0"
9611 "Multiple calls to Text(), not clipped (slow)\0");
9612 ImGui::Text(fmt: "Buffer contents: %d lines, %d bytes", lines, log.size());
9613 if (ImGui::Button(label: "Clear")) { log.clear(); lines = 0; }
9614 ImGui::SameLine();
9615 if (ImGui::Button(label: "Add 1000 lines"))
9616 {
9617 for (int i = 0; i < 1000; i++)
9618 log.appendf(fmt: "%i The quick brown fox jumps over the lazy dog\n", lines + i);
9619 lines += 1000;
9620 }
9621 ImGui::BeginChild(str_id: "Log");
9622 switch (test_type)
9623 {
9624 case 0:
9625 // Single call to TextUnformatted() with a big buffer
9626 ImGui::TextUnformatted(text: log.begin(), text_end: log.end());
9627 break;
9628 case 1:
9629 {
9630 // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
9631 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
9632 ImGuiListClipper clipper;
9633 clipper.Begin(items_count: lines);
9634 while (clipper.Step())
9635 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9636 ImGui::Text(fmt: "%i The quick brown fox jumps over the lazy dog", i);
9637 ImGui::PopStyleVar();
9638 break;
9639 }
9640 case 2:
9641 // Multiple calls to Text(), not clipped (slow)
9642 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
9643 for (int i = 0; i < lines; i++)
9644 ImGui::Text(fmt: "%i The quick brown fox jumps over the lazy dog", i);
9645 ImGui::PopStyleVar();
9646 break;
9647 }
9648 ImGui::EndChild();
9649 ImGui::End();
9650}
9651
9652//-----------------------------------------------------------------------------
9653// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
9654//-----------------------------------------------------------------------------
9655
9656// Demonstrate creating a window which gets auto-resized according to its content.
9657static void ShowExampleAppAutoResize(bool* p_open)
9658{
9659 if (!ImGui::Begin(name: "Example: Auto-resizing window", p_open, flags: ImGuiWindowFlags_AlwaysAutoResize))
9660 {
9661 ImGui::End();
9662 return;
9663 }
9664 IMGUI_DEMO_MARKER("Examples/Auto-resizing window");
9665
9666 static int lines = 10;
9667 ImGui::TextUnformatted(
9668 text: "Window will resize every-frame to the size of its content.\n"
9669 "Note that you probably don't want to query the window size to\n"
9670 "output your content because that would create a feedback loop.");
9671 ImGui::SliderInt(label: "Number of lines", v: &lines, v_min: 1, v_max: 20);
9672 for (int i = 0; i < lines; i++)
9673 ImGui::Text(fmt: "%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally
9674 ImGui::End();
9675}
9676
9677//-----------------------------------------------------------------------------
9678// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
9679//-----------------------------------------------------------------------------
9680
9681// Demonstrate creating a window with custom resize constraints.
9682// Note that size constraints currently don't work on a docked window (when in 'docking' branch)
9683static void ShowExampleAppConstrainedResize(bool* p_open)
9684{
9685 struct CustomConstraints
9686 {
9687 // Helper functions to demonstrate programmatic constraints
9688 // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.
9689 // FIXME: None of the three demos works consistently when resizing from borders.
9690 static void AspectRatio(ImGuiSizeCallbackData* data)
9691 {
9692 float aspect_ratio = *(float*)data->UserData;
9693 data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio);
9694 }
9695 static void Square(ImGuiSizeCallbackData* data)
9696 {
9697 data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);
9698 }
9699 static void Step(ImGuiSizeCallbackData* data)
9700 {
9701 float step = *(float*)data->UserData;
9702 data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step);
9703 }
9704 };
9705
9706 const char* test_desc[] =
9707 {
9708 "Between 100x100 and 500x500",
9709 "At least 100x100",
9710 "Resize vertical + lock current width",
9711 "Resize horizontal + lock current height",
9712 "Width Between 400 and 500",
9713 "Height at least 400",
9714 "Custom: Aspect Ratio 16:9",
9715 "Custom: Always Square",
9716 "Custom: Fixed Steps (100)",
9717 };
9718
9719 // Options
9720 static bool auto_resize = false;
9721 static bool window_padding = true;
9722 static int type = 6; // Aspect Ratio
9723 static int display_lines = 10;
9724
9725 // Submit constraint
9726 float aspect_ratio = 16.0f / 9.0f;
9727 float fixed_step = 100.0f;
9728 if (type == 0) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(100, 100), size_max: ImVec2(500, 500)); // Between 100x100 and 500x500
9729 if (type == 1) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(100, 100), size_max: ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100
9730 if (type == 2) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(-1, 0), size_max: ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width
9731 if (type == 3) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0, -1), size_max: ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height
9732 if (type == 4) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(400, -1), size_max: ImVec2(500, -1)); // Width Between and 400 and 500
9733 if (type == 5) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(-1, 400), size_max: ImVec2(-1, FLT_MAX)); // Height at least 400
9734 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
9735 if (type == 7) ImGui::SetNextWindowSizeConstraints(size_min: ImVec2(0, 0), size_max: ImVec2(FLT_MAX, FLT_MAX), custom_callback: CustomConstraints::Square); // Always Square
9736 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
9737
9738 // Submit window
9739 if (!window_padding)
9740 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: ImVec2(0.0f, 0.0f));
9741 const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;
9742 const bool window_open = ImGui::Begin(name: "Example: Constrained Resize", p_open, flags: window_flags);
9743 if (!window_padding)
9744 ImGui::PopStyleVar();
9745 if (window_open)
9746 {
9747 IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
9748 if (ImGui::GetIO().KeyShift)
9749 {
9750 // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture.
9751 ImVec2 avail_size = ImGui::GetContentRegionAvail();
9752 ImVec2 pos = ImGui::GetCursorScreenPos();
9753 ImGui::ColorButton(desc_id: "viewport", col: ImVec4(0.5f, 0.2f, 0.5f, 1.0f), flags: ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, size: avail_size);
9754 ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10));
9755 ImGui::Text(fmt: "%.2f x %.2f", avail_size.x, avail_size.y);
9756 }
9757 else
9758 {
9759 ImGui::Text(fmt: "(Hold SHIFT to display a dummy viewport)");
9760 if (ImGui::IsWindowDocked())
9761 ImGui::Text(fmt: "Warning: Sizing Constraints won't work if the window is docked!");
9762 if (ImGui::Button(label: "Set 200x200")) { ImGui::SetWindowSize(size: ImVec2(200, 200)); } ImGui::SameLine();
9763 if (ImGui::Button(label: "Set 500x500")) { ImGui::SetWindowSize(size: ImVec2(500, 500)); } ImGui::SameLine();
9764 if (ImGui::Button(label: "Set 800x200")) { ImGui::SetWindowSize(size: ImVec2(800, 200)); }
9765 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9766 ImGui::Combo(label: "Constraint", current_item: &type, items: test_desc, IM_ARRAYSIZE(test_desc));
9767 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9768 ImGui::DragInt(label: "Lines", v: &display_lines, v_speed: 0.2f, v_min: 1, v_max: 100);
9769 ImGui::Checkbox(label: "Auto-resize", v: &auto_resize);
9770 ImGui::Checkbox(label: "Window padding", v: &window_padding);
9771 for (int i = 0; i < display_lines; i++)
9772 ImGui::Text(fmt: "%*sHello, sailor! Making this line long enough for the example.", i * 4, "");
9773 }
9774 }
9775 ImGui::End();
9776}
9777
9778//-----------------------------------------------------------------------------
9779// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
9780//-----------------------------------------------------------------------------
9781
9782// Demonstrate creating a simple static window with no decoration
9783// + a context-menu to choose which corner of the screen to use.
9784static void ShowExampleAppSimpleOverlay(bool* p_open)
9785{
9786 static int location = 0;
9787 ImGuiIO& io = ImGui::GetIO();
9788 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
9789 if (location >= 0)
9790 {
9791 const float PAD = 10.0f;
9792 const ImGuiViewport* viewport = ImGui::GetMainViewport();
9793 ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
9794 ImVec2 work_size = viewport->WorkSize;
9795 ImVec2 window_pos, window_pos_pivot;
9796 window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);
9797 window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);
9798 window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f;
9799 window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f;
9800 ImGui::SetNextWindowPos(pos: window_pos, cond: ImGuiCond_Always, pivot: window_pos_pivot);
9801 ImGui::SetNextWindowViewport(viewport->ID);
9802 window_flags |= ImGuiWindowFlags_NoMove;
9803 }
9804 else if (location == -2)
9805 {
9806 // Center window
9807 ImGui::SetNextWindowPos(pos: ImGui::GetMainViewport()->GetCenter(), cond: ImGuiCond_Always, pivot: ImVec2(0.5f, 0.5f));
9808 window_flags |= ImGuiWindowFlags_NoMove;
9809 }
9810 ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
9811 if (ImGui::Begin(name: "Example: Simple overlay", p_open, flags: window_flags))
9812 {
9813 IMGUI_DEMO_MARKER("Examples/Simple Overlay");
9814 ImGui::Text(fmt: "Simple overlay\n" "(right-click to change position)");
9815 ImGui::Separator();
9816 if (ImGui::IsMousePosValid())
9817 ImGui::Text(fmt: "Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
9818 else
9819 ImGui::Text(fmt: "Mouse Position: <invalid>");
9820 if (ImGui::BeginPopupContextWindow())
9821 {
9822 if (ImGui::MenuItem(label: "Custom", NULL, selected: location == -1)) location = -1;
9823 if (ImGui::MenuItem(label: "Center", NULL, selected: location == -2)) location = -2;
9824 if (ImGui::MenuItem(label: "Top-left", NULL, selected: location == 0)) location = 0;
9825 if (ImGui::MenuItem(label: "Top-right", NULL, selected: location == 1)) location = 1;
9826 if (ImGui::MenuItem(label: "Bottom-left", NULL, selected: location == 2)) location = 2;
9827 if (ImGui::MenuItem(label: "Bottom-right", NULL, selected: location == 3)) location = 3;
9828 if (p_open && ImGui::MenuItem(label: "Close")) *p_open = false;
9829 ImGui::EndPopup();
9830 }
9831 }
9832 ImGui::End();
9833}
9834
9835//-----------------------------------------------------------------------------
9836// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
9837//-----------------------------------------------------------------------------
9838
9839// Demonstrate creating a window covering the entire screen/viewport
9840static void ShowExampleAppFullscreen(bool* p_open)
9841{
9842 static bool use_work_area = true;
9843 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
9844
9845 // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
9846 // Based on your use case you may want one or the other.
9847 const ImGuiViewport* viewport = ImGui::GetMainViewport();
9848 ImGui::SetNextWindowPos(pos: use_work_area ? viewport->WorkPos : viewport->Pos);
9849 ImGui::SetNextWindowSize(size: use_work_area ? viewport->WorkSize : viewport->Size);
9850
9851 if (ImGui::Begin(name: "Example: Fullscreen window", p_open, flags))
9852 {
9853 ImGui::Checkbox(label: "Use work area instead of main area", v: &use_work_area);
9854 ImGui::SameLine();
9855 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.");
9856
9857 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoBackground", flags: &flags, flags_value: ImGuiWindowFlags_NoBackground);
9858 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoDecoration", flags: &flags, flags_value: ImGuiWindowFlags_NoDecoration);
9859 ImGui::Indent();
9860 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoTitleBar", flags: &flags, flags_value: ImGuiWindowFlags_NoTitleBar);
9861 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoCollapse", flags: &flags, flags_value: ImGuiWindowFlags_NoCollapse);
9862 ImGui::CheckboxFlags(label: "ImGuiWindowFlags_NoScrollbar", flags: &flags, flags_value: ImGuiWindowFlags_NoScrollbar);
9863 ImGui::Unindent();
9864
9865 if (p_open && ImGui::Button(label: "Close this window"))
9866 *p_open = false;
9867 }
9868 ImGui::End();
9869}
9870
9871//-----------------------------------------------------------------------------
9872// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()
9873//-----------------------------------------------------------------------------
9874
9875// Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation.
9876// This applies to all regular items as well.
9877// Read FAQ section "How can I have multiple widgets with the same label?" for details.
9878static void ShowExampleAppWindowTitles(bool*)
9879{
9880 const ImGuiViewport* viewport = ImGui::GetMainViewport();
9881 const ImVec2 base_pos = viewport->Pos;
9882
9883 // By default, Windows are uniquely identified by their title.
9884 // You can use the "##" and "###" markers to manipulate the display/ID.
9885
9886 // Using "##" to display same title but have unique identifier.
9887 ImGui::SetNextWindowPos(pos: ImVec2(base_pos.x + 100, base_pos.y + 100), cond: ImGuiCond_FirstUseEver);
9888 ImGui::Begin(name: "Same title as another window##1");
9889 IMGUI_DEMO_MARKER("Examples/Manipulating window titles");
9890 ImGui::Text(fmt: "This is window 1.\nMy title is the same as window 2, but my identifier is unique.");
9891 ImGui::End();
9892
9893 ImGui::SetNextWindowPos(pos: ImVec2(base_pos.x + 100, base_pos.y + 200), cond: ImGuiCond_FirstUseEver);
9894 ImGui::Begin(name: "Same title as another window##2");
9895 ImGui::Text(fmt: "This is window 2.\nMy title is the same as window 1, but my identifier is unique.");
9896 ImGui::End();
9897
9898 // Using "###" to display a changing title but keep a static identifier "AnimatedTitle"
9899 char buf[128];
9900 sprintf(s: buf, format: "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount());
9901 ImGui::SetNextWindowPos(pos: ImVec2(base_pos.x + 100, base_pos.y + 300), cond: ImGuiCond_FirstUseEver);
9902 ImGui::Begin(name: buf);
9903 ImGui::Text(fmt: "This window has a changing title.");
9904 ImGui::End();
9905}
9906
9907//-----------------------------------------------------------------------------
9908// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
9909//-----------------------------------------------------------------------------
9910
9911// Add a |_| looking shape
9912static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz)
9913{
9914 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 } };
9915 for (const ImVec2& p : pos_norms)
9916 draw_list->PathLineTo(pos: ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y)));
9917}
9918
9919// Demonstrate using the low-level ImDrawList to draw custom shapes.
9920static void ShowExampleAppCustomRendering(bool* p_open)
9921{
9922 if (!ImGui::Begin(name: "Example: Custom rendering", p_open))
9923 {
9924 ImGui::End();
9925 return;
9926 }
9927 IMGUI_DEMO_MARKER("Examples/Custom Rendering");
9928
9929 // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
9930 // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
9931 // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
9932 // exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
9933
9934 if (ImGui::BeginTabBar(str_id: "##TabBar"))
9935 {
9936 if (ImGui::BeginTabItem(label: "Primitives"))
9937 {
9938 ImGui::PushItemWidth(item_width: -ImGui::GetFontSize() * 15);
9939 ImDrawList* draw_list = ImGui::GetWindowDrawList();
9940
9941 // Draw gradients
9942 // (note that those are currently exacerbating our sRGB/Linear issues)
9943 // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..
9944 ImGui::Text(fmt: "Gradients");
9945 ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
9946 {
9947 ImVec2 p0 = ImGui::GetCursorScreenPos();
9948 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9949 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));
9950 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));
9951 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);
9952 ImGui::InvisibleButton(str_id: "##gradient1", size: gradient_size);
9953 }
9954 {
9955 ImVec2 p0 = ImGui::GetCursorScreenPos();
9956 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9957 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));
9958 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));
9959 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);
9960 ImGui::InvisibleButton(str_id: "##gradient2", size: gradient_size);
9961 }
9962
9963 // Draw a bunch of primitives
9964 ImGui::Text(fmt: "All primitives");
9965 static float sz = 36.0f;
9966 static float thickness = 3.0f;
9967 static int ngon_sides = 6;
9968 static bool circle_segments_override = false;
9969 static int circle_segments_override_v = 12;
9970 static bool curve_segments_override = false;
9971 static int curve_segments_override_v = 8;
9972 static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
9973 ImGui::DragFloat(label: "Size", v: &sz, v_speed: 0.2f, v_min: 2.0f, v_max: 100.0f, format: "%.0f");
9974 ImGui::DragFloat(label: "Thickness", v: &thickness, v_speed: 0.05f, v_min: 1.0f, v_max: 8.0f, format: "%.02f");
9975 ImGui::SliderInt(label: "N-gon sides", v: &ngon_sides, v_min: 3, v_max: 12);
9976 ImGui::Checkbox(label: "##circlesegmentoverride", v: &circle_segments_override);
9977 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
9978 circle_segments_override |= ImGui::SliderInt(label: "Circle segments override", v: &circle_segments_override_v, v_min: 3, v_max: 40);
9979 ImGui::Checkbox(label: "##curvessegmentoverride", v: &curve_segments_override);
9980 ImGui::SameLine(offset_from_start_x: 0.0f, spacing: ImGui::GetStyle().ItemInnerSpacing.x);
9981 curve_segments_override |= ImGui::SliderInt(label: "Curves segments override", v: &curve_segments_override_v, v_min: 3, v_max: 40);
9982 ImGui::ColorEdit4(label: "Color", col: &colf.x);
9983
9984 const ImVec2 p = ImGui::GetCursorScreenPos();
9985 const ImU32 col = ImColor(colf);
9986 const float spacing = 10.0f;
9987 const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;
9988 const float rounding = sz / 5.0f;
9989 const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;
9990 const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;
9991 const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves
9992 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) };
9993
9994 float x = p.x + 4.0f;
9995 float y = p.y + 4.0f;
9996 for (int n = 0; n < 2; n++)
9997 {
9998 // First line uses a thickness of 1.0f, second line uses the configurable thickness
9999 float th = (n == 0) ? 1.0f : thickness;
10000 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
10001 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
10002 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
10003 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
10004 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
10005 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
10006 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
10007 //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
10008 PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, flags: ImDrawFlags_Closed, thickness: th); x += sz + spacing; // Concave Shape
10009 //draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th);
10010 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!)
10011 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!)
10012 draw_list->AddLine(p1: ImVec2(x, y), p2: ImVec2(x + sz, y + sz), col, thickness: th); x += sz + spacing; // Diagonal line
10013
10014 // Path
10015 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);
10016 draw_list->PathStroke(col, flags: ImDrawFlags_None, thickness: th);
10017 x += sz + spacing;
10018
10019 // Quadratic Bezier Curve (3 control points)
10020 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);
10021 x += sz + spacing;
10022
10023 // Cubic Bezier Curve (4 control points)
10024 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);
10025
10026 x = p.x + 4;
10027 y += sz + spacing;
10028 }
10029
10030 // Filled shapes
10031 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
10032 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
10033 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
10034 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square
10035 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
10036 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
10037 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
10038 //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
10039 PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col); x += sz + spacing; // Concave shape
10040 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)
10041 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)
10042 draw_list->AddRectFilled(p_min: ImVec2(x, y), p_max: ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine)
10043
10044 // Path
10045 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);
10046 draw_list->PathFillConvex(col);
10047 x += sz + spacing;
10048
10049 // Quadratic Bezier Curve (3 control points)
10050 draw_list->PathLineTo(pos: ImVec2(x + cp3[0].x, y + cp3[0].y));
10051 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);
10052 draw_list->PathFillConvex(col);
10053 x += sz + spacing;
10054
10055 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));
10056 x += sz + spacing;
10057
10058 ImGui::Dummy(size: ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f));
10059 ImGui::PopItemWidth();
10060 ImGui::EndTabItem();
10061 }
10062
10063 if (ImGui::BeginTabItem(label: "Canvas"))
10064 {
10065 static ImVector<ImVec2> points;
10066 static ImVec2 scrolling(0.0f, 0.0f);
10067 static bool opt_enable_grid = true;
10068 static bool opt_enable_context_menu = true;
10069 static bool adding_line = false;
10070
10071 ImGui::Checkbox(label: "Enable grid", v: &opt_enable_grid);
10072 ImGui::Checkbox(label: "Enable context menu", v: &opt_enable_context_menu);
10073 ImGui::Text(fmt: "Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
10074
10075 // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
10076 // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
10077 // To use a child window instead we could use, e.g:
10078 // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
10079 // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
10080 // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);
10081 // ImGui::PopStyleColor();
10082 // ImGui::PopStyleVar();
10083 // [...]
10084 // ImGui::EndChild();
10085
10086 // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
10087 ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
10088 ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
10089 if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
10090 if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
10091 ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
10092
10093 // Draw border and background color
10094 ImGuiIO& io = ImGui::GetIO();
10095 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10096 draw_list->AddRectFilled(p_min: canvas_p0, p_max: canvas_p1, IM_COL32(50, 50, 50, 255));
10097 draw_list->AddRect(p_min: canvas_p0, p_max: canvas_p1, IM_COL32(255, 255, 255, 255));
10098
10099 // This will catch our interactions
10100 ImGui::InvisibleButton(str_id: "canvas", size: canvas_sz, flags: ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
10101 const bool is_hovered = ImGui::IsItemHovered(); // Hovered
10102 const bool is_active = ImGui::IsItemActive(); // Held
10103 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
10104 const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
10105
10106 // Add first and second point
10107 if (is_hovered && !adding_line && ImGui::IsMouseClicked(button: ImGuiMouseButton_Left))
10108 {
10109 points.push_back(v: mouse_pos_in_canvas);
10110 points.push_back(v: mouse_pos_in_canvas);
10111 adding_line = true;
10112 }
10113 if (adding_line)
10114 {
10115 points.back() = mouse_pos_in_canvas;
10116 if (!ImGui::IsMouseDown(button: ImGuiMouseButton_Left))
10117 adding_line = false;
10118 }
10119
10120 // Pan (we use a zero mouse threshold when there's no context menu)
10121 // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
10122 const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;
10123 if (is_active && ImGui::IsMouseDragging(button: ImGuiMouseButton_Right, lock_threshold: mouse_threshold_for_pan))
10124 {
10125 scrolling.x += io.MouseDelta.x;
10126 scrolling.y += io.MouseDelta.y;
10127 }
10128
10129 // Context menu (under default mouse threshold)
10130 ImVec2 drag_delta = ImGui::GetMouseDragDelta(button: ImGuiMouseButton_Right);
10131 if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
10132 ImGui::OpenPopupOnItemClick(str_id: "context", popup_flags: ImGuiPopupFlags_MouseButtonRight);
10133 if (ImGui::BeginPopup(str_id: "context"))
10134 {
10135 if (adding_line)
10136 points.resize(new_size: points.size() - 2);
10137 adding_line = false;
10138 if (ImGui::MenuItem(label: "Remove one", NULL, selected: false, enabled: points.Size > 0)) { points.resize(new_size: points.size() - 2); }
10139 if (ImGui::MenuItem(label: "Remove all", NULL, selected: false, enabled: points.Size > 0)) { points.clear(); }
10140 ImGui::EndPopup();
10141 }
10142
10143 // Draw grid + all lines in the canvas
10144 draw_list->PushClipRect(clip_rect_min: canvas_p0, clip_rect_max: canvas_p1, intersect_with_current_clip_rect: true);
10145 if (opt_enable_grid)
10146 {
10147 const float GRID_STEP = 64.0f;
10148 for (float x = fmodf(x: scrolling.x, y: GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
10149 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));
10150 for (float y = fmodf(x: scrolling.y, y: GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
10151 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));
10152 }
10153 for (int n = 0; n < points.Size; n += 2)
10154 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);
10155 draw_list->PopClipRect();
10156
10157 ImGui::EndTabItem();
10158 }
10159
10160 if (ImGui::BeginTabItem(label: "BG/FG draw lists"))
10161 {
10162 static bool draw_bg = true;
10163 static bool draw_fg = true;
10164 ImGui::Checkbox(label: "Draw in Background draw list", v: &draw_bg);
10165 ImGui::SameLine(); HelpMarker(desc: "The Background draw list will be rendered below every Dear ImGui windows.");
10166 ImGui::Checkbox(label: "Draw in Foreground draw list", v: &draw_fg);
10167 ImGui::SameLine(); HelpMarker(desc: "The Foreground draw list will be rendered over every Dear ImGui windows.");
10168 ImVec2 window_pos = ImGui::GetWindowPos();
10169 ImVec2 window_size = ImGui::GetWindowSize();
10170 ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);
10171 if (draw_bg)
10172 ImGui::GetBackgroundDrawList()->AddCircle(center: window_center, radius: window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), num_segments: 0, thickness: 10 + 4);
10173 if (draw_fg)
10174 ImGui::GetForegroundDrawList()->AddCircle(center: window_center, radius: window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), num_segments: 0, thickness: 10);
10175 ImGui::EndTabItem();
10176 }
10177
10178 // Demonstrate out-of-order rendering via channels splitting
10179 // We use functions in ImDrawList as each draw list contains a convenience splitter,
10180 // but you can also instantiate your own ImDrawListSplitter if you need to nest them.
10181 if (ImGui::BeginTabItem(label: "Draw Channels"))
10182 {
10183 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10184 {
10185 ImGui::Text(fmt: "Blue shape is drawn first: appears in back");
10186 ImGui::Text(fmt: "Red shape is drawn after: appears in front");
10187 ImVec2 p0 = ImGui::GetCursorScreenPos();
10188 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
10189 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
10190 ImGui::Dummy(size: ImVec2(75, 75));
10191 }
10192 ImGui::Separator();
10193 {
10194 ImGui::Text(fmt: "Blue shape is drawn first, into channel 1: appears in front");
10195 ImGui::Text(fmt: "Red shape is drawn after, into channel 0: appears in back");
10196 ImVec2 p1 = ImGui::GetCursorScreenPos();
10197
10198 // Create 2 channels and draw a Blue shape THEN a Red shape.
10199 // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.
10200 draw_list->ChannelsSplit(count: 2);
10201 draw_list->ChannelsSetCurrent(n: 1);
10202 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
10203 draw_list->ChannelsSetCurrent(n: 0);
10204 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
10205
10206 // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.
10207 // This works by copying draw indices only (vertices are not copied).
10208 draw_list->ChannelsMerge();
10209 ImGui::Dummy(size: ImVec2(75, 75));
10210 ImGui::Text(fmt: "After reordering, contents of channel 0 appears below channel 1.");
10211 }
10212 ImGui::EndTabItem();
10213 }
10214
10215 ImGui::EndTabBar();
10216 }
10217
10218 ImGui::End();
10219}
10220
10221//-----------------------------------------------------------------------------
10222// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
10223//-----------------------------------------------------------------------------
10224
10225// Demonstrate using DockSpace() to create an explicit docking node within an existing window.
10226// Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking!
10227// - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.
10228// - Drag from window menu button (upper-left button) to undock an entire node (all windows).
10229// - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to enable docking.
10230// About dockspaces:
10231// - Use DockSpace() to create an explicit dock node _within_ an existing window.
10232// - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport.
10233// This is often used with ImGuiDockNodeFlags_PassthruCentralNode.
10234// - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! (*)
10235// - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked.
10236// e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly.
10237// (*) because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node,
10238// because that window is submitted as part of the part of the NewFrame() call. An easy workaround is that you can create
10239// your own implicit "Debug##2" window after calling DockSpace() and leave it in the window stack for anyone to use.
10240void ShowExampleAppDockSpace(bool* p_open)
10241{
10242 // READ THIS !!!
10243 // TL;DR; this demo is more complicated than what most users you would normally use.
10244 // If we remove all options we are showcasing, this demo would become:
10245 // void ShowExampleAppDockSpace()
10246 // {
10247 // ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
10248 // }
10249 // In most cases you should be able to just call DockSpaceOverViewport() and ignore all the code below!
10250 // In this specific demo, we are not using DockSpaceOverViewport() because:
10251 // - (1) we allow the host window to be floating/moveable instead of filling the viewport (when opt_fullscreen == false)
10252 // - (2) we allow the host window to have padding (when opt_padding == true)
10253 // - (3) we expose many flags and need a way to have them visible.
10254 // - (4) we have a local menu bar in the host window (vs. you could use BeginMainMenuBar() + DockSpaceOverViewport()
10255 // in your code, but we don't here because we allow the window to be floating)
10256
10257 static bool opt_fullscreen = true;
10258 static bool opt_padding = false;
10259 static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
10260
10261 // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
10262 // because it would be confusing to have two docking targets within each others.
10263 ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
10264 if (opt_fullscreen)
10265 {
10266 const ImGuiViewport* viewport = ImGui::GetMainViewport();
10267 ImGui::SetNextWindowPos(pos: viewport->WorkPos);
10268 ImGui::SetNextWindowSize(size: viewport->WorkSize);
10269 ImGui::SetNextWindowViewport(viewport->ID);
10270 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowRounding, val: 0.0f);
10271 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowBorderSize, val: 0.0f);
10272 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
10273 window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
10274 }
10275 else
10276 {
10277 dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
10278 }
10279
10280 // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background
10281 // and handle the pass-thru hole, so we ask Begin() to not render a background.
10282 if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
10283 window_flags |= ImGuiWindowFlags_NoBackground;
10284
10285 // Important: note that we proceed even if Begin() returns false (aka window is collapsed).
10286 // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
10287 // all active windows docked into it will lose their parent and become undocked.
10288 // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
10289 // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
10290 if (!opt_padding)
10291 ImGui::PushStyleVar(idx: ImGuiStyleVar_WindowPadding, val: ImVec2(0.0f, 0.0f));
10292 ImGui::Begin(name: "DockSpace Demo", p_open, flags: window_flags);
10293 if (!opt_padding)
10294 ImGui::PopStyleVar();
10295
10296 if (opt_fullscreen)
10297 ImGui::PopStyleVar(count: 2);
10298
10299 // Submit the DockSpace
10300 ImGuiIO& io = ImGui::GetIO();
10301 if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
10302 {
10303 ImGuiID dockspace_id = ImGui::GetID(str_id: "MyDockSpace");
10304 ImGui::DockSpace(dockspace_id, size: ImVec2(0.0f, 0.0f), flags: dockspace_flags);
10305 }
10306 else
10307 {
10308 ShowDockingDisabledMessage();
10309 }
10310
10311 if (ImGui::BeginMenuBar())
10312 {
10313 if (ImGui::BeginMenu(label: "Options"))
10314 {
10315 // Disabling fullscreen would allow the window to be moved to the front of other windows,
10316 // which we can't undo at the moment without finer window depth/z control.
10317 ImGui::MenuItem(label: "Fullscreen", NULL, p_selected: &opt_fullscreen);
10318 ImGui::MenuItem(label: "Padding", NULL, p_selected: &opt_padding);
10319 ImGui::Separator();
10320
10321 if (ImGui::MenuItem(label: "Flag: NoDockingOverCentralNode", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingOverCentralNode; }
10322 if (ImGui::MenuItem(label: "Flag: NoDockingSplit", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoDockingSplit) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingSplit; }
10323 if (ImGui::MenuItem(label: "Flag: NoUndocking", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoUndocking) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoUndocking; }
10324 if (ImGui::MenuItem(label: "Flag: NoResize", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoResize; }
10325 if (ImGui::MenuItem(label: "Flag: AutoHideTabBar", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; }
10326 if (ImGui::MenuItem(label: "Flag: PassthruCentralNode", shortcut: "", selected: (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, enabled: opt_fullscreen)) { dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; }
10327 ImGui::Separator();
10328
10329 if (ImGui::MenuItem(label: "Close", NULL, selected: false, enabled: p_open != NULL))
10330 *p_open = false;
10331 ImGui::EndMenu();
10332 }
10333 HelpMarker(
10334 desc: "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n"
10335 "- Drag from window title bar or their tab to dock/undock." "\n"
10336 "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n"
10337 "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n"
10338 "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)" "\n"
10339 "This demo app has nothing to do with enabling docking!" "\n\n"
10340 "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window." "\n\n"
10341 "Read comments in ShowExampleAppDockSpace() for more details.");
10342
10343 ImGui::EndMenuBar();
10344 }
10345
10346 ImGui::End();
10347}
10348
10349//-----------------------------------------------------------------------------
10350// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
10351//-----------------------------------------------------------------------------
10352
10353// Simplified structure to mimic a Document model
10354struct MyDocument
10355{
10356 char Name[32]; // Document title
10357 int UID; // Unique ID (necessary as we can change title)
10358 bool Open; // Set when open (we keep an array of all available documents to simplify demo code!)
10359 bool OpenPrev; // Copy of Open from last update.
10360 bool Dirty; // Set when the document has been modified
10361 ImVec4 Color; // An arbitrary variable associated to the document
10362
10363 MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
10364 {
10365 UID = uid;
10366 snprintf(s: Name, maxlen: sizeof(Name), format: "%s", name);
10367 Open = OpenPrev = open;
10368 Dirty = false;
10369 Color = color;
10370 }
10371 void DoOpen() { Open = true; }
10372 void DoForceClose() { Open = false; Dirty = false; }
10373 void DoSave() { Dirty = false; }
10374};
10375
10376struct ExampleAppDocuments
10377{
10378 ImVector<MyDocument> Documents;
10379 ImVector<MyDocument*> CloseQueue;
10380 MyDocument* RenamingDoc = NULL;
10381 bool RenamingStarted = false;
10382
10383 ExampleAppDocuments()
10384 {
10385 Documents.push_back(v: MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
10386 Documents.push_back(v: MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
10387 Documents.push_back(v: MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
10388 Documents.push_back(v: MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));
10389 Documents.push_back(v: MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f)));
10390 Documents.push_back(v: MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f)));
10391 }
10392
10393 // As we allow to change document name, we append a never-changing document ID so tabs are stable
10394 void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size)
10395 {
10396 snprintf(s: out_buf, maxlen: out_buf_size, format: "%s###doc%d", doc->Name, doc->UID);
10397 }
10398
10399 // Display placeholder contents for the Document
10400 void DisplayDocContents(MyDocument* doc)
10401 {
10402 ImGui::PushID(ptr_id: doc);
10403 ImGui::Text(fmt: "Document \"%s\"", doc->Name);
10404 ImGui::PushStyleColor(idx: ImGuiCol_Text, col: doc->Color);
10405 ImGui::TextWrapped(fmt: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
10406 ImGui::PopStyleColor();
10407
10408 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_R, flags: ImGuiInputFlags_Tooltip);
10409 if (ImGui::Button(label: "Rename.."))
10410 {
10411 RenamingDoc = doc;
10412 RenamingStarted = true;
10413 }
10414 ImGui::SameLine();
10415
10416 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_M, flags: ImGuiInputFlags_Tooltip);
10417 if (ImGui::Button(label: "Modify"))
10418 doc->Dirty = true;
10419
10420 ImGui::SameLine();
10421 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_S, flags: ImGuiInputFlags_Tooltip);
10422 if (ImGui::Button(label: "Save"))
10423 doc->DoSave();
10424
10425 ImGui::SameLine();
10426 ImGui::SetNextItemShortcut(key_chord: ImGuiMod_Ctrl | ImGuiKey_W, flags: ImGuiInputFlags_Tooltip);
10427 if (ImGui::Button(label: "Close"))
10428 CloseQueue.push_back(v: doc);
10429 ImGui::ColorEdit3(label: "color", col: &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior.
10430 ImGui::PopID();
10431 }
10432
10433 // Display context menu for the Document
10434 void DisplayDocContextMenu(MyDocument* doc)
10435 {
10436 if (!ImGui::BeginPopupContextItem())
10437 return;
10438
10439 char buf[256];
10440 sprintf(s: buf, format: "Save %s", doc->Name);
10441 if (ImGui::MenuItem(label: buf, shortcut: "Ctrl+S", selected: false, enabled: doc->Open))
10442 doc->DoSave();
10443 if (ImGui::MenuItem(label: "Rename...", shortcut: "Ctrl+R", selected: false, enabled: doc->Open))
10444 RenamingDoc = doc;
10445 if (ImGui::MenuItem(label: "Close", shortcut: "Ctrl+W", selected: false, enabled: doc->Open))
10446 CloseQueue.push_back(v: doc);
10447 ImGui::EndPopup();
10448 }
10449
10450 // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
10451 // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
10452 // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
10453 // 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
10454 // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
10455 // give the impression of a flicker for one frame.
10456 // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
10457 // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
10458 void NotifyOfDocumentsClosedElsewhere()
10459 {
10460 for (MyDocument& doc : Documents)
10461 {
10462 if (!doc.Open && doc.OpenPrev)
10463 ImGui::SetTabItemClosed(doc.Name);
10464 doc.OpenPrev = doc.Open;
10465 }
10466 }
10467};
10468
10469void ShowExampleAppDocuments(bool* p_open)
10470{
10471 static ExampleAppDocuments app;
10472
10473 // Options
10474 enum Target
10475 {
10476 Target_None,
10477 Target_Tab, // Create documents as local tab into a local tab bar
10478 Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace
10479 };
10480 static Target opt_target = Target_Tab;
10481 static bool opt_reorderable = true;
10482 static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;
10483
10484 // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant")
10485 // that we emit gets docked into the same spot as the parent window ("Example: Documents").
10486 // This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab
10487 // not visible, which in turn would stop submitting the "Eggplant" window.
10488 // We avoid this problem by submitting our documents window even if our parent window is not currently visible.
10489 // Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking.
10490
10491 bool window_contents_visible = ImGui::Begin(name: "Example: Documents", p_open, flags: ImGuiWindowFlags_MenuBar);
10492 if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow)
10493 {
10494 ImGui::End();
10495 return;
10496 }
10497
10498 // Menu
10499 if (ImGui::BeginMenuBar())
10500 {
10501 if (ImGui::BeginMenu(label: "File"))
10502 {
10503 int open_count = 0;
10504 for (MyDocument& doc : app.Documents)
10505 open_count += doc.Open ? 1 : 0;
10506
10507 if (ImGui::BeginMenu(label: "Open", enabled: open_count < app.Documents.Size))
10508 {
10509 for (MyDocument& doc : app.Documents)
10510 if (!doc.Open && ImGui::MenuItem(label: doc.Name))
10511 doc.DoOpen();
10512 ImGui::EndMenu();
10513 }
10514 if (ImGui::MenuItem(label: "Close All Documents", NULL, selected: false, enabled: open_count > 0))
10515 for (MyDocument& doc : app.Documents)
10516 app.CloseQueue.push_back(v: &doc);
10517 if (ImGui::MenuItem(label: "Exit") && p_open)
10518 *p_open = false;
10519 ImGui::EndMenu();
10520 }
10521 ImGui::EndMenuBar();
10522 }
10523
10524 // [Debug] List documents with one checkbox for each
10525 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
10526 {
10527 MyDocument& doc = app.Documents[doc_n];
10528 if (doc_n > 0)
10529 ImGui::SameLine();
10530 ImGui::PushID(ptr_id: &doc);
10531 if (ImGui::Checkbox(label: doc.Name, v: &doc.Open))
10532 if (!doc.Open)
10533 doc.DoForceClose();
10534 ImGui::PopID();
10535 }
10536 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * 12);
10537 ImGui::Combo(label: "Output", current_item: (int*)&opt_target, items_separated_by_zeros: "None\0TabBar+Tabs\0DockSpace+Window\0");
10538 ImGui::PopItemWidth();
10539 bool redock_all = false;
10540 if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox(label: "Reorderable Tabs", v: &opt_reorderable); }
10541 if (opt_target == Target_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button(label: "Redock all"); }
10542
10543 ImGui::Separator();
10544
10545 // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.
10546 // They have multiple effects:
10547 // - Display a dot next to the title.
10548 // - Tab is selected when clicking the X close button.
10549 // - Closure is not assumed (will wait for user to stop submitting the tab).
10550 // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
10551 // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty
10552 // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.
10553 // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
10554
10555 // Tabs
10556 if (opt_target == Target_Tab)
10557 {
10558 ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);
10559 tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;
10560 if (ImGui::BeginTabBar(str_id: "##tabs", flags: tab_bar_flags))
10561 {
10562 if (opt_reorderable)
10563 app.NotifyOfDocumentsClosedElsewhere();
10564
10565 // [DEBUG] Stress tests
10566 //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.
10567 //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..
10568
10569 // Submit Tabs
10570 for (MyDocument& doc : app.Documents)
10571 {
10572 if (!doc.Open)
10573 continue;
10574
10575 // As we allow to change document name, we append a never-changing document id so tabs are stable
10576 char doc_name_buf[64];
10577 app.GetTabName(doc: &doc, out_buf: doc_name_buf, out_buf_size: sizeof(doc_name_buf));
10578 ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);
10579 bool visible = ImGui::BeginTabItem(label: doc_name_buf, p_open: &doc.Open, flags: tab_flags);
10580
10581 // Cancel attempt to close when unsaved add to save queue so we can display a popup.
10582 if (!doc.Open && doc.Dirty)
10583 {
10584 doc.Open = true;
10585 app.CloseQueue.push_back(v: &doc);
10586 }
10587
10588 app.DisplayDocContextMenu(doc: &doc);
10589 if (visible)
10590 {
10591 app.DisplayDocContents(doc: &doc);
10592 ImGui::EndTabItem();
10593 }
10594 }
10595
10596 ImGui::EndTabBar();
10597 }
10598 }
10599 else if (opt_target == Target_DockSpaceAndWindow)
10600 {
10601 if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)
10602 {
10603 app.NotifyOfDocumentsClosedElsewhere();
10604
10605 // Create a DockSpace node where any window can be docked
10606 ImGuiID dockspace_id = ImGui::GetID(str_id: "MyDockSpace");
10607 ImGui::DockSpace(dockspace_id);
10608
10609 // Create Windows
10610 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
10611 {
10612 MyDocument* doc = &app.Documents[doc_n];
10613 if (!doc->Open)
10614 continue;
10615
10616 ImGui::SetNextWindowDockID(dock_id: dockspace_id, cond: redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver);
10617 ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0);
10618 bool visible = ImGui::Begin(name: doc->Name, p_open: &doc->Open, flags: window_flags);
10619
10620 // Cancel attempt to close when unsaved add to save queue so we can display a popup.
10621 if (!doc->Open && doc->Dirty)
10622 {
10623 doc->Open = true;
10624 app.CloseQueue.push_back(v: doc);
10625 }
10626
10627 app.DisplayDocContextMenu(doc);
10628 if (visible)
10629 app.DisplayDocContents(doc);
10630
10631 ImGui::End();
10632 }
10633 }
10634 else
10635 {
10636 ShowDockingDisabledMessage();
10637 }
10638 }
10639
10640 // Early out other contents
10641 if (!window_contents_visible)
10642 {
10643 ImGui::End();
10644 return;
10645 }
10646
10647 // Display renaming UI
10648 if (app.RenamingDoc != NULL)
10649 {
10650 if (app.RenamingStarted)
10651 ImGui::OpenPopup(str_id: "Rename");
10652 if (ImGui::BeginPopup(str_id: "Rename"))
10653 {
10654 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30);
10655 if (ImGui::InputText(label: "###Name", buf: app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), flags: ImGuiInputTextFlags_EnterReturnsTrue))
10656 {
10657 ImGui::CloseCurrentPopup();
10658 app.RenamingDoc = NULL;
10659 }
10660 if (app.RenamingStarted)
10661 ImGui::SetKeyboardFocusHere(-1);
10662 ImGui::EndPopup();
10663 }
10664 else
10665 {
10666 app.RenamingDoc = NULL;
10667 }
10668 app.RenamingStarted = false;
10669 }
10670
10671 // Display closing confirmation UI
10672 if (!app.CloseQueue.empty())
10673 {
10674 int close_queue_unsaved_documents = 0;
10675 for (int n = 0; n < app.CloseQueue.Size; n++)
10676 if (app.CloseQueue[n]->Dirty)
10677 close_queue_unsaved_documents++;
10678
10679 if (close_queue_unsaved_documents == 0)
10680 {
10681 // Close documents when all are unsaved
10682 for (int n = 0; n < app.CloseQueue.Size; n++)
10683 app.CloseQueue[n]->DoForceClose();
10684 app.CloseQueue.clear();
10685 }
10686 else
10687 {
10688 if (!ImGui::IsPopupOpen(str_id: "Save?"))
10689 ImGui::OpenPopup(str_id: "Save?");
10690 if (ImGui::BeginPopupModal(name: "Save?", NULL, flags: ImGuiWindowFlags_AlwaysAutoResize))
10691 {
10692 ImGui::Text(fmt: "Save change to the following items?");
10693 float item_height = ImGui::GetTextLineHeightWithSpacing();
10694 if (ImGui::BeginChild(id: ImGui::GetID(str_id: "frame"), size: ImVec2(-FLT_MIN, 6.25f * item_height), child_flags: ImGuiChildFlags_FrameStyle))
10695 for (MyDocument* doc : app.CloseQueue)
10696 if (doc->Dirty)
10697 ImGui::Text(fmt: "%s", doc->Name);
10698 ImGui::EndChild();
10699
10700 ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
10701 if (ImGui::Button(label: "Yes", size: button_size))
10702 {
10703 for (MyDocument* doc : app.CloseQueue)
10704 {
10705 if (doc->Dirty)
10706 doc->DoSave();
10707 doc->DoForceClose();
10708 }
10709 app.CloseQueue.clear();
10710 ImGui::CloseCurrentPopup();
10711 }
10712 ImGui::SameLine();
10713 if (ImGui::Button(label: "No", size: button_size))
10714 {
10715 for (MyDocument* doc : app.CloseQueue)
10716 doc->DoForceClose();
10717 app.CloseQueue.clear();
10718 ImGui::CloseCurrentPopup();
10719 }
10720 ImGui::SameLine();
10721 if (ImGui::Button(label: "Cancel", size: button_size))
10722 {
10723 app.CloseQueue.clear();
10724 ImGui::CloseCurrentPopup();
10725 }
10726 ImGui::EndPopup();
10727 }
10728 }
10729 }
10730
10731 ImGui::End();
10732}
10733
10734//-----------------------------------------------------------------------------
10735// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
10736//-----------------------------------------------------------------------------
10737
10738//#include "imgui_internal.h" // NavMoveRequestTryWrapping()
10739
10740struct ExampleAsset
10741{
10742 ImGuiID ID;
10743 int Type;
10744
10745 ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; }
10746
10747 static const ImGuiTableSortSpecs* s_current_sort_specs;
10748
10749 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count)
10750 {
10751 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
10752 if (items_count > 1)
10753 qsort(base: items, nmemb: (size_t)items_count, size: sizeof(items[0]), compar: ExampleAsset::CompareWithSortSpecs);
10754 s_current_sort_specs = NULL;
10755 }
10756
10757 // Compare function to be used by qsort()
10758 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
10759 {
10760 const ExampleAsset* a = (const ExampleAsset*)lhs;
10761 const ExampleAsset* b = (const ExampleAsset*)rhs;
10762 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
10763 {
10764 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
10765 int delta = 0;
10766 if (sort_spec->ColumnIndex == 0)
10767 delta = ((int)a->ID - (int)b->ID);
10768 else if (sort_spec->ColumnIndex == 1)
10769 delta = (a->Type - b->Type);
10770 if (delta > 0)
10771 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
10772 if (delta < 0)
10773 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
10774 }
10775 return ((int)a->ID - (int)b->ID);
10776 }
10777};
10778const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL;
10779
10780struct ExampleAssetsBrowser
10781{
10782 // Options
10783 bool ShowTypeOverlay = true;
10784 bool AllowSorting = true;
10785 bool AllowDragUnselected = false;
10786 bool AllowBoxSelect = true;
10787 float IconSize = 32.0f;
10788 int IconSpacing = 10;
10789 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.
10790 bool StretchSpacing = true;
10791
10792 // State
10793 ImVector<ExampleAsset> Items; // Our items
10794 ExampleSelectionWithDeletion Selection; // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion)
10795 ImGuiID NextItemId = 0; // Unique identifier when creating new items
10796 bool RequestDelete = false; // Deferred deletion request
10797 bool RequestSort = false; // Deferred sort request
10798 float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better
10799
10800 // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way.
10801 ImVec2 LayoutItemSize;
10802 ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing
10803 float LayoutItemSpacing = 0.0f;
10804 float LayoutSelectableSpacing = 0.0f;
10805 float LayoutOuterPadding = 0.0f;
10806 int LayoutColumnCount = 0;
10807 int LayoutLineCount = 0;
10808
10809 // Functions
10810 ExampleAssetsBrowser()
10811 {
10812 AddItems(count: 10000);
10813 }
10814 void AddItems(int count)
10815 {
10816 if (Items.Size == 0)
10817 NextItemId = 0;
10818 Items.reserve(new_capacity: Items.Size + count);
10819 for (int n = 0; n < count; n++, NextItemId++)
10820 Items.push_back(v: ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2));
10821 RequestSort = true;
10822 }
10823 void ClearItems()
10824 {
10825 Items.clear();
10826 Selection.Clear();
10827 }
10828
10829 // Logic would be written in the main code BeginChild() and outputting to local variables.
10830 // We extracted it into a function so we can call it easily from multiple places.
10831 void UpdateLayoutSizes(float avail_width)
10832 {
10833 // Layout: when not stretching: allow extending into right-most spacing.
10834 LayoutItemSpacing = (float)IconSpacing;
10835 if (StretchSpacing == false)
10836 avail_width += floorf(x: LayoutItemSpacing * 0.5f);
10837
10838 // Layout: calculate number of icon per line and number of lines
10839 LayoutItemSize = ImVec2(floorf(x: IconSize), floorf(x: IconSize));
10840 LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);
10841 LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;
10842
10843 // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.
10844 if (StretchSpacing && LayoutColumnCount > 1)
10845 LayoutItemSpacing = floorf(x: avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount;
10846
10847 LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing);
10848 LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);
10849 LayoutOuterPadding = floorf(x: LayoutItemSpacing * 0.5f);
10850 }
10851
10852 void Draw(const char* title, bool* p_open)
10853 {
10854 ImGui::SetNextWindowSize(size: ImVec2(IconSize * 25, IconSize * 15), cond: ImGuiCond_FirstUseEver);
10855 if (!ImGui::Begin(name: title, p_open, flags: ImGuiWindowFlags_MenuBar))
10856 {
10857 ImGui::End();
10858 return;
10859 }
10860
10861 // Menu bar
10862 if (ImGui::BeginMenuBar())
10863 {
10864 if (ImGui::BeginMenu(label: "File"))
10865 {
10866 if (ImGui::MenuItem(label: "Add 10000 items"))
10867 AddItems(count: 10000);
10868 if (ImGui::MenuItem(label: "Clear items"))
10869 ClearItems();
10870 ImGui::Separator();
10871 if (ImGui::MenuItem(label: "Close", NULL, selected: false, enabled: p_open != NULL))
10872 *p_open = false;
10873 ImGui::EndMenu();
10874 }
10875 if (ImGui::BeginMenu(label: "Edit"))
10876 {
10877 if (ImGui::MenuItem(label: "Delete", shortcut: "Del", selected: false, enabled: Selection.Size > 0))
10878 RequestDelete = true;
10879 ImGui::EndMenu();
10880 }
10881 if (ImGui::BeginMenu(label: "Options"))
10882 {
10883 ImGui::PushItemWidth(item_width: ImGui::GetFontSize() * 10);
10884
10885 ImGui::SeparatorText(label: "Contents");
10886 ImGui::Checkbox(label: "Show Type Overlay", v: &ShowTypeOverlay);
10887 ImGui::Checkbox(label: "Allow Sorting", v: &AllowSorting);
10888
10889 ImGui::SeparatorText(label: "Selection Behavior");
10890 ImGui::Checkbox(label: "Allow dragging unselected item", v: &AllowDragUnselected);
10891 ImGui::Checkbox(label: "Allow box-selection", v: &AllowBoxSelect);
10892
10893 ImGui::SeparatorText(label: "Layout");
10894 ImGui::SliderFloat(label: "Icon Size", v: &IconSize, v_min: 16.0f, v_max: 128.0f, format: "%.0f");
10895 ImGui::SameLine(); HelpMarker(desc: "Use CTRL+Wheel to zoom");
10896 ImGui::SliderInt(label: "Icon Spacing", v: &IconSpacing, v_min: 0, v_max: 32);
10897 ImGui::SliderInt(label: "Icon Hit Spacing", v: &IconHitSpacing, v_min: 0, v_max: 32);
10898 ImGui::Checkbox(label: "Stretch Spacing", v: &StretchSpacing);
10899 ImGui::PopItemWidth();
10900 ImGui::EndMenu();
10901 }
10902 ImGui::EndMenuBar();
10903 }
10904
10905 // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI
10906 if (AllowSorting)
10907 {
10908 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(0, 0));
10909 ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders;
10910 if (ImGui::BeginTable(str_id: "for_sort_specs_only", columns: 2, flags: table_flags_for_sort_specs, outer_size: ImVec2(0.0f, ImGui::GetFrameHeight())))
10911 {
10912 ImGui::TableSetupColumn(label: "Index");
10913 ImGui::TableSetupColumn(label: "Type");
10914 ImGui::TableHeadersRow();
10915 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
10916 if (sort_specs->SpecsDirty || RequestSort)
10917 {
10918 ExampleAsset::SortWithSortSpecs(sort_specs, items: Items.Data, items_count: Items.Size);
10919 sort_specs->SpecsDirty = RequestSort = false;
10920 }
10921 ImGui::EndTable();
10922 }
10923 ImGui::PopStyleVar();
10924 }
10925
10926 ImGuiIO& io = ImGui::GetIO();
10927 ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));
10928 if (ImGui::BeginChild(str_id: "Assets", size: ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), child_flags: ImGuiChildFlags_Borders, window_flags: ImGuiWindowFlags_NoMove))
10929 {
10930 ImDrawList* draw_list = ImGui::GetWindowDrawList();
10931
10932 const float avail_width = ImGui::GetContentRegionAvail().x;
10933 UpdateLayoutSizes(avail_width);
10934
10935 // Calculate and store start position.
10936 ImVec2 start_pos = ImGui::GetCursorScreenPos();
10937 start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding);
10938 ImGui::SetCursorScreenPos(start_pos);
10939
10940 // Multi-select
10941 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid;
10942
10943 // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items)
10944 if (AllowBoxSelect)
10945 ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d;
10946
10947 // - This feature allows dragging an unselected item without selecting it (rarely used)
10948 if (AllowDragUnselected)
10949 ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease;
10950
10951 // - Enable keyboard wrapping on X axis
10952 // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing:
10953 // ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
10954 // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system)
10955 ms_flags |= ImGuiMultiSelectFlags_NavWrapX;
10956
10957 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags: ms_flags, selection_size: Selection.Size, items_count: Items.Size);
10958
10959 // Use custom selection adapter: store ID in selection (recommended)
10960 Selection.UserData = this;
10961 Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; };
10962 Selection.ApplyRequests(ms_io);
10963
10964 const bool want_delete = (ImGui::Shortcut(key_chord: ImGuiKey_Delete, flags: ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete;
10965 const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, items_count: Items.Size) : -1;
10966 RequestDelete = false;
10967
10968 // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items)
10969 // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...
10970 // But it is necessary for two reasons:
10971 // - Selectables uses it by default to visually fill the space between two items.
10972 // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do).
10973 ImGui::PushStyleVar(idx: ImGuiStyleVar_ItemSpacing, val: ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing));
10974
10975 // Rendering parameters
10976 const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) };
10977 const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220));
10978 const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f);
10979 const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize(text: "999").x);
10980
10981 const int column_count = LayoutColumnCount;
10982 ImGuiListClipper clipper;
10983 clipper.Begin(items_count: LayoutLineCount, items_height: LayoutItemStep.y);
10984 if (item_curr_idx_to_focus != -1)
10985 clipper.IncludeItemByIndex(item_index: item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped.
10986 if (ms_io->RangeSrcItem != -1)
10987 clipper.IncludeItemByIndex(item_index: (int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped.
10988 while (clipper.Step())
10989 {
10990 for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++)
10991 {
10992 const int item_min_idx_for_current_line = line_idx * column_count;
10993 const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size);
10994 for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx)
10995 {
10996 ExampleAsset* item_data = &Items[item_idx];
10997 ImGui::PushID(int_id: (int)item_data->ID);
10998
10999 // Position item
11000 ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y);
11001 ImGui::SetCursorScreenPos(pos);
11002
11003 ImGui::SetNextItemSelectionUserData(item_idx);
11004 bool item_is_selected = Selection.Contains(id: (ImGuiID)item_data->ID);
11005 bool item_is_visible = ImGui::IsRectVisible(size: LayoutItemSize);
11006 ImGui::Selectable(label: "", selected: item_is_selected, flags: ImGuiSelectableFlags_None, size: LayoutItemSize);
11007
11008 // Update our selection state immediately (without waiting for EndMultiSelect() requests)
11009 // because we use this to alter the color of our text/icon.
11010 if (ImGui::IsItemToggledSelection())
11011 item_is_selected = !item_is_selected;
11012
11013 // Focus (for after deletion)
11014 if (item_curr_idx_to_focus == item_idx)
11015 ImGui::SetKeyboardFocusHere(-1);
11016
11017 // Drag and drop
11018 if (ImGui::BeginDragDropSource())
11019 {
11020 // Create payload with full selection OR single unselected item.
11021 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
11022 if (ImGui::GetDragDropPayload() == NULL)
11023 {
11024 ImVector<ImGuiID> payload_items;
11025 void* it = NULL;
11026 ImGuiID id = 0;
11027 if (!item_is_selected)
11028 payload_items.push_back(v: item_data->ID);
11029 else
11030 while (Selection.GetNextSelectedItem(opaque_it: &it, out_id: &id))
11031 payload_items.push_back(v: id);
11032 ImGui::SetDragDropPayload(type: "ASSETS_BROWSER_ITEMS", data: payload_items.Data, sz: (size_t)payload_items.size_in_bytes());
11033 }
11034
11035 // Display payload content in tooltip, by extracting it from the payload data
11036 // (we could read from selection, but it is more correct and reusable to read from payload)
11037 const ImGuiPayload* payload = ImGui::GetDragDropPayload();
11038 const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID);
11039 ImGui::Text(fmt: "%d assets", payload_count);
11040
11041 ImGui::EndDragDropSource();
11042 }
11043
11044 // Render icon (a real app would likely display an image/thumbnail here)
11045 // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well.
11046 if (item_is_visible)
11047 {
11048 ImVec2 box_min(pos.x - 1, pos.y - 1);
11049 ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious
11050 draw_list->AddRectFilled(p_min: box_min, p_max: box_max, col: icon_bg_color); // Background color
11051 if (ShowTypeOverlay && item_data->Type != 0)
11052 {
11053 ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)];
11054 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);
11055 }
11056 if (display_label)
11057 {
11058 ImU32 label_col = ImGui::GetColorU32(idx: item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled);
11059 char label[32];
11060 sprintf(s: label, format: "%d", item_data->ID);
11061 draw_list->AddText(pos: ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), col: label_col, text_begin: label);
11062 }
11063 }
11064
11065 ImGui::PopID();
11066 }
11067 }
11068 }
11069 clipper.End();
11070 ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
11071
11072 // Context menu
11073 if (ImGui::BeginPopupContextWindow())
11074 {
11075 ImGui::Text(fmt: "Selection: %d items", Selection.Size);
11076 ImGui::Separator();
11077 if (ImGui::MenuItem(label: "Delete", shortcut: "Del", selected: false, enabled: Selection.Size > 0))
11078 RequestDelete = true;
11079 ImGui::EndPopup();
11080 }
11081
11082 ms_io = ImGui::EndMultiSelect();
11083 Selection.ApplyRequests(ms_io);
11084 if (want_delete)
11085 Selection.ApplyDeletionPostLoop(ms_io, items&: Items, item_curr_idx_to_select: item_curr_idx_to_focus);
11086
11087 // Zooming with CTRL+Wheel
11088 if (ImGui::IsWindowAppearing())
11089 ZoomWheelAccum = 0.0f;
11090 if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(key: ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false)
11091 {
11092 ZoomWheelAccum += io.MouseWheel;
11093 if (fabsf(x: ZoomWheelAccum) >= 1.0f)
11094 {
11095 // Calculate hovered item index from mouse location
11096 // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it.
11097 const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x;
11098 const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y;
11099 const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx;
11100 //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
11101
11102 // Zoom
11103 IconSize *= powf(x: 1.1f, y: (float)(int)ZoomWheelAccum);
11104 IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f);
11105 ZoomWheelAccum -= (int)ZoomWheelAccum;
11106 UpdateLayoutSizes(avail_width);
11107
11108 // Manipulate scroll to that we will land at the same Y location of currently hovered item.
11109 // - Calculate next frame position of item under mouse
11110 // - Set new scroll position to be used in next ImGui::BeginChild() call.
11111 float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(x: hovered_item_ny, y: 1.0f)) * LayoutItemStep.y;
11112 hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y;
11113 float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y;
11114 ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y);
11115 }
11116 }
11117 }
11118 ImGui::EndChild();
11119
11120 ImGui::Text(fmt: "Selected: %d/%d items", Selection.Size, Items.Size);
11121 ImGui::End();
11122 }
11123};
11124
11125void ShowExampleAppAssetsBrowser(bool* p_open)
11126{
11127 IMGUI_DEMO_MARKER("Examples/Assets Browser");
11128 static ExampleAssetsBrowser assets_browser;
11129 assets_browser.Draw(title: "Example: Assets Browser", p_open);
11130}
11131
11132// End of Demo code
11133#else
11134
11135void ImGui::ShowAboutWindow(bool*) {}
11136void ImGui::ShowDemoWindow(bool*) {}
11137void ImGui::ShowUserGuide() {}
11138void ImGui::ShowStyleEditor(ImGuiStyle*) {}
11139bool ImGui::ShowStyleSelector(const char*) { return false; }
11140
11141#endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS
11142
11143#endif // #ifndef IMGUI_DISABLE
11144

source code of imgui/imgui_demo.cpp