imgui_demo.cpp (538481B)
1 // dear imgui, v1.91.0 2 // (demo code) 3 4 // Help: 5 // - Read FAQ at http://dearimgui.com/faq 6 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. 7 // - Need help integrating Dear ImGui in your codebase? 8 // - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started 9 // - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. 10 // Read top of imgui.cpp and imgui.h for many details, documentation, comments, links. 11 // Get the latest version at https://github.com/ocornut/imgui 12 13 // How to easily locate code? 14 // - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools 15 // - Browse an online version the demo with code linked to hovered widgets: https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html 16 // - Find a visible string and search for it in the code! 17 18 //--------------------------------------------------- 19 // PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT! 20 //--------------------------------------------------- 21 // Message to the person tempted to delete this file when integrating Dear ImGui into their codebase: 22 // Think again! It is the most useful reference code that you and other coders will want to refer to and call. 23 // Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of your game/app! 24 // Also include Metrics! ItemPicker! DebugLog! and other debug features. 25 // Removing this file from your project is hindering access to documentation for everyone in your team, 26 // likely leading you to poorer usage of the library. 27 // Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow(). 28 // If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be 29 // linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty. 30 // In another situation, whenever you have Dear ImGui available you probably want this to be available for reference. 31 // Thank you, 32 // -Your beloved friend, imgui_demo.cpp (which you won't delete) 33 34 //-------------------------------------------- 35 // ABOUT THE MEANING OF THE 'static' KEYWORD: 36 //-------------------------------------------- 37 // In this demo code, we frequently use 'static' variables inside functions. 38 // A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function. 39 // Think of "static int n = 0;" as "global int n = 0;" ! 40 // We do this IN THE DEMO because we want: 41 // - to gather code and data in the same place. 42 // - to make the demo source code faster to read, faster to change, smaller in size. 43 // - it is also a convenient way of storing simple UI related information as long as your function 44 // doesn't need to be reentrant or used in multiple threads. 45 // This might be a pattern you will want to use in your code, but most of the data you would be working 46 // with in a complex codebase is likely going to be stored outside your functions. 47 48 //----------------------------------------- 49 // ABOUT THE CODING STYLE OF OUR DEMO CODE 50 //----------------------------------------- 51 // The Demo code in this file is designed to be easy to copy-and-paste into your application! 52 // Because of this: 53 // - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace. 54 // - We try to declare static variables in the local scope, as close as possible to the code using them. 55 // - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API. 56 // - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided 57 // by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional 58 // and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h. 59 // Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp. 60 61 // Navigating this file: 62 // - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. 63 // - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. 64 // - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. 65 // - You can search/grep for all sections listed in the index to find the section. 66 67 /* 68 69 Index of this file: 70 71 // [SECTION] Forward Declarations 72 // [SECTION] Helpers 73 // [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos) 74 // [SECTION] Demo Window / ShowDemoWindow() 75 // [SECTION] ShowDemoWindowMenuBar() 76 // [SECTION] ShowDemoWindowWidgets() 77 // [SECTION] ShowDemoWindowMultiSelect() 78 // [SECTION] ShowDemoWindowLayout() 79 // [SECTION] ShowDemoWindowPopups() 80 // [SECTION] ShowDemoWindowTables() 81 // [SECTION] ShowDemoWindowInputs() 82 // [SECTION] About Window / ShowAboutWindow() 83 // [SECTION] Style Editor / ShowStyleEditor() 84 // [SECTION] User Guide / ShowUserGuide() 85 // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() 86 // [SECTION] Example App: Debug Console / ShowExampleAppConsole() 87 // [SECTION] Example App: Debug Log / ShowExampleAppLog() 88 // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() 89 // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() 90 // [SECTION] Example App: Long Text / ShowExampleAppLongText() 91 // [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() 92 // [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() 93 // [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay() 94 // [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen() 95 // [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles() 96 // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() 97 // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() 98 // [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser() 99 100 */ 101 102 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 103 #define _CRT_SECURE_NO_WARNINGS 104 #endif 105 106 #include "imgui.h" 107 #ifndef IMGUI_DISABLE 108 109 // System includes 110 #include <ctype.h> // toupper 111 #include <limits.h> // INT_MIN, INT_MAX 112 #include <math.h> // sqrtf, powf, cosf, sinf, floorf, ceilf 113 #include <stdio.h> // vsnprintf, sscanf, printf 114 #include <stdlib.h> // NULL, malloc, free, atoi 115 #include <stdint.h> // intptr_t 116 #if !defined(_MSC_VER) || _MSC_VER >= 1800 117 #include <inttypes.h> // PRId64/PRIu64, not avail in some MinGW headers. 118 #endif 119 120 // Visual Studio warnings 121 #ifdef _MSC_VER 122 #pragma warning (disable: 4127) // condition expression is constant 123 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen 124 #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). 125 #endif 126 127 // Clang/GCC warnings with -Weverything 128 #if defined(__clang__) 129 #if __has_warning("-Wunknown-warning-option") 130 #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! 131 #endif 132 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' 133 #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. 134 #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) 135 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 136 #pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal 137 #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. 138 #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. 139 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 140 #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. 141 #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier 142 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision 143 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access 144 #elif defined(__GNUC__) 145 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind 146 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size 147 #pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure) 148 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function 149 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value 150 #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. 151 #endif 152 153 // Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!) 154 #ifdef _WIN32 155 #define IM_NEWLINE "\r\n" 156 #else 157 #define IM_NEWLINE "\n" 158 #endif 159 160 // Helpers 161 #if defined(_MSC_VER) && !defined(snprintf) 162 #define snprintf _snprintf 163 #endif 164 #if defined(_MSC_VER) && !defined(vsnprintf) 165 #define vsnprintf _vsnprintf 166 #endif 167 168 // Format specifiers for 64-bit values (hasn't been decently standardized before VS2013) 169 #if !defined(PRId64) && defined(_MSC_VER) 170 #define PRId64 "I64d" 171 #define PRIu64 "I64u" 172 #elif !defined(PRId64) 173 #define PRId64 "lld" 174 #define PRIu64 "llu" 175 #endif 176 177 // Helpers macros 178 // We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste, 179 // but making an exception here as those are largely simplifying code... 180 // In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo. 181 #define IM_MIN(A, B) (((A) < (B)) ? (A) : (B)) 182 #define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B)) 183 #define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V)) 184 185 // Enforce cdecl calling convention for functions called by the standard library, 186 // in case compilation settings changed the default to e.g. __vectorcall 187 #ifndef IMGUI_CDECL 188 #ifdef _MSC_VER 189 #define IMGUI_CDECL __cdecl 190 #else 191 #define IMGUI_CDECL 192 #endif 193 #endif 194 195 //----------------------------------------------------------------------------- 196 // [SECTION] Forward Declarations 197 //----------------------------------------------------------------------------- 198 199 #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) 200 201 // Forward Declarations 202 struct ImGuiDemoWindowData; 203 static void ShowExampleAppMainMenuBar(); 204 static void ShowExampleAppAssetsBrowser(bool* p_open); 205 static void ShowExampleAppConsole(bool* p_open); 206 static void ShowExampleAppCustomRendering(bool* p_open); 207 static void ShowExampleAppDocuments(bool* p_open); 208 static void ShowExampleAppLog(bool* p_open); 209 static void ShowExampleAppLayout(bool* p_open); 210 static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data); 211 static void ShowExampleAppSimpleOverlay(bool* p_open); 212 static void ShowExampleAppAutoResize(bool* p_open); 213 static void ShowExampleAppConstrainedResize(bool* p_open); 214 static void ShowExampleAppFullscreen(bool* p_open); 215 static void ShowExampleAppLongText(bool* p_open); 216 static void ShowExampleAppWindowTitles(bool* p_open); 217 static void ShowExampleMenuFile(); 218 219 // We split the contents of the big ShowDemoWindow() function into smaller functions 220 // (because the link time of very large functions tends to grow non-linearly) 221 static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data); 222 static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data); 223 static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data); 224 static void ShowDemoWindowLayout(); 225 static void ShowDemoWindowPopups(); 226 static void ShowDemoWindowTables(); 227 static void ShowDemoWindowColumns(); 228 static void ShowDemoWindowInputs(); 229 230 //----------------------------------------------------------------------------- 231 // [SECTION] Helpers 232 //----------------------------------------------------------------------------- 233 234 // Helper to display a little (?) mark which shows a tooltip when hovered. 235 // In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md) 236 static void HelpMarker(const char* desc) 237 { 238 ImGui::TextDisabled("(?)"); 239 if (ImGui::BeginItemTooltip()) 240 { 241 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); 242 ImGui::TextUnformatted(desc); 243 ImGui::PopTextWrapPos(); 244 ImGui::EndTooltip(); 245 } 246 } 247 248 // Helper to wire demo markers located in code to an interactive browser 249 typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data); 250 extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; 251 extern void* GImGuiDemoMarkerCallbackUserData; 252 ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL; 253 void* GImGuiDemoMarkerCallbackUserData = NULL; 254 #define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) 255 256 //----------------------------------------------------------------------------- 257 // [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) 258 //----------------------------------------------------------------------------- 259 260 // Simple representation for a tree 261 // (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.) 262 struct ExampleTreeNode 263 { 264 // Tree structure 265 char Name[28] = ""; 266 int UID = 0; 267 ExampleTreeNode* Parent = NULL; 268 ImVector<ExampleTreeNode*> Childs; 269 unsigned short IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily 270 271 // Leaf Data 272 bool HasData = false; // All leaves have data 273 bool DataMyBool = true; 274 int DataMyInt = 128; 275 ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f); 276 }; 277 278 // Simple representation of struct metadata/serialization data. 279 // (this is a minimal version of what a typical advanced application may provide) 280 struct ExampleMemberInfo 281 { 282 const char* Name; // Member name 283 ImGuiDataType DataType; // Member type 284 int DataCount; // Member count (1 when scalar) 285 int Offset; // Offset inside parent structure 286 }; 287 288 // Metadata description of ExampleTreeNode struct. 289 static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] 290 { 291 { "MyBool", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataMyBool) }, 292 { "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataMyInt) }, 293 { "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataMyVec2) }, 294 }; 295 296 static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent) 297 { 298 ExampleTreeNode* node = IM_NEW(ExampleTreeNode); 299 snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); 300 node->UID = uid; 301 node->Parent = parent; 302 node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0; 303 if (parent) 304 parent->Childs.push_back(node); 305 return node; 306 } 307 308 // Create example tree data 309 // (this allocates _many_ more times than most other code in either Dear ImGui or others demo) 310 static ExampleTreeNode* ExampleTree_CreateDemoTree() 311 { 312 static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" }; 313 char name_buf[32]; 314 int uid = 0; 315 ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL); 316 const int root_items_multiplier = 2; 317 for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++) 318 { 319 snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); 320 ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); 321 const int number_of_childs = (int)strlen(node_L1->Name); 322 for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) 323 { 324 snprintf(name_buf, 32, "Child %d", idx_L1); 325 ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); 326 node_L2->HasData = true; 327 if (idx_L1 == 0) 328 { 329 snprintf(name_buf, 32, "Sub-child %d", 0); 330 ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); 331 node_L3->HasData = true; 332 } 333 } 334 } 335 return node_L0; 336 } 337 338 //----------------------------------------------------------------------------- 339 // [SECTION] Demo Window / ShowDemoWindow() 340 //----------------------------------------------------------------------------- 341 342 // Data to be shared accross different functions of the demo. 343 struct ImGuiDemoWindowData 344 { 345 // Examples Apps (accessible from the "Examples" menu) 346 bool ShowMainMenuBar = false; 347 bool ShowAppAssetsBrowser = false; 348 bool ShowAppConsole = false; 349 bool ShowAppCustomRendering = false; 350 bool ShowAppDocuments = false; 351 bool ShowAppLog = false; 352 bool ShowAppLayout = false; 353 bool ShowAppPropertyEditor = false; 354 bool ShowAppSimpleOverlay = false; 355 bool ShowAppAutoResize = false; 356 bool ShowAppConstrainedResize = false; 357 bool ShowAppFullscreen = false; 358 bool ShowAppLongText = false; 359 bool ShowAppWindowTitles = false; 360 361 // Dear ImGui Tools (accessible from the "Tools" menu) 362 bool ShowMetrics = false; 363 bool ShowDebugLog = false; 364 bool ShowIDStackTool = false; 365 bool ShowStyleEditor = false; 366 bool ShowAbout = false; 367 368 // Other data 369 ExampleTreeNode* DemoTree = NULL; 370 }; 371 372 // Demonstrate most Dear ImGui features (this is big function!) 373 // You may execute this function to experiment with the UI and understand what it does. 374 // You may then search for keywords in the code when you are interested by a specific feature. 375 void ImGui::ShowDemoWindow(bool* p_open) 376 { 377 // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup 378 // Most functions would normally just assert/crash if the context is missing. 379 IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!"); 380 381 // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. 382 IMGUI_CHECKVERSION(); 383 384 // Stored data 385 static ImGuiDemoWindowData demo_data; 386 387 // Examples Apps (accessible from the "Examples" menu) 388 if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); } 389 if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); } 390 if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); } 391 if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); } 392 if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); } 393 if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); } 394 if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); } 395 if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); } 396 if (demo_data.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(&demo_data.ShowAppSimpleOverlay); } 397 if (demo_data.ShowAppAutoResize) { ShowExampleAppAutoResize(&demo_data.ShowAppAutoResize); } 398 if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo_data.ShowAppConstrainedResize); } 399 if (demo_data.ShowAppFullscreen) { ShowExampleAppFullscreen(&demo_data.ShowAppFullscreen); } 400 if (demo_data.ShowAppLongText) { ShowExampleAppLongText(&demo_data.ShowAppLongText); } 401 if (demo_data.ShowAppWindowTitles) { ShowExampleAppWindowTitles(&demo_data.ShowAppWindowTitles); } 402 403 // Dear ImGui Tools (accessible from the "Tools" menu) 404 if (demo_data.ShowMetrics) { ImGui::ShowMetricsWindow(&demo_data.ShowMetrics); } 405 if (demo_data.ShowDebugLog) { ImGui::ShowDebugLogWindow(&demo_data.ShowDebugLog); } 406 if (demo_data.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(&demo_data.ShowIDStackTool); } 407 if (demo_data.ShowAbout) { ImGui::ShowAboutWindow(&demo_data.ShowAbout); } 408 if (demo_data.ShowStyleEditor) 409 { 410 ImGui::Begin("Dear ImGui Style Editor", &demo_data.ShowStyleEditor); 411 ImGui::ShowStyleEditor(); 412 ImGui::End(); 413 } 414 415 // Demonstrate the various window flags. Typically you would just use the default! 416 static bool no_titlebar = false; 417 static bool no_scrollbar = false; 418 static bool no_menu = false; 419 static bool no_move = false; 420 static bool no_resize = false; 421 static bool no_collapse = false; 422 static bool no_close = false; 423 static bool no_nav = false; 424 static bool no_background = false; 425 static bool no_bring_to_front = false; 426 static bool unsaved_document = false; 427 428 ImGuiWindowFlags window_flags = 0; 429 if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; 430 if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; 431 if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; 432 if (no_move) window_flags |= ImGuiWindowFlags_NoMove; 433 if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; 434 if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; 435 if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; 436 if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; 437 if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; 438 if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument; 439 if (no_close) p_open = NULL; // Don't pass our bool* to Begin 440 441 // We specify a default position/size in case there's no data in the .ini file. 442 // We only do it to make the demo applications a little more welcoming, but typically this isn't required. 443 const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); 444 ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver); 445 ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); 446 447 // Main body of the Demo window starts here. 448 if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags)) 449 { 450 // Early out if the window is collapsed, as an optimization. 451 ImGui::End(); 452 return; 453 } 454 455 // Most "big" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details. 456 ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets. 457 //ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align) 458 459 // Menu Bar 460 ShowDemoWindowMenuBar(&demo_data); 461 462 ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); 463 ImGui::Spacing(); 464 465 IMGUI_DEMO_MARKER("Help"); 466 if (ImGui::CollapsingHeader("Help")) 467 { 468 ImGui::SeparatorText("ABOUT THIS DEMO:"); 469 ImGui::BulletText("Sections below are demonstrating many aspects of the library."); 470 ImGui::BulletText("The \"Examples\" menu above leads to more demo contents."); 471 ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n" 472 "and Metrics/Debugger (general purpose Dear ImGui debugging tool)."); 473 474 ImGui::SeparatorText("PROGRAMMER GUIDE:"); 475 ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!"); 476 ImGui::BulletText("See comments in imgui.cpp."); 477 ImGui::BulletText("See example applications in the examples/ folder."); 478 ImGui::BulletText("Read the FAQ at "); 479 ImGui::SameLine(0, 0); 480 ImGui::TextLinkOpenURL("https://www.dearimgui.com/faq/"); 481 ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls."); 482 ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls."); 483 484 ImGui::SeparatorText("USER GUIDE:"); 485 ImGui::ShowUserGuide(); 486 } 487 488 IMGUI_DEMO_MARKER("Configuration"); 489 if (ImGui::CollapsingHeader("Configuration")) 490 { 491 ImGuiIO& io = ImGui::GetIO(); 492 493 if (ImGui::TreeNode("Configuration##2")) 494 { 495 ImGui::SeparatorText("General"); 496 ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); 497 ImGui::SameLine(); HelpMarker("Enable keyboard controls."); 498 ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); 499 ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); 500 ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", &io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); 501 ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); 502 ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", &io.ConfigFlags, ImGuiConfigFlags_NoMouse); 503 ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable mouse inputs and interactions."); 504 505 // The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it: 506 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) 507 { 508 if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f) 509 { 510 ImGui::SameLine(); 511 ImGui::Text("<<PRESS SPACE TO DISABLE>>"); 512 } 513 // Prevent both being checked 514 if (ImGui::IsKeyPressed(ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)) 515 io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; 516 } 517 518 ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); 519 ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); 520 ImGui::CheckboxFlags("io.ConfigFlags: NoKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NoKeyboard); 521 ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable keyboard inputs and interactions."); 522 523 ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue); 524 ImGui::SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates."); 525 ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); 526 ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); 527 528 ImGui::SeparatorText("Widgets"); 529 ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); 530 ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)."); 531 ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive); 532 ImGui::SameLine(); HelpMarker("Pressing Enter will keep item active and select contents (single-line only)."); 533 ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText); 534 ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving)."); 535 ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); 536 ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); 537 ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); 538 ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors); 539 ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); 540 ImGui::Text("Also see Style->Rendering for rendering options."); 541 542 ImGui::SeparatorText("Debug"); 543 ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent); 544 ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application."); 545 ImGui::BeginDisabled(); 546 ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); // . 547 ImGui::EndDisabled(); 548 ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover."); 549 ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop); 550 ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running."); 551 ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss); 552 ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data."); 553 ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); 554 ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)."); 555 556 ImGui::TreePop(); 557 ImGui::Spacing(); 558 } 559 560 IMGUI_DEMO_MARKER("Configuration/Backend Flags"); 561 if (ImGui::TreeNode("Backend Flags")) 562 { 563 HelpMarker( 564 "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" 565 "Here we expose them as read-only fields to avoid breaking interactions with your backend."); 566 567 // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright? 568 ImGui::BeginDisabled(); 569 ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &io.BackendFlags, ImGuiBackendFlags_HasGamepad); 570 ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); 571 ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); 572 ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); 573 ImGui::EndDisabled(); 574 ImGui::TreePop(); 575 ImGui::Spacing(); 576 } 577 578 IMGUI_DEMO_MARKER("Configuration/Style"); 579 if (ImGui::TreeNode("Style")) 580 { 581 ImGui::Checkbox("Style Editor", &demo_data.ShowStyleEditor); 582 ImGui::SameLine(); 583 HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); 584 ImGui::TreePop(); 585 ImGui::Spacing(); 586 } 587 588 IMGUI_DEMO_MARKER("Configuration/Capture, Logging"); 589 if (ImGui::TreeNode("Capture/Logging")) 590 { 591 HelpMarker( 592 "The logging API redirects all text output so you can easily capture the content of " 593 "a window or a block. Tree nodes can be automatically expanded.\n" 594 "Try opening any of the contents below in this window and then click one of the \"Log To\" button."); 595 ImGui::LogButtons(); 596 597 HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output."); 598 if (ImGui::Button("Copy \"Hello, world!\" to clipboard")) 599 { 600 ImGui::LogToClipboard(); 601 ImGui::LogText("Hello, world!"); 602 ImGui::LogFinish(); 603 } 604 ImGui::TreePop(); 605 } 606 } 607 608 IMGUI_DEMO_MARKER("Window options"); 609 if (ImGui::CollapsingHeader("Window options")) 610 { 611 if (ImGui::BeginTable("split", 3)) 612 { 613 ImGui::TableNextColumn(); ImGui::Checkbox("No titlebar", &no_titlebar); 614 ImGui::TableNextColumn(); ImGui::Checkbox("No scrollbar", &no_scrollbar); 615 ImGui::TableNextColumn(); ImGui::Checkbox("No menu", &no_menu); 616 ImGui::TableNextColumn(); ImGui::Checkbox("No move", &no_move); 617 ImGui::TableNextColumn(); ImGui::Checkbox("No resize", &no_resize); 618 ImGui::TableNextColumn(); ImGui::Checkbox("No collapse", &no_collapse); 619 ImGui::TableNextColumn(); ImGui::Checkbox("No close", &no_close); 620 ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav); 621 ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); 622 ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); 623 ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); 624 ImGui::EndTable(); 625 } 626 } 627 628 // All demo contents 629 ShowDemoWindowWidgets(&demo_data); 630 ShowDemoWindowLayout(); 631 ShowDemoWindowPopups(); 632 ShowDemoWindowTables(); 633 ShowDemoWindowInputs(); 634 635 // End of ShowDemoWindow() 636 ImGui::PopItemWidth(); 637 ImGui::End(); 638 } 639 640 //----------------------------------------------------------------------------- 641 // [SECTION] ShowDemoWindowMenuBar() 642 //----------------------------------------------------------------------------- 643 644 static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data) 645 { 646 IMGUI_DEMO_MARKER("Menu"); 647 if (ImGui::BeginMenuBar()) 648 { 649 if (ImGui::BeginMenu("Menu")) 650 { 651 IMGUI_DEMO_MARKER("Menu/File"); 652 ShowExampleMenuFile(); 653 ImGui::EndMenu(); 654 } 655 if (ImGui::BeginMenu("Examples")) 656 { 657 IMGUI_DEMO_MARKER("Menu/Examples"); 658 ImGui::MenuItem("Main menu bar", NULL, &demo_data->ShowMainMenuBar); 659 660 ImGui::SeparatorText("Mini apps"); 661 ImGui::MenuItem("Assets Browser", NULL, &demo_data->ShowAppAssetsBrowser); 662 ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole); 663 ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering); 664 ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments); 665 ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog); 666 ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor); 667 ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout); 668 ImGui::MenuItem("Simple overlay", NULL, &demo_data->ShowAppSimpleOverlay); 669 670 ImGui::SeparatorText("Concepts"); 671 ImGui::MenuItem("Auto-resizing window", NULL, &demo_data->ShowAppAutoResize); 672 ImGui::MenuItem("Constrained-resizing window", NULL, &demo_data->ShowAppConstrainedResize); 673 ImGui::MenuItem("Fullscreen window", NULL, &demo_data->ShowAppFullscreen); 674 ImGui::MenuItem("Long text display", NULL, &demo_data->ShowAppLongText); 675 ImGui::MenuItem("Manipulating window titles", NULL, &demo_data->ShowAppWindowTitles); 676 677 ImGui::EndMenu(); 678 } 679 //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! 680 if (ImGui::BeginMenu("Tools")) 681 { 682 IMGUI_DEMO_MARKER("Menu/Tools"); 683 #ifndef IMGUI_DISABLE_DEBUG_TOOLS 684 const bool has_debug_tools = true; 685 #else 686 const bool has_debug_tools = false; 687 #endif 688 ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools); 689 ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools); 690 ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools); 691 ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor); 692 bool is_debugger_present = ImGui::GetIO().ConfigDebugIsDebuggerPresent; 693 if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present)) 694 ImGui::DebugStartItemPicker(); 695 if (!is_debugger_present) 696 ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools."); 697 ImGui::Separator(); 698 ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout); 699 ImGui::EndMenu(); 700 } 701 ImGui::EndMenuBar(); 702 } 703 } 704 705 //----------------------------------------------------------------------------- 706 // [SECTION] ShowDemoWindowWidgets() 707 //----------------------------------------------------------------------------- 708 709 static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) 710 { 711 IMGUI_DEMO_MARKER("Widgets"); 712 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); 713 if (!ImGui::CollapsingHeader("Widgets")) 714 return; 715 716 static bool disable_all = false; // The Checkbox for that is inside the "Disabled" section at the bottom 717 if (disable_all) 718 ImGui::BeginDisabled(); 719 720 IMGUI_DEMO_MARKER("Widgets/Basic"); 721 if (ImGui::TreeNode("Basic")) 722 { 723 ImGui::SeparatorText("General"); 724 725 IMGUI_DEMO_MARKER("Widgets/Basic/Button"); 726 static int clicked = 0; 727 if (ImGui::Button("Button")) 728 clicked++; 729 if (clicked & 1) 730 { 731 ImGui::SameLine(); 732 ImGui::Text("Thanks for clicking me!"); 733 } 734 735 IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox"); 736 static bool check = true; 737 ImGui::Checkbox("checkbox", &check); 738 739 IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton"); 740 static int e = 0; 741 ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); 742 ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); 743 ImGui::RadioButton("radio c", &e, 2); 744 745 // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. 746 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)"); 747 for (int i = 0; i < 7; i++) 748 { 749 if (i > 0) 750 ImGui::SameLine(); 751 ImGui::PushID(i); 752 ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); 753 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); 754 ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); 755 ImGui::Button("Click"); 756 ImGui::PopStyleColor(3); 757 ImGui::PopID(); 758 } 759 760 // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements 761 // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!) 762 // See 'Demo->Layout->Text Baseline Alignment' for details. 763 ImGui::AlignTextToFramePadding(); 764 ImGui::Text("Hold to repeat:"); 765 ImGui::SameLine(); 766 767 // Arrow buttons with Repeater 768 IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); 769 static int counter = 0; 770 float spacing = ImGui::GetStyle().ItemInnerSpacing.x; 771 ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); 772 if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } 773 ImGui::SameLine(0.0f, spacing); 774 if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } 775 ImGui::PopItemFlag(); 776 ImGui::SameLine(); 777 ImGui::Text("%d", counter); 778 779 ImGui::Button("Tooltip"); 780 ImGui::SetItemTooltip("I am a tooltip"); 781 782 ImGui::LabelText("label", "Value"); 783 784 ImGui::SeparatorText("Inputs"); 785 786 { 787 // To wire InputText() with std::string or any other custom string type, 788 // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. 789 IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); 790 static char str0[128] = "Hello, world!"; 791 ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); 792 ImGui::SameLine(); HelpMarker( 793 "USER:\n" 794 "Hold SHIFT or use mouse to select text.\n" 795 "CTRL+Left/Right to word jump.\n" 796 "CTRL+A or Double-Click to select all.\n" 797 "CTRL+X,CTRL+C,CTRL+V clipboard.\n" 798 "CTRL+Z,CTRL+Y undo/redo.\n" 799 "ESCAPE to revert.\n\n" 800 "PROGRAMMER:\n" 801 "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() " 802 "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated " 803 "in imgui_demo.cpp)."); 804 805 static char str1[128] = ""; 806 ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); 807 808 IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); 809 static int i0 = 123; 810 ImGui::InputInt("input int", &i0); 811 812 static float f0 = 0.001f; 813 ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f"); 814 815 static double d0 = 999999.00000001; 816 ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f"); 817 818 static float f1 = 1.e10f; 819 ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); 820 ImGui::SameLine(); HelpMarker( 821 "You can input value using the scientific notation,\n" 822 " e.g. \"1e+8\" becomes \"100000000\"."); 823 824 static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; 825 ImGui::InputFloat3("input float3", vec4a); 826 } 827 828 ImGui::SeparatorText("Drags"); 829 830 { 831 IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat"); 832 static int i1 = 50, i2 = 42, i3 = 128; 833 ImGui::DragInt("drag int", &i1, 1); 834 ImGui::SameLine(); HelpMarker( 835 "Click and drag to edit value.\n" 836 "Hold SHIFT/ALT for faster/slower edit.\n" 837 "Double-click or CTRL+click to input value."); 838 ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp); 839 ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround); 840 841 static float f1 = 1.00f, f2 = 0.0067f; 842 ImGui::DragFloat("drag float", &f1, 0.005f); 843 ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); 844 //ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround); 845 } 846 847 ImGui::SeparatorText("Sliders"); 848 849 { 850 IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat"); 851 static int i1 = 0; 852 ImGui::SliderInt("slider int", &i1, -1, 3); 853 ImGui::SameLine(); HelpMarker("CTRL+click to input value."); 854 855 static float f1 = 0.123f, f2 = 0.0f; 856 ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); 857 ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic); 858 859 IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle"); 860 static float angle = 0.0f; 861 ImGui::SliderAngle("slider angle", &angle); 862 863 // Using the format string to display a name instead of an integer. 864 // Here we completely omit '%d' from the format string, so it'll only display a name. 865 // This technique can also be used with DragInt(). 866 IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)"); 867 enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT }; 868 static int elem = Element_Fire; 869 const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; 870 const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown"; 871 ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here. 872 ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer."); 873 } 874 875 ImGui::SeparatorText("Selectors/Pickers"); 876 877 { 878 IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4"); 879 static float col1[3] = { 1.0f, 0.0f, 0.2f }; 880 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; 881 ImGui::ColorEdit3("color 1", col1); 882 ImGui::SameLine(); HelpMarker( 883 "Click on the color square to open a color picker.\n" 884 "Click and hold to use drag and drop.\n" 885 "Right-click on the color square to show options.\n" 886 "CTRL+click on individual component to input value.\n"); 887 888 ImGui::ColorEdit4("color 2", col2); 889 } 890 891 { 892 // Using the _simplified_ one-liner Combo() api here 893 // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api. 894 IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); 895 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; 896 static int item_current = 0; 897 ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); 898 ImGui::SameLine(); HelpMarker( 899 "Using the simplified one-liner Combo API here.\n" 900 "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API."); 901 } 902 903 { 904 // Using the _simplified_ one-liner ListBox() api here 905 // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api. 906 IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); 907 const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; 908 static int item_current = 1; 909 ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); 910 ImGui::SameLine(); HelpMarker( 911 "Using the simplified one-liner ListBox API here.\n" 912 "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API."); 913 } 914 915 ImGui::TreePop(); 916 } 917 918 IMGUI_DEMO_MARKER("Widgets/Tooltips"); 919 if (ImGui::TreeNode("Tooltips")) 920 { 921 // Tooltips are windows following the mouse. They do not take focus away. 922 ImGui::SeparatorText("General"); 923 924 // Typical use cases: 925 // - Short-form (text only): SetItemTooltip("Hello"); 926 // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); } 927 928 // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); } 929 // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); } 930 931 HelpMarker( 932 "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n" 933 "We provide a helper SetItemTooltip() function to perform the two with standards flags."); 934 935 ImVec2 sz = ImVec2(-FLT_MIN, 0.0f); 936 937 ImGui::Button("Basic", sz); 938 ImGui::SetItemTooltip("I am a tooltip"); 939 940 ImGui::Button("Fancy", sz); 941 if (ImGui::BeginItemTooltip()) 942 { 943 ImGui::Text("I am a fancy tooltip"); 944 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; 945 ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); 946 ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); 947 ImGui::EndTooltip(); 948 } 949 950 ImGui::SeparatorText("Always On"); 951 952 // Showcase NOT relying on a IsItemHovered() to emit a tooltip. 953 // Here the tooltip is always emitted when 'always_on == true'. 954 static int always_on = 0; 955 ImGui::RadioButton("Off", &always_on, 0); 956 ImGui::SameLine(); 957 ImGui::RadioButton("Always On (Simple)", &always_on, 1); 958 ImGui::SameLine(); 959 ImGui::RadioButton("Always On (Advanced)", &always_on, 2); 960 if (always_on == 1) 961 ImGui::SetTooltip("I am following you around."); 962 else if (always_on == 2 && ImGui::BeginTooltip()) 963 { 964 ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f)); 965 ImGui::EndTooltip(); 966 } 967 968 ImGui::SeparatorText("Custom"); 969 970 HelpMarker( 971 "Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize" 972 "tooltip activation details across your application. You may however decide to use custom" 973 "flags for a specific tooltip instance."); 974 975 // The following examples are passed for documentation purpose but may not be useful to most users. 976 // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from 977 // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or gamepad/keyboard is being used. 978 // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary. 979 ImGui::Button("Manual", sz); 980 if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) 981 ImGui::SetTooltip("I am a manually emitted tooltip."); 982 983 ImGui::Button("DelayNone", sz); 984 if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone)) 985 ImGui::SetTooltip("I am a tooltip with no delay."); 986 987 ImGui::Button("DelayShort", sz); 988 if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay)) 989 ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort); 990 991 ImGui::Button("DelayLong", sz); 992 if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) 993 ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal); 994 995 ImGui::Button("Stationary", sz); 996 if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary)) 997 ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating."); 998 999 // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav', 1000 // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag. 1001 ImGui::BeginDisabled(); 1002 ImGui::Button("Disabled item", sz); 1003 if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) 1004 ImGui::SetTooltip("I am a a tooltip for a disabled item."); 1005 ImGui::EndDisabled(); 1006 1007 ImGui::TreePop(); 1008 } 1009 1010 // Testing ImGuiOnceUponAFrame helper. 1011 //static ImGuiOnceUponAFrame once; 1012 //for (int i = 0; i < 5; i++) 1013 // if (once) 1014 // ImGui::Text("This will be displayed only once."); 1015 1016 IMGUI_DEMO_MARKER("Widgets/Tree Nodes"); 1017 if (ImGui::TreeNode("Tree Nodes")) 1018 { 1019 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees"); 1020 if (ImGui::TreeNode("Basic trees")) 1021 { 1022 for (int i = 0; i < 5; i++) 1023 { 1024 // Use SetNextItemOpen() so set the default state of a node to be open. We could 1025 // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing! 1026 if (i == 0) 1027 ImGui::SetNextItemOpen(true, ImGuiCond_Once); 1028 1029 // Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict. 1030 // An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)', 1031 // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine. 1032 ImGui::PushID(i); 1033 if (ImGui::TreeNode("", "Child %d", i)) 1034 { 1035 ImGui::Text("blah blah"); 1036 ImGui::SameLine(); 1037 if (ImGui::SmallButton("button")) {} 1038 ImGui::TreePop(); 1039 } 1040 ImGui::PopID(); 1041 } 1042 ImGui::TreePop(); 1043 } 1044 1045 IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes"); 1046 if (ImGui::TreeNode("Advanced, with Selectable nodes")) 1047 { 1048 HelpMarker( 1049 "This is a more typical looking tree with selectable nodes.\n" 1050 "Click to select, CTRL+Click to toggle, click on arrows or double-click to open."); 1051 static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; 1052 static bool align_label_with_current_x_position = false; 1053 static bool test_drag_and_drop = false; 1054 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow); 1055 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick); 1056 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node."); 1057 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth); 1058 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanTextWidth", &base_flags, ImGuiTreeNodeFlags_SpanTextWidth); ImGui::SameLine(); HelpMarker("Reduce hit area to the text label and a bit of margin."); 1059 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only."); 1060 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap); 1061 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)"); 1062 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsBackHere", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsBackHere); 1063 ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); 1064 ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); 1065 ImGui::Text("Hello!"); 1066 if (align_label_with_current_x_position) 1067 ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); 1068 1069 // 'selection_mask' is dumb representation of what may be user-side selection state. 1070 // You may retain selection state inside or outside your objects in whatever format you see fit. 1071 // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end 1072 /// of the loop. May be a pointer to your own node type, etc. 1073 static int selection_mask = (1 << 2); 1074 int node_clicked = -1; 1075 for (int i = 0; i < 6; i++) 1076 { 1077 // Disable the default "open on single-click behavior" + set Selected flag according to our selection. 1078 // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. 1079 ImGuiTreeNodeFlags node_flags = base_flags; 1080 const bool is_selected = (selection_mask & (1 << i)) != 0; 1081 if (is_selected) 1082 node_flags |= ImGuiTreeNodeFlags_Selected; 1083 if (i < 3) 1084 { 1085 // Items 0..2 are Tree Node 1086 bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); 1087 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) 1088 node_clicked = i; 1089 if (test_drag_and_drop && ImGui::BeginDragDropSource()) 1090 { 1091 ImGui::SetDragDropPayload("_TREENODE", NULL, 0); 1092 ImGui::Text("This is a drag and drop source"); 1093 ImGui::EndDragDropSource(); 1094 } 1095 if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanTextWidth)) 1096 { 1097 // Item 2 has an additional inline button to help demonstrate SpanTextWidth. 1098 ImGui::SameLine(); 1099 if (ImGui::SmallButton("button")) {} 1100 } 1101 if (node_open) 1102 { 1103 ImGui::BulletText("Blah blah\nBlah Blah"); 1104 ImGui::SameLine(); 1105 ImGui::SmallButton("Button"); 1106 ImGui::TreePop(); 1107 } 1108 } 1109 else 1110 { 1111 // Items 3..5 are Tree Leaves 1112 // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can 1113 // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). 1114 node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet 1115 ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); 1116 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) 1117 node_clicked = i; 1118 if (test_drag_and_drop && ImGui::BeginDragDropSource()) 1119 { 1120 ImGui::SetDragDropPayload("_TREENODE", NULL, 0); 1121 ImGui::Text("This is a drag and drop source"); 1122 ImGui::EndDragDropSource(); 1123 } 1124 } 1125 } 1126 if (node_clicked != -1) 1127 { 1128 // Update selection state 1129 // (process outside of tree loop to avoid visual inconsistencies during the clicking frame) 1130 if (ImGui::GetIO().KeyCtrl) 1131 selection_mask ^= (1 << node_clicked); // CTRL+click to toggle 1132 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 1133 selection_mask = (1 << node_clicked); // Click to single-select 1134 } 1135 if (align_label_with_current_x_position) 1136 ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); 1137 ImGui::TreePop(); 1138 } 1139 ImGui::TreePop(); 1140 } 1141 1142 IMGUI_DEMO_MARKER("Widgets/Collapsing Headers"); 1143 if (ImGui::TreeNode("Collapsing Headers")) 1144 { 1145 static bool closable_group = true; 1146 ImGui::Checkbox("Show 2nd header", &closable_group); 1147 if (ImGui::CollapsingHeader("Header", ImGuiTreeNodeFlags_None)) 1148 { 1149 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); 1150 for (int i = 0; i < 5; i++) 1151 ImGui::Text("Some content %d", i); 1152 } 1153 if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) 1154 { 1155 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); 1156 for (int i = 0; i < 5; i++) 1157 ImGui::Text("More content %d", i); 1158 } 1159 /* 1160 if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet)) 1161 ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); 1162 */ 1163 ImGui::TreePop(); 1164 } 1165 1166 IMGUI_DEMO_MARKER("Widgets/Bullets"); 1167 if (ImGui::TreeNode("Bullets")) 1168 { 1169 ImGui::BulletText("Bullet point 1"); 1170 ImGui::BulletText("Bullet point 2\nOn multiple lines"); 1171 if (ImGui::TreeNode("Tree node")) 1172 { 1173 ImGui::BulletText("Another bullet point"); 1174 ImGui::TreePop(); 1175 } 1176 ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); 1177 ImGui::Bullet(); ImGui::SmallButton("Button"); 1178 ImGui::TreePop(); 1179 } 1180 1181 IMGUI_DEMO_MARKER("Widgets/Text"); 1182 if (ImGui::TreeNode("Text")) 1183 { 1184 IMGUI_DEMO_MARKER("Widgets/Text/Colored Text"); 1185 if (ImGui::TreeNode("Colorful Text")) 1186 { 1187 // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. 1188 ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Pink"); 1189 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Yellow"); 1190 ImGui::TextDisabled("Disabled"); 1191 ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle."); 1192 ImGui::TreePop(); 1193 } 1194 1195 IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping"); 1196 if (ImGui::TreeNode("Word Wrapping")) 1197 { 1198 // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. 1199 ImGui::TextWrapped( 1200 "This text should automatically wrap on the edge of the window. The current implementation " 1201 "for text wrapping follows simple rules suitable for English and possibly other languages."); 1202 ImGui::Spacing(); 1203 1204 static float wrap_width = 200.0f; 1205 ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); 1206 1207 ImDrawList* draw_list = ImGui::GetWindowDrawList(); 1208 for (int n = 0; n < 2; n++) 1209 { 1210 ImGui::Text("Test paragraph %d:", n); 1211 ImVec2 pos = ImGui::GetCursorScreenPos(); 1212 ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y); 1213 ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()); 1214 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); 1215 if (n == 0) 1216 ImGui::Text("The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); 1217 else 1218 ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); 1219 1220 // Draw actual text bounding box, following by marker of our expected limit (should not overlap!) 1221 draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255)); 1222 draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255)); 1223 ImGui::PopTextWrapPos(); 1224 } 1225 1226 ImGui::TreePop(); 1227 } 1228 1229 IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text"); 1230 if (ImGui::TreeNode("UTF-8 Text")) 1231 { 1232 // UTF-8 test with Japanese characters 1233 // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.) 1234 // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8 1235 // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you 1236 // can save your source files as 'UTF-8 without signature'). 1237 // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 1238 // CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants. 1239 // Don't do this in your application! Please use u8"text in any language" in your application! 1240 // Note that characters values are preserved even by InputText() if the font cannot be displayed, 1241 // so you can safely copy & paste garbled characters into another application. 1242 ImGui::TextWrapped( 1243 "CJK text will only appear if the font was loaded with the appropriate CJK character ranges. " 1244 "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. " 1245 "Read docs/FONTS.md for details."); 1246 ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); 1247 ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); 1248 static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; 1249 //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis 1250 ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); 1251 ImGui::TreePop(); 1252 } 1253 ImGui::TreePop(); 1254 } 1255 1256 IMGUI_DEMO_MARKER("Widgets/Images"); 1257 if (ImGui::TreeNode("Images")) 1258 { 1259 ImGuiIO& io = ImGui::GetIO(); 1260 ImGui::TextWrapped( 1261 "Below we are displaying the font texture (which is the only texture we have access to in this demo). " 1262 "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. " 1263 "Hover the texture for a zoomed view!"); 1264 1265 // Below we are displaying the font texture because it is the only texture we have access to inside the demo! 1266 // Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that 1267 // will be passed to the rendering backend via the ImDrawCmd structure. 1268 // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top 1269 // of their respective source file to specify what they expect to be stored in ImTextureID, for example: 1270 // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer 1271 // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc. 1272 // More: 1273 // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers 1274 // to ImGui::Image(), and gather width/height through your own functions, etc. 1275 // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer, 1276 // it will help you debug issues if you are confused about it. 1277 // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). 1278 // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md 1279 // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples 1280 ImTextureID my_tex_id = io.Fonts->TexID; 1281 float my_tex_w = (float)io.Fonts->TexWidth; 1282 float my_tex_h = (float)io.Fonts->TexHeight; 1283 { 1284 static bool use_text_color_for_tint = false; 1285 ImGui::Checkbox("Use Text Color for Tint", &use_text_color_for_tint); 1286 ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); 1287 ImVec2 pos = ImGui::GetCursorScreenPos(); 1288 ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left 1289 ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right 1290 ImVec4 tint_col = use_text_color_for_tint ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint 1291 ImVec4 border_col = ImGui::GetStyleColorVec4(ImGuiCol_Border); 1292 ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col); 1293 if (ImGui::BeginItemTooltip()) 1294 { 1295 float region_sz = 32.0f; 1296 float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; 1297 float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; 1298 float zoom = 4.0f; 1299 if (region_x < 0.0f) { region_x = 0.0f; } 1300 else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; } 1301 if (region_y < 0.0f) { region_y = 0.0f; } 1302 else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; } 1303 ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); 1304 ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); 1305 ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); 1306 ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); 1307 ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, tint_col, border_col); 1308 ImGui::EndTooltip(); 1309 } 1310 } 1311 1312 IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); 1313 ImGui::TextWrapped("And now some textured buttons.."); 1314 static int pressed_count = 0; 1315 for (int i = 0; i < 8; i++) 1316 { 1317 // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures. 1318 // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation. 1319 // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples 1320 ImGui::PushID(i); 1321 if (i > 0) 1322 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(i - 1.0f, i - 1.0f)); 1323 ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible 1324 ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left 1325 ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h); // UV coordinates for (32,32) in our texture 1326 ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background 1327 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint 1328 if (ImGui::ImageButton("", my_tex_id, size, uv0, uv1, bg_col, tint_col)) 1329 pressed_count += 1; 1330 if (i > 0) 1331 ImGui::PopStyleVar(); 1332 ImGui::PopID(); 1333 ImGui::SameLine(); 1334 } 1335 ImGui::NewLine(); 1336 ImGui::Text("Pressed %d times.", pressed_count); 1337 ImGui::TreePop(); 1338 } 1339 1340 IMGUI_DEMO_MARKER("Widgets/Combo"); 1341 if (ImGui::TreeNode("Combo")) 1342 { 1343 // Combo Boxes are also called "Dropdown" in other systems 1344 // Expose flags as checkbox for the demo 1345 static ImGuiComboFlags flags = 0; 1346 ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft); 1347 ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); 1348 if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton)) 1349 flags &= ~ImGuiComboFlags_NoPreview; // Clear incompatible flags 1350 if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview)) 1351 flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags 1352 if (ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview)) 1353 flags &= ~ImGuiComboFlags_NoPreview; 1354 1355 // Override default popup height 1356 if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightSmall", &flags, ImGuiComboFlags_HeightSmall)) 1357 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall); 1358 if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightRegular", &flags, ImGuiComboFlags_HeightRegular)) 1359 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular); 1360 if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightLargest", &flags, ImGuiComboFlags_HeightLargest)) 1361 flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest); 1362 1363 // Using the generic BeginCombo() API, you have full control over how to display the combo contents. 1364 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively 1365 // stored in the object itself, etc.) 1366 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; 1367 static int item_selected_idx = 0; // Here we store our selection data as an index. 1368 1369 // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[]) 1370 const char* combo_preview_value = items[item_selected_idx]; 1371 1372 if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) 1373 { 1374 for (int n = 0; n < IM_ARRAYSIZE(items); n++) 1375 { 1376 const bool is_selected = (item_selected_idx == n); 1377 if (ImGui::Selectable(items[n], is_selected)) 1378 item_selected_idx = n; 1379 1380 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) 1381 if (is_selected) 1382 ImGui::SetItemDefaultFocus(); 1383 } 1384 ImGui::EndCombo(); 1385 } 1386 1387 ImGui::Spacing(); 1388 ImGui::SeparatorText("One-liner variants"); 1389 HelpMarker("Flags above don't apply to this section."); 1390 1391 // Simplified one-liner Combo() API, using values packed in a single constant string 1392 // This is a convenience for when the selection set is small and known at compile-time. 1393 static int item_current_2 = 0; 1394 ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); 1395 1396 // Simplified one-liner Combo() using an array of const char* 1397 // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. 1398 static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview 1399 ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); 1400 1401 // Simplified one-liner Combo() using an accessor function 1402 static int item_current_4 = 0; 1403 ImGui::Combo("combo 4 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items)); 1404 1405 ImGui::TreePop(); 1406 } 1407 1408 IMGUI_DEMO_MARKER("Widgets/List Boxes"); 1409 if (ImGui::TreeNode("List boxes")) 1410 { 1411 // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild() 1412 // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. 1413 // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild() 1414 // to always be called (inconsistent with BeginListBox()/EndListBox()). 1415 1416 // Using the generic BeginListBox() API, you have full control over how to display the combo contents. 1417 // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively 1418 // stored in the object itself, etc.) 1419 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; 1420 static int item_selected_idx = 0; // Here we store our selected data as an index. 1421 1422 static bool item_highlight = false; 1423 int item_highlighted_idx = -1; // Here we store our highlighted data as an index. 1424 ImGui::Checkbox("Highlight hovered item in second listbox", &item_highlight); 1425 1426 if (ImGui::BeginListBox("listbox 1")) 1427 { 1428 for (int n = 0; n < IM_ARRAYSIZE(items); n++) 1429 { 1430 const bool is_selected = (item_selected_idx == n); 1431 if (ImGui::Selectable(items[n], is_selected)) 1432 item_selected_idx = n; 1433 1434 if (item_highlight && ImGui::IsItemHovered()) 1435 item_highlighted_idx = n; 1436 1437 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) 1438 if (is_selected) 1439 ImGui::SetItemDefaultFocus(); 1440 } 1441 ImGui::EndListBox(); 1442 } 1443 ImGui::SameLine(); HelpMarker("Here we are sharing selection state between both boxes."); 1444 1445 // Custom size: use all width, 5 items tall 1446 ImGui::Text("Full-width:"); 1447 if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing()))) 1448 { 1449 for (int n = 0; n < IM_ARRAYSIZE(items); n++) 1450 { 1451 bool is_selected = (item_selected_idx == n); 1452 ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0; 1453 if (ImGui::Selectable(items[n], is_selected, flags)) 1454 item_selected_idx = n; 1455 1456 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) 1457 if (is_selected) 1458 ImGui::SetItemDefaultFocus(); 1459 } 1460 ImGui::EndListBox(); 1461 } 1462 1463 ImGui::TreePop(); 1464 } 1465 1466 IMGUI_DEMO_MARKER("Widgets/Selectables"); 1467 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); 1468 if (ImGui::TreeNode("Selectables")) 1469 { 1470 // Selectable() has 2 overloads: 1471 // - The one taking "bool selected" as a read-only selection information. 1472 // When Selectable() has been clicked it returns true and you can alter selection state accordingly. 1473 // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) 1474 // The earlier is more flexible, as in real application your selection may be stored in many different ways 1475 // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc). 1476 IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); 1477 if (ImGui::TreeNode("Basic")) 1478 { 1479 static bool selection[5] = { false, true, false, false }; 1480 ImGui::Selectable("1. I am selectable", &selection[0]); 1481 ImGui::Selectable("2. I am selectable", &selection[1]); 1482 ImGui::Selectable("3. I am selectable", &selection[2]); 1483 if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick)) 1484 if (ImGui::IsMouseDoubleClicked(0)) 1485 selection[3] = !selection[3]; 1486 ImGui::TreePop(); 1487 } 1488 1489 IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line"); 1490 if (ImGui::TreeNode("Rendering more items on the same line")) 1491 { 1492 // (1) Using SetNextItemAllowOverlap() 1493 // (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically. 1494 static bool selected[3] = { false, false, false }; 1495 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1"); 1496 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2"); 1497 ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3"); 1498 ImGui::TreePop(); 1499 } 1500 1501 IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables"); 1502 if (ImGui::TreeNode("In Tables")) 1503 { 1504 static bool selected[10] = {}; 1505 1506 if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) 1507 { 1508 for (int i = 0; i < 10; i++) 1509 { 1510 char label[32]; 1511 sprintf(label, "Item %d", i); 1512 ImGui::TableNextColumn(); 1513 ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap 1514 } 1515 ImGui::EndTable(); 1516 } 1517 ImGui::Spacing(); 1518 if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) 1519 { 1520 for (int i = 0; i < 10; i++) 1521 { 1522 char label[32]; 1523 sprintf(label, "Item %d", i); 1524 ImGui::TableNextRow(); 1525 ImGui::TableNextColumn(); 1526 ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns); 1527 ImGui::TableNextColumn(); 1528 ImGui::Text("Some other contents"); 1529 ImGui::TableNextColumn(); 1530 ImGui::Text("123456"); 1531 } 1532 ImGui::EndTable(); 1533 } 1534 ImGui::TreePop(); 1535 } 1536 1537 IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); 1538 if (ImGui::TreeNode("Grid")) 1539 { 1540 static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; 1541 1542 // Add in a bit of silly fun... 1543 const float time = (float)ImGui::GetTime(); 1544 const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected... 1545 if (winning_state) 1546 ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f))); 1547 1548 for (int y = 0; y < 4; y++) 1549 for (int x = 0; x < 4; x++) 1550 { 1551 if (x > 0) 1552 ImGui::SameLine(); 1553 ImGui::PushID(y * 4 + x); 1554 if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(50, 50))) 1555 { 1556 // Toggle clicked cell + toggle neighbors 1557 selected[y][x] ^= 1; 1558 if (x > 0) { selected[y][x - 1] ^= 1; } 1559 if (x < 3) { selected[y][x + 1] ^= 1; } 1560 if (y > 0) { selected[y - 1][x] ^= 1; } 1561 if (y < 3) { selected[y + 1][x] ^= 1; } 1562 } 1563 ImGui::PopID(); 1564 } 1565 1566 if (winning_state) 1567 ImGui::PopStyleVar(); 1568 ImGui::TreePop(); 1569 } 1570 IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); 1571 if (ImGui::TreeNode("Alignment")) 1572 { 1573 HelpMarker( 1574 "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item " 1575 "basis using PushStyleVar(). You'll probably want to always keep your default situation to " 1576 "left-align otherwise it becomes difficult to layout multiple items on a same line"); 1577 static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true }; 1578 for (int y = 0; y < 3; y++) 1579 { 1580 for (int x = 0; x < 3; x++) 1581 { 1582 ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f); 1583 char name[32]; 1584 sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y); 1585 if (x > 0) ImGui::SameLine(); 1586 ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment); 1587 ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80)); 1588 ImGui::PopStyleVar(); 1589 } 1590 } 1591 ImGui::TreePop(); 1592 } 1593 ImGui::TreePop(); 1594 } 1595 1596 ShowDemoWindowMultiSelect(demo_data); 1597 1598 // To wire InputText() with std::string or any other custom string type, 1599 // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. 1600 IMGUI_DEMO_MARKER("Widgets/Text Input"); 1601 if (ImGui::TreeNode("Text Input")) 1602 { 1603 IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input"); 1604 if (ImGui::TreeNode("Multi-line Text Input")) 1605 { 1606 // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize 1607 // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. 1608 static char text[1024 * 16] = 1609 "/*\n" 1610 " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" 1611 " the hexadecimal encoding of one offending instruction,\n" 1612 " more formally, the invalid operand with locked CMPXCHG8B\n" 1613 " instruction bug, is a design flaw in the majority of\n" 1614 " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" 1615 " processors (all in the P5 microarchitecture).\n" 1616 "*/\n\n" 1617 "label:\n" 1618 "\tlock cmpxchg8b eax\n"; 1619 1620 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; 1621 HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)"); 1622 ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); 1623 ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput); 1624 ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets."); 1625 ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine); 1626 ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); 1627 ImGui::TreePop(); 1628 } 1629 1630 IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input"); 1631 if (ImGui::TreeNode("Filtered Text Input")) 1632 { 1633 struct TextFilters 1634 { 1635 // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback) 1636 static int FilterCasingSwap(ImGuiInputTextCallbackData* data) 1637 { 1638 if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase 1639 else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase 1640 return 0; 1641 } 1642 1643 // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out) 1644 static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) 1645 { 1646 if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) 1647 return 0; 1648 return 1; 1649 } 1650 }; 1651 1652 static char buf1[32] = ""; ImGui::InputText("default", buf1, 32); 1653 static char buf2[32] = ""; ImGui::InputText("decimal", buf2, 32, ImGuiInputTextFlags_CharsDecimal); 1654 static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); 1655 static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, 32, ImGuiInputTextFlags_CharsUppercase); 1656 static char buf5[32] = ""; ImGui::InputText("no blank", buf5, 32, ImGuiInputTextFlags_CharsNoBlank); 1657 static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters. 1658 static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters. 1659 ImGui::TreePop(); 1660 } 1661 1662 IMGUI_DEMO_MARKER("Widgets/Text Input/Password input"); 1663 if (ImGui::TreeNode("Password Input")) 1664 { 1665 static char password[64] = "password123"; 1666 ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); 1667 ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); 1668 ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); 1669 ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password)); 1670 ImGui::TreePop(); 1671 } 1672 1673 IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks"); 1674 if (ImGui::TreeNode("Completion, History, Edit Callbacks")) 1675 { 1676 struct Funcs 1677 { 1678 static int MyCallback(ImGuiInputTextCallbackData* data) 1679 { 1680 if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion) 1681 { 1682 data->InsertChars(data->CursorPos, ".."); 1683 } 1684 else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) 1685 { 1686 if (data->EventKey == ImGuiKey_UpArrow) 1687 { 1688 data->DeleteChars(0, data->BufTextLen); 1689 data->InsertChars(0, "Pressed Up!"); 1690 data->SelectAll(); 1691 } 1692 else if (data->EventKey == ImGuiKey_DownArrow) 1693 { 1694 data->DeleteChars(0, data->BufTextLen); 1695 data->InsertChars(0, "Pressed Down!"); 1696 data->SelectAll(); 1697 } 1698 } 1699 else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit) 1700 { 1701 // Toggle casing of first character 1702 char c = data->Buf[0]; 1703 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32; 1704 data->BufDirty = true; 1705 1706 // Increment a counter 1707 int* p_int = (int*)data->UserData; 1708 *p_int = *p_int + 1; 1709 } 1710 return 0; 1711 } 1712 }; 1713 static char buf1[64]; 1714 ImGui::InputText("Completion", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); 1715 ImGui::SameLine(); HelpMarker( 1716 "Here we append \"..\" each time Tab is pressed. " 1717 "See 'Examples>Console' for a more meaningful demonstration of using this callback."); 1718 1719 static char buf2[64]; 1720 ImGui::InputText("History", buf2, 64, ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); 1721 ImGui::SameLine(); HelpMarker( 1722 "Here we replace and select text each time Up/Down are pressed. " 1723 "See 'Examples>Console' for a more meaningful demonstration of using this callback."); 1724 1725 static char buf3[64]; 1726 static int edit_count = 0; 1727 ImGui::InputText("Edit", buf3, 64, ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); 1728 ImGui::SameLine(); HelpMarker( 1729 "Here we toggle the casing of the first character on every edit + count edits."); 1730 ImGui::SameLine(); ImGui::Text("(%d)", edit_count); 1731 1732 ImGui::TreePop(); 1733 } 1734 1735 IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback"); 1736 if (ImGui::TreeNode("Resize Callback")) 1737 { 1738 // To wire InputText() with std::string or any other custom string type, 1739 // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper 1740 // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string. 1741 HelpMarker( 1742 "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n" 1743 "See misc/cpp/imgui_stdlib.h for an implementation of this for std::string."); 1744 struct Funcs 1745 { 1746 static int MyResizeCallback(ImGuiInputTextCallbackData* data) 1747 { 1748 if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) 1749 { 1750 ImVector<char>* my_str = (ImVector<char>*)data->UserData; 1751 IM_ASSERT(my_str->begin() == data->Buf); 1752 my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1 1753 data->Buf = my_str->begin(); 1754 } 1755 return 0; 1756 } 1757 1758 // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace. 1759 // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)' 1760 static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0) 1761 { 1762 IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); 1763 return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str); 1764 } 1765 }; 1766 1767 // For this demo we are using ImVector as a string container. 1768 // Note that because we need to store a terminating zero character, our size/capacity are 1 more 1769 // than usually reported by a typical string class. 1770 static ImVector<char> my_str; 1771 if (my_str.empty()) 1772 my_str.push_back(0); 1773 Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16)); 1774 ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); 1775 ImGui::TreePop(); 1776 } 1777 1778 IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous"); 1779 if (ImGui::TreeNode("Miscellaneous")) 1780 { 1781 static char buf1[16]; 1782 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll; 1783 ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll); 1784 ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); 1785 ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo); 1786 ImGui::InputText("Hello", buf1, IM_ARRAYSIZE(buf1), flags); 1787 ImGui::TreePop(); 1788 } 1789 1790 ImGui::TreePop(); 1791 } 1792 1793 // Tabs 1794 IMGUI_DEMO_MARKER("Widgets/Tabs"); 1795 if (ImGui::TreeNode("Tabs")) 1796 { 1797 IMGUI_DEMO_MARKER("Widgets/Tabs/Basic"); 1798 if (ImGui::TreeNode("Basic")) 1799 { 1800 ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; 1801 if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) 1802 { 1803 if (ImGui::BeginTabItem("Avocado")) 1804 { 1805 ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); 1806 ImGui::EndTabItem(); 1807 } 1808 if (ImGui::BeginTabItem("Broccoli")) 1809 { 1810 ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); 1811 ImGui::EndTabItem(); 1812 } 1813 if (ImGui::BeginTabItem("Cucumber")) 1814 { 1815 ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); 1816 ImGui::EndTabItem(); 1817 } 1818 ImGui::EndTabBar(); 1819 } 1820 ImGui::Separator(); 1821 ImGui::TreePop(); 1822 } 1823 1824 IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button"); 1825 if (ImGui::TreeNode("Advanced & Close Button")) 1826 { 1827 // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). 1828 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable; 1829 ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", &tab_bar_flags, ImGuiTabBarFlags_Reorderable); 1830 ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs); 1831 ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); 1832 ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); 1833 ImGui::CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline); 1834 if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) 1835 tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; 1836 if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) 1837 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); 1838 if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) 1839 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); 1840 1841 // Tab Bar 1842 ImGui::AlignTextToFramePadding(); 1843 ImGui::Text("Opened:"); 1844 const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; 1845 static bool opened[4] = { true, true, true, true }; // Persistent user state 1846 for (int n = 0; n < IM_ARRAYSIZE(opened); n++) 1847 { 1848 ImGui::SameLine(); 1849 ImGui::Checkbox(names[n], &opened[n]); 1850 } 1851 1852 // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): 1853 // the underlying bool will be set to false when the tab is closed. 1854 if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) 1855 { 1856 for (int n = 0; n < IM_ARRAYSIZE(opened); n++) 1857 if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None)) 1858 { 1859 ImGui::Text("This is the %s tab!", names[n]); 1860 if (n & 1) 1861 ImGui::Text("I am an odd tab."); 1862 ImGui::EndTabItem(); 1863 } 1864 ImGui::EndTabBar(); 1865 } 1866 ImGui::Separator(); 1867 ImGui::TreePop(); 1868 } 1869 1870 IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags"); 1871 if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags")) 1872 { 1873 static ImVector<int> active_tabs; 1874 static int next_tab_id = 0; 1875 if (next_tab_id == 0) // Initialize with some default tabs 1876 for (int i = 0; i < 3; i++) 1877 active_tabs.push_back(next_tab_id++); 1878 1879 // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together. 1880 // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags... 1881 // but they tend to make more sense together) 1882 static bool show_leading_button = true; 1883 static bool show_trailing_button = true; 1884 ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button); 1885 ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button); 1886 1887 // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs 1888 static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown; 1889 ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); 1890 if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) 1891 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); 1892 if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) 1893 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); 1894 1895 if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) 1896 { 1897 // Demo a Leading TabItemButton(): click the "?" button to open a menu 1898 if (show_leading_button) 1899 if (ImGui::TabItemButton("?", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip)) 1900 ImGui::OpenPopup("MyHelpMenu"); 1901 if (ImGui::BeginPopup("MyHelpMenu")) 1902 { 1903 ImGui::Selectable("Hello!"); 1904 ImGui::EndPopup(); 1905 } 1906 1907 // Demo Trailing Tabs: click the "+" button to add a new tab. 1908 // (In your app you may want to use a font icon instead of the "+") 1909 // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end. 1910 if (show_trailing_button) 1911 if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) 1912 active_tabs.push_back(next_tab_id++); // Add new tab 1913 1914 // Submit our regular tabs 1915 for (int n = 0; n < active_tabs.Size; ) 1916 { 1917 bool open = true; 1918 char name[16]; 1919 snprintf(name, IM_ARRAYSIZE(name), "%04d", active_tabs[n]); 1920 if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None)) 1921 { 1922 ImGui::Text("This is the %s tab!", name); 1923 ImGui::EndTabItem(); 1924 } 1925 1926 if (!open) 1927 active_tabs.erase(active_tabs.Data + n); 1928 else 1929 n++; 1930 } 1931 1932 ImGui::EndTabBar(); 1933 } 1934 ImGui::Separator(); 1935 ImGui::TreePop(); 1936 } 1937 ImGui::TreePop(); 1938 } 1939 1940 // Plot/Graph widgets are not very good. 1941 // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot 1942 // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions) 1943 IMGUI_DEMO_MARKER("Widgets/Plotting"); 1944 if (ImGui::TreeNode("Plotting")) 1945 { 1946 static bool animate = true; 1947 ImGui::Checkbox("Animate", &animate); 1948 1949 // Plot as lines and plot as histogram 1950 static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; 1951 ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); 1952 ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); 1953 //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!"); 1954 1955 // Fill an array of contiguous float values to plot 1956 // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float 1957 // and the sizeof() of your structure in the "stride" parameter. 1958 static float values[90] = {}; 1959 static int values_offset = 0; 1960 static double refresh_time = 0.0; 1961 if (!animate || refresh_time == 0.0) 1962 refresh_time = ImGui::GetTime(); 1963 while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo 1964 { 1965 static float phase = 0.0f; 1966 values[values_offset] = cosf(phase); 1967 values_offset = (values_offset + 1) % IM_ARRAYSIZE(values); 1968 phase += 0.10f * values_offset; 1969 refresh_time += 1.0f / 60.0f; 1970 } 1971 1972 // Plots can display overlay texts 1973 // (in this example, we will display an average value) 1974 { 1975 float average = 0.0f; 1976 for (int n = 0; n < IM_ARRAYSIZE(values); n++) 1977 average += values[n]; 1978 average /= (float)IM_ARRAYSIZE(values); 1979 char overlay[32]; 1980 sprintf(overlay, "avg %f", average); 1981 ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); 1982 } 1983 1984 // Use functions to generate output 1985 // FIXME: This is actually VERY awkward because current plot API only pass in indices. 1986 // We probably want an API passing floats and user provide sample rate/count. 1987 struct Funcs 1988 { 1989 static float Sin(void*, int i) { return sinf(i * 0.1f); } 1990 static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } 1991 }; 1992 static int func_type = 0, display_count = 70; 1993 ImGui::SeparatorText("Functions"); 1994 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 1995 ImGui::Combo("func", &func_type, "Sin\0Saw\0"); 1996 ImGui::SameLine(); 1997 ImGui::SliderInt("Sample count", &display_count, 1, 400); 1998 float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; 1999 ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); 2000 ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); 2001 ImGui::Separator(); 2002 2003 ImGui::TreePop(); 2004 } 2005 2006 IMGUI_DEMO_MARKER("Widgets/Progress Bars"); 2007 if (ImGui::TreeNode("Progress Bars")) 2008 { 2009 // Animate a simple progress bar 2010 static float progress = 0.0f, progress_dir = 1.0f; 2011 progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; 2012 if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } 2013 if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } 2014 2015 // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width, 2016 // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. 2017 ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f)); 2018 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); 2019 ImGui::Text("Progress Bar"); 2020 2021 float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f); 2022 char buf[32]; 2023 sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753); 2024 ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); 2025 2026 // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value. 2027 // Adjust the factor if you want to adjust the animation speed. 2028 ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), "Searching.."); 2029 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); 2030 ImGui::Text("Indeterminate"); 2031 2032 ImGui::TreePop(); 2033 } 2034 2035 IMGUI_DEMO_MARKER("Widgets/Color"); 2036 if (ImGui::TreeNode("Color/Picker Widgets")) 2037 { 2038 static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); 2039 2040 static bool alpha_preview = true; 2041 static bool alpha_half_preview = false; 2042 static bool drag_and_drop = true; 2043 static bool options_menu = true; 2044 static bool hdr = false; 2045 ImGui::SeparatorText("Options"); 2046 ImGui::Checkbox("With Alpha Preview", &alpha_preview); 2047 ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); 2048 ImGui::Checkbox("With Drag and Drop", &drag_and_drop); 2049 ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); 2050 ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); 2051 ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); 2052 2053 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); 2054 ImGui::SeparatorText("Inline color editor"); 2055 ImGui::Text("Color widget:"); 2056 ImGui::SameLine(); HelpMarker( 2057 "Click on the color square to open a color picker.\n" 2058 "CTRL+click on individual component to input value.\n"); 2059 ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); 2060 2061 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); 2062 ImGui::Text("Color widget HSV with Alpha:"); 2063 ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); 2064 2065 IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); 2066 ImGui::Text("Color widget with Float Display:"); 2067 ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); 2068 2069 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); 2070 ImGui::Text("Color button with Picker:"); 2071 ImGui::SameLine(); HelpMarker( 2072 "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" 2073 "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only " 2074 "be used for the tooltip and picker popup."); 2075 ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); 2076 2077 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); 2078 ImGui::Text("Color button with Custom Picker Popup:"); 2079 2080 // Generate a default palette. The palette will persist and can be edited. 2081 static bool saved_palette_init = true; 2082 static ImVec4 saved_palette[32] = {}; 2083 if (saved_palette_init) 2084 { 2085 for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) 2086 { 2087 ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, 2088 saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); 2089 saved_palette[n].w = 1.0f; // Alpha 2090 } 2091 saved_palette_init = false; 2092 } 2093 2094 static ImVec4 backup_color; 2095 bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); 2096 ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); 2097 open_popup |= ImGui::Button("Palette"); 2098 if (open_popup) 2099 { 2100 ImGui::OpenPopup("mypicker"); 2101 backup_color = color; 2102 } 2103 if (ImGui::BeginPopup("mypicker")) 2104 { 2105 ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); 2106 ImGui::Separator(); 2107 ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); 2108 ImGui::SameLine(); 2109 2110 ImGui::BeginGroup(); // Lock X position 2111 ImGui::Text("Current"); 2112 ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)); 2113 ImGui::Text("Previous"); 2114 if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40))) 2115 color = backup_color; 2116 ImGui::Separator(); 2117 ImGui::Text("Palette"); 2118 for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) 2119 { 2120 ImGui::PushID(n); 2121 if ((n % 8) != 0) 2122 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); 2123 2124 ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip; 2125 if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, ImVec2(20, 20))) 2126 color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! 2127 2128 // Allow user to drop colors into each palette entry. Note that ColorButton() is already a 2129 // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag. 2130 if (ImGui::BeginDragDropTarget()) 2131 { 2132 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) 2133 memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3); 2134 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) 2135 memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4); 2136 ImGui::EndDragDropTarget(); 2137 } 2138 2139 ImGui::PopID(); 2140 } 2141 ImGui::EndGroup(); 2142 ImGui::EndPopup(); 2143 } 2144 2145 IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)"); 2146 ImGui::Text("Color button only:"); 2147 static bool no_border = false; 2148 ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); 2149 ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); 2150 2151 IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); 2152 ImGui::SeparatorText("Color picker"); 2153 static bool alpha = true; 2154 static bool alpha_bar = true; 2155 static bool side_preview = true; 2156 static bool ref_color = false; 2157 static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f); 2158 static int display_mode = 0; 2159 static int picker_mode = 0; 2160 ImGui::Checkbox("With Alpha", &alpha); 2161 ImGui::Checkbox("With Alpha Bar", &alpha_bar); 2162 ImGui::Checkbox("With Side Preview", &side_preview); 2163 if (side_preview) 2164 { 2165 ImGui::SameLine(); 2166 ImGui::Checkbox("With Ref Color", &ref_color); 2167 if (ref_color) 2168 { 2169 ImGui::SameLine(); 2170 ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); 2171 } 2172 } 2173 ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0"); 2174 ImGui::SameLine(); HelpMarker( 2175 "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, " 2176 "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex " 2177 "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); 2178 ImGui::SameLine(); HelpMarker("When not specified explicitly (Auto/Current mode), user can right-click the picker to change mode."); 2179 ImGuiColorEditFlags flags = misc_flags; 2180 if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() 2181 if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; 2182 if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; 2183 if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; 2184 if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; 2185 if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays 2186 if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode 2187 if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV; 2188 if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex; 2189 ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); 2190 2191 ImGui::Text("Set defaults in code:"); 2192 ImGui::SameLine(); HelpMarker( 2193 "SetColorEditOptions() is designed to allow you to set boot-time default.\n" 2194 "We don't have Push/Pop functions because you can force options on a per-widget basis if needed," 2195 "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid" 2196 "encouraging you to persistently save values that aren't forward-compatible."); 2197 if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) 2198 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar); 2199 if (ImGui::Button("Default: Float + HDR + Hue Wheel")) 2200 ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); 2201 2202 // Always display a small version of both types of pickers 2203 // (that's in order to make it more visible in the demo to people who are skimming quickly through it) 2204 ImGui::Text("Both types:"); 2205 float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f; 2206 ImGui::SetNextItemWidth(w); 2207 ImGui::ColorPicker3("##MyColor##5", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); 2208 ImGui::SameLine(); 2209 ImGui::SetNextItemWidth(w); 2210 ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); 2211 2212 // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) 2213 static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV! 2214 ImGui::Spacing(); 2215 ImGui::Text("HSV encoded colors"); 2216 ImGui::SameLine(); HelpMarker( 2217 "By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV" 2218 "allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the" 2219 "added benefit that you can manipulate hue values with the picker even when saturation or value are zero."); 2220 ImGui::Text("Color widget with InputHSV:"); 2221 ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); 2222 ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); 2223 ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f); 2224 2225 ImGui::TreePop(); 2226 } 2227 2228 IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags"); 2229 if (ImGui::TreeNode("Drag/Slider Flags")) 2230 { 2231 // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same! 2232 static ImGuiSliderFlags flags = ImGuiSliderFlags_None; 2233 ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp); 2234 ImGui::SameLine(); HelpMarker("Always clamp value to min/max bounds (if any) when input manually with CTRL+Click."); 2235 ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic); 2236 ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values)."); 2237 ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat); 2238 ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits)."); 2239 ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput); 2240 ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget."); 2241 ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround); 2242 ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)"); 2243 2244 // Drags 2245 static float drag_f = 0.5f; 2246 static int drag_i = 50; 2247 ImGui::Text("Underlying float value: %f", drag_f); 2248 ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags); 2249 ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags); 2250 ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags); 2251 ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags); 2252 ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); 2253 2254 // Sliders 2255 static float slider_f = 0.5f; 2256 static int slider_i = 50; 2257 const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround; 2258 ImGui::Text("Underlying float value: %f", slider_f); 2259 ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders); 2260 ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders); 2261 2262 ImGui::TreePop(); 2263 } 2264 2265 IMGUI_DEMO_MARKER("Widgets/Range Widgets"); 2266 if (ImGui::TreeNode("Range Widgets")) 2267 { 2268 static float begin = 10, end = 90; 2269 static int begin_i = 100, end_i = 1000; 2270 ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_AlwaysClamp); 2271 ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units"); 2272 ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units"); 2273 ImGui::TreePop(); 2274 } 2275 2276 IMGUI_DEMO_MARKER("Widgets/Data Types"); 2277 if (ImGui::TreeNode("Data Types")) 2278 { 2279 // DragScalar/InputScalar/SliderScalar functions allow various data types 2280 // - signed/unsigned 2281 // - 8/16/32/64-bits 2282 // - integer/float/double 2283 // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum 2284 // to pass the type, and passing all arguments by pointer. 2285 // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type. 2286 // In practice, if you frequently use a given type that is not covered by the normal API entry points, 2287 // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*, 2288 // and then pass their address to the generic function. For example: 2289 // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") 2290 // { 2291 // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); 2292 // } 2293 2294 // Setup limits (as helper variables so we can take their address, as explained above) 2295 // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2. 2296 #ifndef LLONG_MIN 2297 ImS64 LLONG_MIN = -9223372036854775807LL - 1; 2298 ImS64 LLONG_MAX = 9223372036854775807LL; 2299 ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1); 2300 #endif 2301 const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127; 2302 const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255; 2303 const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767; 2304 const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535; 2305 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; 2306 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; 2307 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; 2308 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; 2309 const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f; 2310 const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0; 2311 2312 // State 2313 static char s8_v = 127; 2314 static ImU8 u8_v = 255; 2315 static short s16_v = 32767; 2316 static ImU16 u16_v = 65535; 2317 static ImS32 s32_v = -1; 2318 static ImU32 u32_v = (ImU32)-1; 2319 static ImS64 s64_v = -1; 2320 static ImU64 u64_v = (ImU64)-1; 2321 static float f32_v = 0.123f; 2322 static double f64_v = 90000.01234567890123456789; 2323 2324 const float drag_speed = 0.2f; 2325 static bool drag_clamp = false; 2326 IMGUI_DEMO_MARKER("Widgets/Data Types/Drags"); 2327 ImGui::SeparatorText("Drags"); 2328 ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); 2329 ImGui::SameLine(); HelpMarker( 2330 "As with every widget in dear imgui, we never modify values unless there is a user interaction.\n" 2331 "You can override the clamping limits by using CTRL+Click to input a value."); 2332 ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL); 2333 ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms"); 2334 ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); 2335 ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms"); 2336 ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); 2337 ImGui::DragScalar("drag s32 hex", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X"); 2338 ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); 2339 ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); 2340 ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); 2341 ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f"); 2342 ImGui::DragScalar("drag float log", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", ImGuiSliderFlags_Logarithmic); 2343 ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams"); 2344 ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic); 2345 2346 IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders"); 2347 ImGui::SeparatorText("Sliders"); 2348 ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); 2349 ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); 2350 ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d"); 2351 ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u"); 2352 ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); 2353 ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); 2354 ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); 2355 ImGui::SliderScalar("slider s32 hex", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "0x%04X"); 2356 ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); 2357 ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); 2358 ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); 2359 ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%" PRId64); 2360 ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" PRId64); 2361 ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" PRId64); 2362 ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" PRIu64 " ms"); 2363 ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" PRIu64 " ms"); 2364 ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" PRIu64 " ms"); 2365 ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); 2366 ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic); 2367 ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); 2368 ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams"); 2369 ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", ImGuiSliderFlags_Logarithmic); 2370 ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams"); 2371 2372 ImGui::SeparatorText("Sliders (reverse)"); 2373 ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d"); 2374 ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u"); 2375 ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d"); 2376 ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u"); 2377 ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" PRId64); 2378 ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" PRIu64 " ms"); 2379 2380 IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); 2381 static bool inputs_step = true; 2382 static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None; 2383 ImGui::SeparatorText("Inputs"); 2384 ImGui::Checkbox("Show step buttons", &inputs_step); 2385 ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); 2386 ImGui::CheckboxFlags("ImGuiInputTextFlags_ParseEmptyRefVal", &flags, ImGuiInputTextFlags_ParseEmptyRefVal); 2387 ImGui::CheckboxFlags("ImGuiInputTextFlags_DisplayEmptyRefVal", &flags, ImGuiInputTextFlags_DisplayEmptyRefVal); 2388 ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d", flags); 2389 ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u", flags); 2390 ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d", flags); 2391 ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u", flags); 2392 ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d", flags); 2393 ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X", flags); 2394 ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u", flags); 2395 ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", flags); 2396 ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL, NULL, NULL, flags); 2397 ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL, NULL, NULL, flags); 2398 ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL, NULL, NULL, flags); 2399 ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL, NULL, NULL, flags); 2400 2401 ImGui::TreePop(); 2402 } 2403 2404 IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets"); 2405 if (ImGui::TreeNode("Multi-component Widgets")) 2406 { 2407 static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; 2408 static int vec4i[4] = { 1, 5, 100, 255 }; 2409 2410 ImGui::SeparatorText("2-wide"); 2411 ImGui::InputFloat2("input float2", vec4f); 2412 ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); 2413 ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); 2414 ImGui::InputInt2("input int2", vec4i); 2415 ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); 2416 ImGui::SliderInt2("slider int2", vec4i, 0, 255); 2417 2418 ImGui::SeparatorText("3-wide"); 2419 ImGui::InputFloat3("input float3", vec4f); 2420 ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); 2421 ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); 2422 ImGui::InputInt3("input int3", vec4i); 2423 ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); 2424 ImGui::SliderInt3("slider int3", vec4i, 0, 255); 2425 2426 ImGui::SeparatorText("4-wide"); 2427 ImGui::InputFloat4("input float4", vec4f); 2428 ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); 2429 ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); 2430 ImGui::InputInt4("input int4", vec4i); 2431 ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); 2432 ImGui::SliderInt4("slider int4", vec4i, 0, 255); 2433 2434 ImGui::TreePop(); 2435 } 2436 2437 IMGUI_DEMO_MARKER("Widgets/Vertical Sliders"); 2438 if (ImGui::TreeNode("Vertical Sliders")) 2439 { 2440 const float spacing = 4; 2441 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); 2442 2443 static int int_value = 0; 2444 ImGui::VSliderInt("##int", ImVec2(18, 160), &int_value, 0, 5); 2445 ImGui::SameLine(); 2446 2447 static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; 2448 ImGui::PushID("set1"); 2449 for (int i = 0; i < 7; i++) 2450 { 2451 if (i > 0) ImGui::SameLine(); 2452 ImGui::PushID(i); 2453 ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f)); 2454 ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f)); 2455 ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f)); 2456 ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f)); 2457 ImGui::VSliderFloat("##v", ImVec2(18, 160), &values[i], 0.0f, 1.0f, ""); 2458 if (ImGui::IsItemActive() || ImGui::IsItemHovered()) 2459 ImGui::SetTooltip("%.3f", values[i]); 2460 ImGui::PopStyleColor(4); 2461 ImGui::PopID(); 2462 } 2463 ImGui::PopID(); 2464 2465 ImGui::SameLine(); 2466 ImGui::PushID("set2"); 2467 static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; 2468 const int rows = 3; 2469 const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows)); 2470 for (int nx = 0; nx < 4; nx++) 2471 { 2472 if (nx > 0) ImGui::SameLine(); 2473 ImGui::BeginGroup(); 2474 for (int ny = 0; ny < rows; ny++) 2475 { 2476 ImGui::PushID(nx * rows + ny); 2477 ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); 2478 if (ImGui::IsItemActive() || ImGui::IsItemHovered()) 2479 ImGui::SetTooltip("%.3f", values2[nx]); 2480 ImGui::PopID(); 2481 } 2482 ImGui::EndGroup(); 2483 } 2484 ImGui::PopID(); 2485 2486 ImGui::SameLine(); 2487 ImGui::PushID("set3"); 2488 for (int i = 0; i < 4; i++) 2489 { 2490 if (i > 0) ImGui::SameLine(); 2491 ImGui::PushID(i); 2492 ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); 2493 ImGui::VSliderFloat("##v", ImVec2(40, 160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); 2494 ImGui::PopStyleVar(); 2495 ImGui::PopID(); 2496 } 2497 ImGui::PopID(); 2498 ImGui::PopStyleVar(); 2499 ImGui::TreePop(); 2500 } 2501 2502 IMGUI_DEMO_MARKER("Widgets/Drag and drop"); 2503 if (ImGui::TreeNode("Drag and Drop")) 2504 { 2505 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets"); 2506 if (ImGui::TreeNode("Drag and drop in standard widgets")) 2507 { 2508 // ColorEdit widgets automatically act as drag source and drag target. 2509 // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F 2510 // to allow your own widgets to use colors in their drag and drop interaction. 2511 // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo. 2512 HelpMarker("You can drag from the color squares."); 2513 static float col1[3] = { 1.0f, 0.0f, 0.2f }; 2514 static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; 2515 ImGui::ColorEdit3("color 1", col1); 2516 ImGui::ColorEdit4("color 2", col2); 2517 ImGui::TreePop(); 2518 } 2519 2520 IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items"); 2521 if (ImGui::TreeNode("Drag and drop to copy/swap items")) 2522 { 2523 enum Mode 2524 { 2525 Mode_Copy, 2526 Mode_Move, 2527 Mode_Swap 2528 }; 2529 static int mode = 0; 2530 if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine(); 2531 if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine(); 2532 if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } 2533 static const char* names[9] = 2534 { 2535 "Bobby", "Beatrice", "Betty", 2536 "Brianna", "Barry", "Bernard", 2537 "Bibi", "Blaine", "Bryn" 2538 }; 2539 for (int n = 0; n < IM_ARRAYSIZE(names); n++) 2540 { 2541 ImGui::PushID(n); 2542 if ((n % 3) != 0) 2543 ImGui::SameLine(); 2544 ImGui::Button(names[n], ImVec2(60, 60)); 2545 2546 // Our buttons are both drag sources and drag targets here! 2547 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) 2548 { 2549 // Set payload to carry the index of our item (could be anything) 2550 ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int)); 2551 2552 // Display preview (could be anything, e.g. when dragging an image we could decide to display 2553 // the filename and a small preview of the image, etc.) 2554 if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); } 2555 if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); } 2556 if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); } 2557 ImGui::EndDragDropSource(); 2558 } 2559 if (ImGui::BeginDragDropTarget()) 2560 { 2561 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) 2562 { 2563 IM_ASSERT(payload->DataSize == sizeof(int)); 2564 int payload_n = *(const int*)payload->Data; 2565 if (mode == Mode_Copy) 2566 { 2567 names[n] = names[payload_n]; 2568 } 2569 if (mode == Mode_Move) 2570 { 2571 names[n] = names[payload_n]; 2572 names[payload_n] = ""; 2573 } 2574 if (mode == Mode_Swap) 2575 { 2576 const char* tmp = names[n]; 2577 names[n] = names[payload_n]; 2578 names[payload_n] = tmp; 2579 } 2580 } 2581 ImGui::EndDragDropTarget(); 2582 } 2583 ImGui::PopID(); 2584 } 2585 ImGui::TreePop(); 2586 } 2587 2588 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); 2589 if (ImGui::TreeNode("Drag to reorder items (simple)")) 2590 { 2591 // Simple reordering 2592 HelpMarker( 2593 "We don't use the drag and drop api at all here! " 2594 "Instead we query when the item is held but not hovered, and order items accordingly."); 2595 static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" }; 2596 for (int n = 0; n < IM_ARRAYSIZE(item_names); n++) 2597 { 2598 const char* item = item_names[n]; 2599 ImGui::Selectable(item); 2600 2601 if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) 2602 { 2603 int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1); 2604 if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names)) 2605 { 2606 item_names[n] = item_names[n_next]; 2607 item_names[n_next] = item; 2608 ImGui::ResetMouseDragDelta(); 2609 } 2610 } 2611 } 2612 ImGui::TreePop(); 2613 } 2614 2615 IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location"); 2616 if (ImGui::TreeNode("Tooltip at target location")) 2617 { 2618 for (int n = 0; n < 2; n++) 2619 { 2620 // Drop targets 2621 ImGui::Button(n ? "drop here##1" : "drop here##0"); 2622 if (ImGui::BeginDragDropTarget()) 2623 { 2624 ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip; 2625 if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags)) 2626 { 2627 IM_UNUSED(payload); 2628 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); 2629 ImGui::SetTooltip("Cannot drop here!"); 2630 } 2631 ImGui::EndDragDropTarget(); 2632 } 2633 2634 // Drop source 2635 static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f }; 2636 if (n == 0) 2637 ImGui::ColorButton("drag me", col4); 2638 2639 } 2640 ImGui::TreePop(); 2641 } 2642 2643 ImGui::TreePop(); 2644 } 2645 2646 IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)"); 2647 if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) 2648 { 2649 // Select an item type 2650 const char* item_names[] = 2651 { 2652 "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat", 2653 "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" 2654 }; 2655 static int item_type = 4; 2656 static bool item_disabled = false; 2657 ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names)); 2658 ImGui::SameLine(); 2659 HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); 2660 ImGui::Checkbox("Item Disabled", &item_disabled); 2661 2662 // Submit selected items so we can query their status in the code following it. 2663 bool ret = false; 2664 static bool b = false; 2665 static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; 2666 static char str[16] = {}; 2667 if (item_disabled) 2668 ImGui::BeginDisabled(true); 2669 if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction 2670 if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button 2671 if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater) 2672 if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox 2673 if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item 2674 if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) 2675 if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window) 2676 if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input 2677 if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) 2678 if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) 2679 if (item_type == 10){ ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item 2680 if (item_type == 11){ ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) 2681 if (item_type == 12){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node 2682 if (item_type == 13){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. 2683 if (item_type == 14){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } 2684 if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } 2685 2686 bool hovered_delay_none = ImGui::IsItemHovered(); 2687 bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary); 2688 bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort); 2689 bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal); 2690 bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary 2691 2692 // Display the values of IsItemHovered() and other common item state functions. 2693 // Note that the ImGuiHoveredFlags_XXX flags can be combined. 2694 // Because BulletText is an item itself and that would affect the output of IsItemXXX functions, 2695 // we query every state in a single call to avoid storing them and to simplify the code. 2696 ImGui::BulletText( 2697 "Return value = %d\n" 2698 "IsItemFocused() = %d\n" 2699 "IsItemHovered() = %d\n" 2700 "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" 2701 "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" 2702 "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n" 2703 "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n" 2704 "IsItemHovered(_AllowWhenDisabled) = %d\n" 2705 "IsItemHovered(_RectOnly) = %d\n" 2706 "IsItemActive() = %d\n" 2707 "IsItemEdited() = %d\n" 2708 "IsItemActivated() = %d\n" 2709 "IsItemDeactivated() = %d\n" 2710 "IsItemDeactivatedAfterEdit() = %d\n" 2711 "IsItemVisible() = %d\n" 2712 "IsItemClicked() = %d\n" 2713 "IsItemToggledOpen() = %d\n" 2714 "GetItemRectMin() = (%.1f, %.1f)\n" 2715 "GetItemRectMax() = (%.1f, %.1f)\n" 2716 "GetItemRectSize() = (%.1f, %.1f)", 2717 ret, 2718 ImGui::IsItemFocused(), 2719 ImGui::IsItemHovered(), 2720 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), 2721 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), 2722 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem), 2723 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow), 2724 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), 2725 ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), 2726 ImGui::IsItemActive(), 2727 ImGui::IsItemEdited(), 2728 ImGui::IsItemActivated(), 2729 ImGui::IsItemDeactivated(), 2730 ImGui::IsItemDeactivatedAfterEdit(), 2731 ImGui::IsItemVisible(), 2732 ImGui::IsItemClicked(), 2733 ImGui::IsItemToggledOpen(), 2734 ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y, 2735 ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y, 2736 ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y 2737 ); 2738 ImGui::BulletText( 2739 "with Hovering Delay or Stationary test:\n" 2740 "IsItemHovered() = = %d\n" 2741 "IsItemHovered(_Stationary) = %d\n" 2742 "IsItemHovered(_DelayShort) = %d\n" 2743 "IsItemHovered(_DelayNormal) = %d\n" 2744 "IsItemHovered(_Tooltip) = %d", 2745 hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip); 2746 2747 if (item_disabled) 2748 ImGui::EndDisabled(); 2749 2750 char buf[1] = ""; 2751 ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); 2752 ImGui::SameLine(); 2753 HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); 2754 2755 ImGui::TreePop(); 2756 } 2757 2758 IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)"); 2759 if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) 2760 { 2761 static bool embed_all_inside_a_child_window = false; 2762 ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); 2763 if (embed_all_inside_a_child_window) 2764 ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Border); 2765 2766 // Testing IsWindowFocused() function with its various flags. 2767 ImGui::BulletText( 2768 "IsWindowFocused() = %d\n" 2769 "IsWindowFocused(_ChildWindows) = %d\n" 2770 "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" 2771 "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" 2772 "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" 2773 "IsWindowFocused(_RootWindow) = %d\n" 2774 "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" 2775 "IsWindowFocused(_AnyWindow) = %d\n", 2776 ImGui::IsWindowFocused(), 2777 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), 2778 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), 2779 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), 2780 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), 2781 ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), 2782 ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), 2783 ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); 2784 2785 // Testing IsWindowHovered() function with its various flags. 2786 ImGui::BulletText( 2787 "IsWindowHovered() = %d\n" 2788 "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" 2789 "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" 2790 "IsWindowHovered(_ChildWindows) = %d\n" 2791 "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" 2792 "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" 2793 "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" 2794 "IsWindowHovered(_RootWindow) = %d\n" 2795 "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" 2796 "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" 2797 "IsWindowHovered(_AnyWindow) = %d\n" 2798 "IsWindowHovered(_Stationary) = %d\n", 2799 ImGui::IsWindowHovered(), 2800 ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), 2801 ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), 2802 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), 2803 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), 2804 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), 2805 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), 2806 ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), 2807 ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), 2808 ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), 2809 ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), 2810 ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); 2811 2812 ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Border); 2813 ImGui::Text("This is another child window for testing the _ChildWindows flag."); 2814 ImGui::EndChild(); 2815 if (embed_all_inside_a_child_window) 2816 ImGui::EndChild(); 2817 2818 // Calling IsItemHovered() after begin returns the hovered status of the title bar. 2819 // This is useful in particular if you want to create a context menu associated to the title bar of a window. 2820 static bool test_window = false; 2821 ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); 2822 if (test_window) 2823 { 2824 ImGui::Begin("Title bar Hovered/Active tests", &test_window); 2825 if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() 2826 { 2827 if (ImGui::MenuItem("Close")) { test_window = false; } 2828 ImGui::EndPopup(); 2829 } 2830 ImGui::Text( 2831 "IsItemHovered() after begin = %d (== is title bar hovered)\n" 2832 "IsItemActive() after begin = %d (== is window being clicked/moved)\n", 2833 ImGui::IsItemHovered(), ImGui::IsItemActive()); 2834 ImGui::End(); 2835 } 2836 2837 ImGui::TreePop(); 2838 } 2839 2840 // Demonstrate BeginDisabled/EndDisabled using a checkbox located at the bottom of the section (which is a bit odd: 2841 // logically we'd have this checkbox at the top of the section, but we don't want this feature to steal that space) 2842 if (disable_all) 2843 ImGui::EndDisabled(); 2844 2845 IMGUI_DEMO_MARKER("Widgets/Disable Block"); 2846 if (ImGui::TreeNode("Disable block")) 2847 { 2848 ImGui::Checkbox("Disable entire section above", &disable_all); 2849 ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across this section."); 2850 ImGui::TreePop(); 2851 } 2852 2853 IMGUI_DEMO_MARKER("Widgets/Text Filter"); 2854 if (ImGui::TreeNode("Text Filter")) 2855 { 2856 // Helper class to easy setup a text filter. 2857 // You may want to implement a more feature-full filtering scheme in your own application. 2858 HelpMarker("Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings."); 2859 static ImGuiTextFilter filter; 2860 ImGui::Text("Filter usage:\n" 2861 " \"\" display all lines\n" 2862 " \"xxx\" display lines containing \"xxx\"\n" 2863 " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n" 2864 " \"-xxx\" hide lines containing \"xxx\""); 2865 filter.Draw(); 2866 const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; 2867 for (int i = 0; i < IM_ARRAYSIZE(lines); i++) 2868 if (filter.PassFilter(lines[i])) 2869 ImGui::BulletText("%s", lines[i]); 2870 ImGui::TreePop(); 2871 } 2872 } 2873 2874 static const char* ExampleNames[] = 2875 { 2876 "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", 2877 "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", 2878 "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" 2879 }; 2880 2881 // Extra functions to add deletion support to ImGuiSelectionBasicStorage 2882 struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage 2883 { 2884 // Find which item should be Focused after deletion. 2885 // Call _before_ item submission. Retunr an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it. 2886 // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection. 2887 // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. 2888 // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility. 2889 // - 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. 2890 // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset. 2891 int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count) 2892 { 2893 if (Size == 0) 2894 return -1; 2895 2896 // If focused item is not selected... 2897 const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item 2898 if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx)) 2899 { 2900 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. 2901 return focused_idx; // Request to focus same item after deletion. 2902 } 2903 2904 // If focused item is selected: land on first unselected item after focused item. 2905 for (int idx = focused_idx + 1; idx < items_count; idx++) 2906 if (!Contains(GetStorageIdFromIndex(idx))) 2907 return idx; 2908 2909 // If focused item is selected: otherwise return last unselected item before focused item. 2910 for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--) 2911 if (!Contains(GetStorageIdFromIndex(idx))) 2912 return idx; 2913 2914 return -1; 2915 } 2916 2917 // Rewrite item list (delete items) + update selection. 2918 // - Call after EndMultiSelect() 2919 // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data. 2920 template<typename ITEM_TYPE> 2921 void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& items, int item_curr_idx_to_select) 2922 { 2923 // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection). 2924 // If NavId was not part of selection, we will stay on same item. 2925 ImVector<ITEM_TYPE> new_items; 2926 new_items.reserve(items.Size - Size); 2927 int item_next_idx_to_select = -1; 2928 for (int idx = 0; idx < items.Size; idx++) 2929 { 2930 if (!Contains(GetStorageIdFromIndex(idx))) 2931 new_items.push_back(items[idx]); 2932 if (item_curr_idx_to_select == idx) 2933 item_next_idx_to_select = new_items.Size - 1; 2934 } 2935 items.swap(new_items); 2936 2937 // Update selection 2938 Clear(); 2939 if (item_next_idx_to_select != -1 && ms_io->NavIdSelected) 2940 SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true); 2941 } 2942 }; 2943 2944 // Example: Implement dual list box storage and interface 2945 struct ExampleDualListBox 2946 { 2947 ImVector<ImGuiID> Items[2]; // ID is index into ExampleName[] 2948 ImGuiSelectionBasicStorage Selections[2]; // Store ExampleItemId into selection 2949 bool OptKeepSorted = true; 2950 2951 void MoveAll(int src, int dst) 2952 { 2953 IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0)); 2954 for (ImGuiID item_id : Items[src]) 2955 Items[dst].push_back(item_id); 2956 Items[src].clear(); 2957 SortItems(dst); 2958 Selections[src].Swap(Selections[dst]); 2959 Selections[src].Clear(); 2960 } 2961 void MoveSelected(int src, int dst) 2962 { 2963 for (int src_n = 0; src_n < Items[src].Size; src_n++) 2964 { 2965 ImGuiID item_id = Items[src][src_n]; 2966 if (!Selections[src].Contains(item_id)) 2967 continue; 2968 Items[src].erase(&Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap) 2969 Items[dst].push_back(item_id); 2970 src_n--; 2971 } 2972 if (OptKeepSorted) 2973 SortItems(dst); 2974 Selections[src].Swap(Selections[dst]); 2975 Selections[src].Clear(); 2976 } 2977 void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side) 2978 { 2979 // In this example we store item id in selection (instead of item index) 2980 Selections[side].UserData = Items[side].Data; 2981 Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; }; 2982 Selections[side].ApplyRequests(ms_io); 2983 } 2984 static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs) 2985 { 2986 const int* a = (const int*)lhs; 2987 const int* b = (const int*)rhs; 2988 return (*a - *b) > 0 ? +1 : -1; 2989 } 2990 void SortItems(int n) 2991 { 2992 qsort(Items[n].Data, (size_t)Items[n].Size, sizeof(Items[n][0]), CompareItemsByValue); 2993 } 2994 void Show() 2995 { 2996 //ImGui::Checkbox("Sorted", &OptKeepSorted); 2997 if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None)) 2998 { 2999 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side 3000 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Buttons 3001 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Right side 3002 ImGui::TableNextRow(); 3003 3004 int request_move_selected = -1; 3005 int request_move_all = -1; 3006 float child_height_0 = 0.0f; 3007 for (int side = 0; side < 2; side++) 3008 { 3009 // FIXME-MULTISELECT: Dual List Box: Add context menus 3010 // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues. 3011 ImVector<ImGuiID>& items = Items[side]; 3012 ImGuiSelectionBasicStorage& selection = Selections[side]; 3013 3014 ImGui::TableSetColumnIndex((side == 0) ? 0 : 2); 3015 ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size); 3016 3017 // Submit scrolling range to avoid glitches on moving/deletion 3018 const float items_height = ImGui::GetTextLineHeightWithSpacing(); 3019 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); 3020 3021 bool child_visible; 3022 if (side == 0) 3023 { 3024 // Left child is resizable 3025 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), ImVec2(FLT_MAX, FLT_MAX)); 3026 child_visible = ImGui::BeginChild("0", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY); 3027 child_height_0 = ImGui::GetWindowSize().y; 3028 } 3029 else 3030 { 3031 // Right child use same height as left one 3032 child_visible = ImGui::BeginChild("1", ImVec2(-FLT_MIN, child_height_0), ImGuiChildFlags_FrameStyle); 3033 } 3034 if (child_visible) 3035 { 3036 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; 3037 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); 3038 ApplySelectionRequests(ms_io, side); 3039 3040 for (int item_n = 0; item_n < items.Size; item_n++) 3041 { 3042 ImGuiID item_id = items[item_n]; 3043 bool item_is_selected = selection.Contains(item_id); 3044 ImGui::SetNextItemSelectionUserData(item_n); 3045 ImGui::Selectable(ExampleNames[item_id], item_is_selected, ImGuiSelectableFlags_AllowDoubleClick); 3046 if (ImGui::IsItemFocused()) 3047 { 3048 // FIXME-MULTISELECT: Dual List Box: Transfer focus 3049 if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)) 3050 request_move_selected = side; 3051 if (ImGui::IsMouseDoubleClicked(0)) // FIXME-MULTISELECT: Double-click on multi-selection? 3052 request_move_selected = side; 3053 } 3054 } 3055 3056 ms_io = ImGui::EndMultiSelect(); 3057 ApplySelectionRequests(ms_io, side); 3058 } 3059 ImGui::EndChild(); 3060 } 3061 3062 // Buttons columns 3063 ImGui::TableSetColumnIndex(1); 3064 ImGui::NewLine(); 3065 //ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f }; 3066 ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() }; 3067 3068 // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized) 3069 if (ImGui::Button(">>", button_sz)) 3070 request_move_all = 0; 3071 if (ImGui::Button(">", button_sz)) 3072 request_move_selected = 0; 3073 if (ImGui::Button("<", button_sz)) 3074 request_move_selected = 1; 3075 if (ImGui::Button("<<", button_sz)) 3076 request_move_all = 1; 3077 3078 // Process requests 3079 if (request_move_all != -1) 3080 MoveAll(request_move_all, request_move_all ^ 1); 3081 if (request_move_selected != -1) 3082 MoveSelected(request_move_selected, request_move_selected ^ 1); 3083 3084 // FIXME-MULTISELECT: Support action from outside 3085 /* 3086 if (OptKeepSorted == false) 3087 { 3088 ImGui::NewLine(); 3089 if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {} 3090 if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {} 3091 } 3092 */ 3093 3094 ImGui::EndTable(); 3095 } 3096 } 3097 }; 3098 3099 //----------------------------------------------------------------------------- 3100 // [SECTION] ShowDemoWindowMultiSelect() 3101 //----------------------------------------------------------------------------- 3102 // Multi-selection demos 3103 // Also read: https://github.com/ocornut/imgui/wiki/Multi-Select 3104 //----------------------------------------------------------------------------- 3105 3106 static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data) 3107 { 3108 IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); 3109 if (ImGui::TreeNode("Selection State & Multi-Select")) 3110 { 3111 HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); 3112 3113 // Without any fancy API: manage single-selection yourself. 3114 IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select"); 3115 if (ImGui::TreeNode("Single-Select")) 3116 { 3117 static int selected = -1; 3118 for (int n = 0; n < 5; n++) 3119 { 3120 char buf[32]; 3121 sprintf(buf, "Object %d", n); 3122 if (ImGui::Selectable(buf, selected == n)) 3123 selected = n; 3124 } 3125 ImGui::TreePop(); 3126 } 3127 3128 // Demonstrate implementation a most-basic form of multi-selection manually 3129 // This doesn't support the SHIFT modifier which requires BeginMultiSelect()! 3130 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)"); 3131 if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)")) 3132 { 3133 HelpMarker("Hold CTRL and click to select multiple items."); 3134 static bool selection[5] = { false, false, false, false, false }; 3135 for (int n = 0; n < 5; n++) 3136 { 3137 char buf[32]; 3138 sprintf(buf, "Object %d", n); 3139 if (ImGui::Selectable(buf, selection[n])) 3140 { 3141 if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held 3142 memset(selection, 0, sizeof(selection)); 3143 selection[n] ^= 1; // Toggle current item 3144 } 3145 } 3146 ImGui::TreePop(); 3147 } 3148 3149 // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API. 3150 // SHIFT+Click w/ CTRL and other standard features are supported. 3151 // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement. 3152 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); 3153 if (ImGui::TreeNode("Multi-Select")) 3154 { 3155 ImGui::Text("Supported features:"); 3156 ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space)."); 3157 ImGui::BulletText("Ctrl modifier to preserve and toggle selection."); 3158 ImGui::BulletText("Shift modifier for range selection."); 3159 ImGui::BulletText("CTRL+A to select all."); 3160 ImGui::BulletText("Escape to clear selection."); 3161 ImGui::BulletText("Click and drag to box-select."); 3162 ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen."); 3163 3164 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection 3165 const int ITEMS_COUNT = 50; 3166 static ImGuiSelectionBasicStorage selection; 3167 ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); 3168 3169 // The BeginChild() has no purpose for selection logic, other that offering a scrolling region. 3170 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) 3171 { 3172 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; 3173 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); 3174 selection.ApplyRequests(ms_io); 3175 3176 for (int n = 0; n < ITEMS_COUNT; n++) 3177 { 3178 char label[64]; 3179 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); 3180 bool item_is_selected = selection.Contains((ImGuiID)n); 3181 ImGui::SetNextItemSelectionUserData(n); 3182 ImGui::Selectable(label, item_is_selected); 3183 } 3184 3185 ms_io = ImGui::EndMultiSelect(); 3186 selection.ApplyRequests(ms_io); 3187 } 3188 ImGui::EndChild(); 3189 ImGui::TreePop(); 3190 } 3191 3192 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect() 3193 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)"); 3194 if (ImGui::TreeNode("Multi-Select (with clipper)")) 3195 { 3196 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection 3197 static ImGuiSelectionBasicStorage selection; 3198 3199 ImGui::Text("Added features:"); 3200 ImGui::BulletText("Using ImGuiListClipper."); 3201 3202 const int ITEMS_COUNT = 10000; 3203 ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); 3204 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) 3205 { 3206 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; 3207 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); 3208 selection.ApplyRequests(ms_io); 3209 3210 ImGuiListClipper clipper; 3211 clipper.Begin(ITEMS_COUNT); 3212 if (ms_io->RangeSrcItem != -1) 3213 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. 3214 while (clipper.Step()) 3215 { 3216 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) 3217 { 3218 char label[64]; 3219 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); 3220 bool item_is_selected = selection.Contains((ImGuiID)n); 3221 ImGui::SetNextItemSelectionUserData(n); 3222 ImGui::Selectable(label, item_is_selected); 3223 } 3224 } 3225 3226 ms_io = ImGui::EndMultiSelect(); 3227 selection.ApplyRequests(ms_io); 3228 } 3229 ImGui::EndChild(); 3230 ImGui::TreePop(); 3231 } 3232 3233 // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API. 3234 // In order to support Deletion without any glitches you need to: 3235 // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling. 3236 // - (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. 3237 // - (3) BeginXXXX process 3238 // - (4) Focus process 3239 // - (5) EndXXXX process 3240 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)"); 3241 if (ImGui::TreeNode("Multi-Select (with deletion)")) 3242 { 3243 // Storing items data separately from selection data. 3244 // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items) 3245 // Use a custom selection.Adapter: store item identifier in Selection (instead of index) 3246 static ImVector<ImGuiID> items; 3247 static ExampleSelectionWithDeletion selection; 3248 selection.UserData = (void*)&items; 3249 selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector<ImGuiID>* p_items = (ImVector<ImGuiID>*)self->UserData; return (*p_items)[idx]; }; // Index -> ID 3250 3251 ImGui::Text("Added features:"); 3252 ImGui::BulletText("Dynamic list with Delete key support."); 3253 ImGui::Text("Selection size: %d/%d", selection.Size, items.Size); 3254 3255 // Initialize default list with 50 items + button to add/remove items. 3256 static ImGuiID items_next_id = 0; 3257 if (items_next_id == 0) 3258 for (ImGuiID n = 0; n < 50; n++) 3259 items.push_back(items_next_id++); 3260 if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } } 3261 ImGui::SameLine(); 3262 if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetItemSelected(items.back(), false); items.pop_back(); } } 3263 3264 // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion 3265 const float items_height = ImGui::GetTextLineHeightWithSpacing(); 3266 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); 3267 3268 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) 3269 { 3270 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; 3271 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); 3272 selection.ApplyRequests(ms_io); 3273 3274 const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0); 3275 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; 3276 3277 for (int n = 0; n < items.Size; n++) 3278 { 3279 const ImGuiID item_id = items[n]; 3280 char label[64]; 3281 sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]); 3282 3283 bool item_is_selected = selection.Contains(item_id); 3284 ImGui::SetNextItemSelectionUserData(n); 3285 ImGui::Selectable(label, item_is_selected); 3286 if (item_curr_idx_to_focus == n) 3287 ImGui::SetKeyboardFocusHere(-1); 3288 } 3289 3290 // Apply multi-select requests 3291 ms_io = ImGui::EndMultiSelect(); 3292 selection.ApplyRequests(ms_io); 3293 if (want_delete) 3294 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); 3295 } 3296 ImGui::EndChild(); 3297 ImGui::TreePop(); 3298 } 3299 3300 // Implement a Dual List Box (#6648) 3301 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)"); 3302 if (ImGui::TreeNode("Multi-Select (dual list box)")) 3303 { 3304 // Init default state 3305 static ExampleDualListBox dlb; 3306 if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0) 3307 for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++) 3308 dlb.Items[0].push_back((ImGuiID)item_id); 3309 3310 // Show 3311 dlb.Show(); 3312 3313 ImGui::TreePop(); 3314 } 3315 3316 // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect() 3317 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)"); 3318 if (ImGui::TreeNode("Multi-Select (in a table)")) 3319 { 3320 static ImGuiSelectionBasicStorage selection; 3321 3322 const int ITEMS_COUNT = 10000; 3323 ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); 3324 if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter)) 3325 { 3326 ImGui::TableSetupColumn("Object"); 3327 ImGui::TableSetupColumn("Action"); 3328 ImGui::TableSetupScrollFreeze(0, 1); 3329 ImGui::TableHeadersRow(); 3330 3331 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; 3332 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); 3333 selection.ApplyRequests(ms_io); 3334 3335 ImGuiListClipper clipper; 3336 clipper.Begin(ITEMS_COUNT); 3337 if (ms_io->RangeSrcItem != -1) 3338 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. 3339 while (clipper.Step()) 3340 { 3341 for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) 3342 { 3343 ImGui::TableNextRow(); 3344 ImGui::TableNextColumn(); 3345 char label[64]; 3346 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); 3347 bool item_is_selected = selection.Contains((ImGuiID)n); 3348 ImGui::SetNextItemSelectionUserData(n); 3349 ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); 3350 ImGui::TableNextColumn(); 3351 ImGui::SmallButton("hello"); 3352 } 3353 } 3354 3355 ms_io = ImGui::EndMultiSelect(); 3356 selection.ApplyRequests(ms_io); 3357 ImGui::EndTable(); 3358 } 3359 ImGui::TreePop(); 3360 } 3361 3362 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)"); 3363 if (ImGui::TreeNode("Multi-Select (checkboxes)")) 3364 { 3365 ImGui::Text("In a list of checkboxes (not selectable):"); 3366 ImGui::BulletText("Using _NoAutoSelect + _NoAutoClear flags."); 3367 ImGui::BulletText("Shift+Click to check multiple boxes."); 3368 ImGui::BulletText("Shift+Keyboard to copy current value to other boxes."); 3369 3370 // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper. 3371 static bool items[20] = {}; 3372 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; 3373 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); 3374 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); 3375 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. 3376 3377 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) 3378 { 3379 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items)); 3380 ImGuiSelectionExternalStorage storage_wrapper; 3381 storage_wrapper.UserData = (void*)items; 3382 storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; }; 3383 storage_wrapper.ApplyRequests(ms_io); 3384 for (int n = 0; n < 20; n++) 3385 { 3386 char label[32]; 3387 sprintf(label, "Item %d", n); 3388 ImGui::SetNextItemSelectionUserData(n); 3389 ImGui::Checkbox(label, &items[n]); 3390 } 3391 ms_io = ImGui::EndMultiSelect(); 3392 storage_wrapper.ApplyRequests(ms_io); 3393 } 3394 ImGui::EndChild(); 3395 3396 ImGui::TreePop(); 3397 } 3398 3399 // Demonstrate individual selection scopes in same window 3400 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)"); 3401 if (ImGui::TreeNode("Multi-Select (multiple scopes)")) 3402 { 3403 // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection 3404 const int SCOPES_COUNT = 3; 3405 const int ITEMS_COUNT = 8; // Per scope 3406 static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT]; 3407 3408 // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window. 3409 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid; 3410 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) 3411 flags &= ~ImGuiMultiSelectFlags_ScopeRect; 3412 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) 3413 flags &= ~ImGuiMultiSelectFlags_ScopeWindow; 3414 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); 3415 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); 3416 3417 for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) 3418 { 3419 ImGui::PushID(selection_scope_n); 3420 ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n]; 3421 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT); 3422 selection->ApplyRequests(ms_io); 3423 3424 ImGui::SeparatorText("Selection scope"); 3425 ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT); 3426 3427 for (int n = 0; n < ITEMS_COUNT; n++) 3428 { 3429 char label[64]; 3430 sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); 3431 bool item_is_selected = selection->Contains((ImGuiID)n); 3432 ImGui::SetNextItemSelectionUserData(n); 3433 ImGui::Selectable(label, item_is_selected); 3434 } 3435 3436 // Apply multi-select requests 3437 ms_io = ImGui::EndMultiSelect(); 3438 selection->ApplyRequests(ms_io); 3439 ImGui::PopID(); 3440 } 3441 ImGui::TreePop(); 3442 } 3443 3444 // See ShowExampleAppAssetsBrowser() 3445 if (ImGui::TreeNode("Multi-Select (tiled assets browser)")) 3446 { 3447 ImGui::Checkbox("Assets Browser", &demo_data->ShowAppAssetsBrowser); 3448 ImGui::Text("(also access from 'Examples->Assets Browser' in menu)"); 3449 ImGui::TreePop(); 3450 } 3451 3452 // Demonstrate supporting multiple-selection in a tree. 3453 // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly! 3454 // This showcase how SetNextItemSelectionUserData() never assume indices! 3455 // - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request. 3456 // We want this interpolation to match what the user sees: in visible order, skipping closed nodes. 3457 // This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper. 3458 // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you 3459 // are more likely to build an array mapping sequential indices to visible tree nodes, since your 3460 // filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier. 3461 // - Consider this a prototype: we are working toward simplifying some of it. 3462 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)"); 3463 if (ImGui::TreeNode("Multi-Select (trees)")) 3464 { 3465 HelpMarker( 3466 "This is rather advanced and experimental. If you are getting started with multi-select," 3467 "please don't start by looking at how to use it for a tree!\n\n" 3468 "Future versions will try to simplify and formalize some of this."); 3469 3470 struct ExampleTreeFuncs 3471 { 3472 static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection) 3473 { 3474 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; 3475 tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Enable pressing left to jump to parent 3476 if (node->Childs.Size == 0) 3477 tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf; 3478 if (selection->Contains((ImGuiID)node->UID)) 3479 tree_node_flags |= ImGuiTreeNodeFlags_Selected; 3480 3481 // Using SetNextItemStorageID() to specify storage id, so we can easily peek into 3482 // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions. 3483 ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node); 3484 ImGui::SetNextItemStorageID((ImGuiID)node->UID); 3485 if (ImGui::TreeNodeEx(node->Name, tree_node_flags)) 3486 { 3487 for (ExampleTreeNode* child : node->Childs) 3488 DrawNode(child, selection); 3489 ImGui::TreePop(); 3490 } 3491 else if (ImGui::IsItemToggledOpen()) 3492 { 3493 TreeCloseAndUnselectChildNodes(node, selection); 3494 } 3495 } 3496 3497 static bool TreeNodeGetOpen(ExampleTreeNode* node) 3498 { 3499 return ImGui::GetStateStorage()->GetBool((ImGuiID)node->UID); 3500 } 3501 3502 static void TreeNodeSetOpen(ExampleTreeNode* node, bool open) 3503 { 3504 ImGui::GetStateStorage()->SetBool((ImGuiID)node->UID, open); 3505 } 3506 3507 // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected. 3508 // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node 3509 // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc. 3510 static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0) 3511 { 3512 // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!) 3513 int unselected_count = selection->Contains((ImGuiID)node->UID) ? 1 : 0; 3514 if (depth == 0 || TreeNodeGetOpen(node)) 3515 { 3516 for (ExampleTreeNode* child : node->Childs) 3517 unselected_count += TreeCloseAndUnselectChildNodes(child, selection, depth + 1); 3518 TreeNodeSetOpen(node, false); 3519 } 3520 3521 // Select root node if any of its child was selected, otherwise unselect 3522 selection->SetItemSelected((ImGuiID)node->UID, (depth == 0 && unselected_count > 0)); 3523 return unselected_count; 3524 } 3525 3526 // Apply multi-selection requests 3527 static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection) 3528 { 3529 for (ImGuiSelectionRequest& req : ms_io->Requests) 3530 { 3531 if (req.Type == ImGuiSelectionRequestType_SetAll) 3532 { 3533 if (req.Selected) 3534 TreeSetAllInOpenNodes(tree, selection, req.Selected); 3535 else 3536 selection->Clear(); 3537 } 3538 else if (req.Type == ImGuiSelectionRequestType_SetRange) 3539 { 3540 ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem; 3541 ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem; 3542 for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(node, last_node)) 3543 selection->SetItemSelected((ImGuiID)node->UID, req.Selected); 3544 } 3545 } 3546 } 3547 3548 static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected) 3549 { 3550 if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme 3551 selection->SetItemSelected((ImGuiID)node->UID, selected); 3552 if (node->Parent == NULL || TreeNodeGetOpen(node)) 3553 for (ExampleTreeNode* child : node->Childs) 3554 TreeSetAllInOpenNodes(child, selection, selected); 3555 } 3556 3557 // Interpolate in *user-visible order* AND only *over opened nodes*. 3558 // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler. 3559 // Here the tricks are that: 3560 // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion. 3561 // this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)' 3562 // which would only be called when crossing from child to a parent, aka not too much. 3563 // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack, 3564 // making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location. 3565 static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node) 3566 { 3567 // Reached last node 3568 if (curr_node == last_node) 3569 return NULL; 3570 3571 // Recurse into childs. Query storage to tell if the node is open. 3572 if (curr_node->Childs.Size > 0 && TreeNodeGetOpen(curr_node)) 3573 return curr_node->Childs[0]; 3574 3575 // Next sibling, then into our own parent 3576 while (curr_node->Parent != NULL) 3577 { 3578 if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size) 3579 return curr_node->Parent->Childs[curr_node->IndexInParent + 1]; 3580 curr_node = curr_node->Parent; 3581 } 3582 return NULL; 3583 } 3584 3585 }; // ExampleTreeFuncs 3586 3587 static ImGuiSelectionBasicStorage selection; 3588 if (demo_data->DemoTree == NULL) 3589 demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once 3590 ImGui::Text("Selection size: %d", selection.Size); 3591 3592 if (ImGui::BeginChild("##Tree", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) 3593 { 3594 ExampleTreeNode* tree = demo_data->DemoTree; 3595 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d; 3596 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1); 3597 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection); 3598 for (ExampleTreeNode* node : tree->Childs) 3599 ExampleTreeFuncs::DrawNode(node, &selection); 3600 ms_io = ImGui::EndMultiSelect(); 3601 ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection); 3602 } 3603 ImGui::EndChild(); 3604 3605 ImGui::TreePop(); 3606 } 3607 3608 // Advanced demonstration of BeginMultiSelect() 3609 // - Showcase clipping. 3610 // - Showcase deletion. 3611 // - Showcase basic drag and drop. 3612 // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). 3613 // - Showcase using inside a table. 3614 IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)"); 3615 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); 3616 if (ImGui::TreeNode("Multi-Select (advanced)")) 3617 { 3618 // Options 3619 enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; 3620 static bool use_clipper = true; 3621 static bool use_deletion = true; 3622 static bool use_drag_drop = true; 3623 static bool show_in_table = false; 3624 static bool show_color_button = true; 3625 static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; 3626 static WidgetType widget_type = WidgetType_Selectable; 3627 3628 if (ImGui::TreeNode("Options")) 3629 { 3630 if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } 3631 ImGui::SameLine(); 3632 if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } 3633 ImGui::SameLine(); 3634 HelpMarker("TreeNode() is technically supported but... using this correctly is more complicated (you need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this.\n\nFor now the tree demo is actually a little bit meaningless because it is an empty tree with only root nodes."); 3635 ImGui::Checkbox("Enable clipper", &use_clipper); 3636 ImGui::Checkbox("Enable deletion", &use_deletion); 3637 ImGui::Checkbox("Enable drag & drop", &use_drag_drop); 3638 ImGui::Checkbox("Show in a table", &show_in_table); 3639 ImGui::Checkbox("Show color button", &show_color_button); 3640 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); 3641 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); 3642 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect); 3643 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); 3644 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); 3645 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect); 3646 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); 3647 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); 3648 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); 3649 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); 3650 ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); 3651 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) 3652 flags &= ~ImGuiMultiSelectFlags_ScopeRect; 3653 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) 3654 flags &= ~ImGuiMultiSelectFlags_ScopeWindow; 3655 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick)) 3656 flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; 3657 if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) 3658 flags &= ~ImGuiMultiSelectFlags_SelectOnClick; 3659 ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); 3660 ImGui::TreePop(); 3661 } 3662 3663 // Initialize default list with 1000 items. 3664 // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection 3665 static ImVector<int> items; 3666 static int items_next_id = 0; 3667 if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } } 3668 static ExampleSelectionWithDeletion selection; 3669 static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu 3670 3671 ImGui::Text("Selection size: %d/%d", selection.Size, items.Size); 3672 3673 const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing(); 3674 ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); 3675 if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) 3676 { 3677 ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); 3678 if (widget_type == WidgetType_TreeNode) 3679 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); 3680 3681 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); 3682 selection.ApplyRequests(ms_io); 3683 3684 const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu; 3685 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; 3686 request_deletion_from_menu = false; 3687 3688 if (show_in_table) 3689 { 3690 if (widget_type == WidgetType_TreeNode) 3691 ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); 3692 ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); 3693 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); 3694 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); 3695 //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); 3696 } 3697 3698 ImGuiListClipper clipper; 3699 if (use_clipper) 3700 { 3701 clipper.Begin(items.Size); 3702 if (item_curr_idx_to_focus != -1) 3703 clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped. 3704 if (ms_io->RangeSrcItem != -1) 3705 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. 3706 } 3707 3708 while (!use_clipper || clipper.Step()) 3709 { 3710 const int item_begin = use_clipper ? clipper.DisplayStart : 0; 3711 const int item_end = use_clipper ? clipper.DisplayEnd : items.Size; 3712 for (int n = item_begin; n < item_end; n++) 3713 { 3714 if (show_in_table) 3715 ImGui::TableNextColumn(); 3716 3717 const int item_id = items[n]; 3718 const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]; 3719 char label[64]; 3720 sprintf(label, "Object %05d: %s", item_id, item_category); 3721 3722 // IMPORTANT: for deletion refocus to work we need object ID to be stable, 3723 // aka not depend on their index in the list. Here we use our persistent item_id 3724 // instead of index to build a unique ID that will persist. 3725 // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion). 3726 ImGui::PushID(item_id); 3727 3728 // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part 3729 // of the selection scope doesn't erroneously alter our selection. 3730 if (show_color_button) 3731 { 3732 ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; 3733 ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz); 3734 ImGui::SameLine(); 3735 } 3736 3737 // Submit item 3738 bool item_is_selected = selection.Contains((ImGuiID)n); 3739 bool item_is_open = false; 3740 ImGui::SetNextItemSelectionUserData(n); 3741 if (widget_type == WidgetType_Selectable) 3742 { 3743 ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None); 3744 } 3745 else if (widget_type == WidgetType_TreeNode) 3746 { 3747 ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; 3748 if (item_is_selected) 3749 tree_node_flags |= ImGuiTreeNodeFlags_Selected; 3750 item_is_open = ImGui::TreeNodeEx(label, tree_node_flags); 3751 } 3752 3753 // Focus (for after deletion) 3754 if (item_curr_idx_to_focus == n) 3755 ImGui::SetKeyboardFocusHere(-1); 3756 3757 // Drag and Drop 3758 if (use_drag_drop && ImGui::BeginDragDropSource()) 3759 { 3760 // Create payload with full selection OR single unselected item. 3761 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) 3762 if (ImGui::GetDragDropPayload() == NULL) 3763 { 3764 ImVector<int> payload_items; 3765 void* it = NULL; 3766 ImGuiID id = 0; 3767 if (!item_is_selected) 3768 payload_items.push_back(item_id); 3769 else 3770 while (selection.GetNextSelectedItem(&it, &id)) 3771 payload_items.push_back((int)id); 3772 ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); 3773 } 3774 3775 // Display payload content in tooltip 3776 const ImGuiPayload* payload = ImGui::GetDragDropPayload(); 3777 const int* payload_items = (int*)payload->Data; 3778 const int payload_count = (int)payload->DataSize / (int)sizeof(int); 3779 if (payload_count == 1) 3780 ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]); 3781 else 3782 ImGui::Text("Dragging %d objects", payload_count); 3783 3784 ImGui::EndDragDropSource(); 3785 } 3786 3787 if (widget_type == WidgetType_TreeNode && item_is_open) 3788 ImGui::TreePop(); 3789 3790 // Right-click: context menu 3791 if (ImGui::BeginPopupContextItem()) 3792 { 3793 ImGui::BeginDisabled(!use_deletion || selection.Size == 0); 3794 sprintf(label, "Delete %d item(s)###DeleteSelected", selection.Size); 3795 if (ImGui::Selectable(label)) 3796 request_deletion_from_menu = true; 3797 ImGui::EndDisabled(); 3798 ImGui::Selectable("Close"); 3799 ImGui::EndPopup(); 3800 } 3801 3802 // Demo content within a table 3803 if (show_in_table) 3804 { 3805 ImGui::TableNextColumn(); 3806 ImGui::SetNextItemWidth(-FLT_MIN); 3807 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); 3808 ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); 3809 ImGui::PopStyleVar(); 3810 } 3811 3812 ImGui::PopID(); 3813 } 3814 if (!use_clipper) 3815 break; 3816 } 3817 3818 if (show_in_table) 3819 { 3820 ImGui::EndTable(); 3821 if (widget_type == WidgetType_TreeNode) 3822 ImGui::PopStyleVar(); 3823 } 3824 3825 // Apply multi-select requests 3826 ms_io = ImGui::EndMultiSelect(); 3827 selection.ApplyRequests(ms_io); 3828 if (want_delete) 3829 selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); 3830 3831 if (widget_type == WidgetType_TreeNode) 3832 ImGui::PopStyleVar(); 3833 } 3834 ImGui::EndChild(); 3835 ImGui::TreePop(); 3836 } 3837 ImGui::TreePop(); 3838 } 3839 } 3840 3841 //----------------------------------------------------------------------------- 3842 // [SECTION] ShowDemoWindowLayout() 3843 //----------------------------------------------------------------------------- 3844 3845 static void ShowDemoWindowLayout() 3846 { 3847 IMGUI_DEMO_MARKER("Layout"); 3848 if (!ImGui::CollapsingHeader("Layout & Scrolling")) 3849 return; 3850 3851 IMGUI_DEMO_MARKER("Layout/Child windows"); 3852 if (ImGui::TreeNode("Child windows")) 3853 { 3854 ImGui::SeparatorText("Child windows"); 3855 3856 HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); 3857 static bool disable_mouse_wheel = false; 3858 static bool disable_menu = false; 3859 ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); 3860 ImGui::Checkbox("Disable Menu", &disable_menu); 3861 3862 // Child 1: no border, enable horizontal scrollbar 3863 { 3864 ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; 3865 if (disable_mouse_wheel) 3866 window_flags |= ImGuiWindowFlags_NoScrollWithMouse; 3867 ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags); 3868 for (int i = 0; i < 100; i++) 3869 ImGui::Text("%04d: scrollable region", i); 3870 ImGui::EndChild(); 3871 } 3872 3873 ImGui::SameLine(); 3874 3875 // Child 2: rounded border 3876 { 3877 ImGuiWindowFlags window_flags = ImGuiWindowFlags_None; 3878 if (disable_mouse_wheel) 3879 window_flags |= ImGuiWindowFlags_NoScrollWithMouse; 3880 if (!disable_menu) 3881 window_flags |= ImGuiWindowFlags_MenuBar; 3882 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); 3883 ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Border, window_flags); 3884 if (!disable_menu && ImGui::BeginMenuBar()) 3885 { 3886 if (ImGui::BeginMenu("Menu")) 3887 { 3888 ShowExampleMenuFile(); 3889 ImGui::EndMenu(); 3890 } 3891 ImGui::EndMenuBar(); 3892 } 3893 if (ImGui::BeginTable("split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings)) 3894 { 3895 for (int i = 0; i < 100; i++) 3896 { 3897 char buf[32]; 3898 sprintf(buf, "%03d", i); 3899 ImGui::TableNextColumn(); 3900 ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); 3901 } 3902 ImGui::EndTable(); 3903 } 3904 ImGui::EndChild(); 3905 ImGui::PopStyleVar(); 3906 } 3907 3908 // Child 3: manual-resize 3909 ImGui::SeparatorText("Manual-resize"); 3910 { 3911 HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents."); 3912 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); 3913 if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) 3914 for (int n = 0; n < 10; n++) 3915 ImGui::Text("Line %04d", n); 3916 ImGui::PopStyleColor(); 3917 ImGui::EndChild(); 3918 } 3919 3920 // Child 4: auto-resizing height with a limit 3921 ImGui::SeparatorText("Auto-resize with constraints"); 3922 { 3923 static int draw_lines = 3; 3924 static int max_height_in_lines = 10; 3925 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 3926 ImGui::DragInt("Lines Count", &draw_lines, 0.2f); 3927 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 3928 ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f); 3929 3930 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines)); 3931 if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) 3932 for (int n = 0; n < draw_lines; n++) 3933 ImGui::Text("Line %04d", n); 3934 ImGui::EndChild(); 3935 } 3936 3937 ImGui::SeparatorText("Misc/Advanced"); 3938 3939 // Demonstrate a few extra things 3940 // - Changing ImGuiCol_ChildBg (which is transparent black in default styles) 3941 // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window) 3942 // You can also call SetNextWindowPos() to position the child window. The parent window will effectively 3943 // layout from this position. 3944 // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from 3945 // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details. 3946 { 3947 static int offset_x = 0; 3948 static bool override_bg_color = true; 3949 static ImGuiChildFlags child_flags = ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY; 3950 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 3951 ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); 3952 ImGui::Checkbox("Override ChildBg color", &override_bg_color); 3953 ImGui::CheckboxFlags("ImGuiChildFlags_Border", &child_flags, ImGuiChildFlags_Border); 3954 ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding); 3955 ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX); 3956 ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY); 3957 ImGui::CheckboxFlags("ImGuiChildFlags_FrameStyle", &child_flags, ImGuiChildFlags_FrameStyle); 3958 ImGui::SameLine(); HelpMarker("Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding."); 3959 if (child_flags & ImGuiChildFlags_FrameStyle) 3960 override_bg_color = false; 3961 3962 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x); 3963 if (override_bg_color) 3964 ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); 3965 ImGui::BeginChild("Red", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None); 3966 if (override_bg_color) 3967 ImGui::PopStyleColor(); 3968 3969 for (int n = 0; n < 50; n++) 3970 ImGui::Text("Some test %d", n); 3971 ImGui::EndChild(); 3972 bool child_is_hovered = ImGui::IsItemHovered(); 3973 ImVec2 child_rect_min = ImGui::GetItemRectMin(); 3974 ImVec2 child_rect_max = ImGui::GetItemRectMax(); 3975 ImGui::Text("Hovered: %d", child_is_hovered); 3976 ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y); 3977 } 3978 3979 ImGui::TreePop(); 3980 } 3981 3982 IMGUI_DEMO_MARKER("Layout/Widgets Width"); 3983 if (ImGui::TreeNode("Widgets Width")) 3984 { 3985 static float f = 0.0f; 3986 static bool show_indented_items = true; 3987 ImGui::Checkbox("Show indented items", &show_indented_items); 3988 3989 // Use SetNextItemWidth() to set the width of a single upcoming item. 3990 // Use PushItemWidth()/PopItemWidth() to set the width of a group of items. 3991 // In real code use you'll probably want to choose width values that are proportional to your font size 3992 // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc. 3993 3994 ImGui::Text("SetNextItemWidth/PushItemWidth(100)"); 3995 ImGui::SameLine(); HelpMarker("Fixed width."); 3996 ImGui::PushItemWidth(100); 3997 ImGui::DragFloat("float##1b", &f); 3998 if (show_indented_items) 3999 { 4000 ImGui::Indent(); 4001 ImGui::DragFloat("float (indented)##1b", &f); 4002 ImGui::Unindent(); 4003 } 4004 ImGui::PopItemWidth(); 4005 4006 ImGui::Text("SetNextItemWidth/PushItemWidth(-100)"); 4007 ImGui::SameLine(); HelpMarker("Align to right edge minus 100"); 4008 ImGui::PushItemWidth(-100); 4009 ImGui::DragFloat("float##2a", &f); 4010 if (show_indented_items) 4011 { 4012 ImGui::Indent(); 4013 ImGui::DragFloat("float (indented)##2b", &f); 4014 ImGui::Unindent(); 4015 } 4016 ImGui::PopItemWidth(); 4017 4018 ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)"); 4019 ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); 4020 ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 4021 ImGui::DragFloat("float##3a", &f); 4022 if (show_indented_items) 4023 { 4024 ImGui::Indent(); 4025 ImGui::DragFloat("float (indented)##3b", &f); 4026 ImGui::Unindent(); 4027 } 4028 ImGui::PopItemWidth(); 4029 4030 ImGui::Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)"); 4031 ImGui::SameLine(); HelpMarker("Align to right edge minus half"); 4032 ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); 4033 ImGui::DragFloat("float##4a", &f); 4034 if (show_indented_items) 4035 { 4036 ImGui::Indent(); 4037 ImGui::DragFloat("float (indented)##4b", &f); 4038 ImGui::Unindent(); 4039 } 4040 ImGui::PopItemWidth(); 4041 4042 // Demonstrate using PushItemWidth to surround three items. 4043 // Calling SetNextItemWidth() before each of them would have the same effect. 4044 ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)"); 4045 ImGui::SameLine(); HelpMarker("Align to right edge"); 4046 ImGui::PushItemWidth(-FLT_MIN); 4047 ImGui::DragFloat("##float5a", &f); 4048 if (show_indented_items) 4049 { 4050 ImGui::Indent(); 4051 ImGui::DragFloat("float (indented)##5b", &f); 4052 ImGui::Unindent(); 4053 } 4054 ImGui::PopItemWidth(); 4055 4056 ImGui::TreePop(); 4057 } 4058 4059 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout"); 4060 if (ImGui::TreeNode("Basic Horizontal Layout")) 4061 { 4062 ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); 4063 4064 // Text 4065 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); 4066 ImGui::Text("Two items: Hello"); ImGui::SameLine(); 4067 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor"); 4068 4069 // Adjust spacing 4070 ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); 4071 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor"); 4072 4073 // Button 4074 ImGui::AlignTextToFramePadding(); 4075 ImGui::Text("Normal buttons"); ImGui::SameLine(); 4076 ImGui::Button("Banana"); ImGui::SameLine(); 4077 ImGui::Button("Apple"); ImGui::SameLine(); 4078 ImGui::Button("Corniflower"); 4079 4080 // Button 4081 ImGui::Text("Small buttons"); ImGui::SameLine(); 4082 ImGui::SmallButton("Like this one"); ImGui::SameLine(); 4083 ImGui::Text("can fit within a text block."); 4084 4085 // Aligned to arbitrary position. Easy/cheap column. 4086 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)"); 4087 ImGui::Text("Aligned"); 4088 ImGui::SameLine(150); ImGui::Text("x=150"); 4089 ImGui::SameLine(300); ImGui::Text("x=300"); 4090 ImGui::Text("Aligned"); 4091 ImGui::SameLine(150); ImGui::SmallButton("x=150"); 4092 ImGui::SameLine(300); ImGui::SmallButton("x=300"); 4093 4094 // Checkbox 4095 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)"); 4096 static bool c1 = false, c2 = false, c3 = false, c4 = false; 4097 ImGui::Checkbox("My", &c1); ImGui::SameLine(); 4098 ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); 4099 ImGui::Checkbox("Is", &c3); ImGui::SameLine(); 4100 ImGui::Checkbox("Rich", &c4); 4101 4102 // Various 4103 static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f; 4104 ImGui::PushItemWidth(80); 4105 const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; 4106 static int item = -1; 4107 ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); 4108 ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine(); 4109 ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine(); 4110 ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f); 4111 ImGui::PopItemWidth(); 4112 4113 ImGui::PushItemWidth(80); 4114 ImGui::Text("Lists:"); 4115 static int selection[4] = { 0, 1, 2, 3 }; 4116 for (int i = 0; i < 4; i++) 4117 { 4118 if (i > 0) ImGui::SameLine(); 4119 ImGui::PushID(i); 4120 ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); 4121 ImGui::PopID(); 4122 //ImGui::SetItemTooltip("ListBox %d hovered", i); 4123 } 4124 ImGui::PopItemWidth(); 4125 4126 // Dummy 4127 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy"); 4128 ImVec2 button_sz(40, 40); 4129 ImGui::Button("A", button_sz); ImGui::SameLine(); 4130 ImGui::Dummy(button_sz); ImGui::SameLine(); 4131 ImGui::Button("B", button_sz); 4132 4133 // Manually wrapping 4134 // (we should eventually provide this as an automatic layout feature, but for now you can do it manually) 4135 IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping"); 4136 ImGui::Text("Manual wrapping:"); 4137 ImGuiStyle& style = ImGui::GetStyle(); 4138 int buttons_count = 20; 4139 float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x; 4140 for (int n = 0; n < buttons_count; n++) 4141 { 4142 ImGui::PushID(n); 4143 ImGui::Button("Box", button_sz); 4144 float last_button_x2 = ImGui::GetItemRectMax().x; 4145 float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line 4146 if (n + 1 < buttons_count && next_button_x2 < window_visible_x2) 4147 ImGui::SameLine(); 4148 ImGui::PopID(); 4149 } 4150 4151 ImGui::TreePop(); 4152 } 4153 4154 IMGUI_DEMO_MARKER("Layout/Groups"); 4155 if (ImGui::TreeNode("Groups")) 4156 { 4157 HelpMarker( 4158 "BeginGroup() basically locks the horizontal position for new line. " 4159 "EndGroup() bundles the whole group so that you can use \"item\" functions such as " 4160 "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group."); 4161 ImGui::BeginGroup(); 4162 { 4163 ImGui::BeginGroup(); 4164 ImGui::Button("AAA"); 4165 ImGui::SameLine(); 4166 ImGui::Button("BBB"); 4167 ImGui::SameLine(); 4168 ImGui::BeginGroup(); 4169 ImGui::Button("CCC"); 4170 ImGui::Button("DDD"); 4171 ImGui::EndGroup(); 4172 ImGui::SameLine(); 4173 ImGui::Button("EEE"); 4174 ImGui::EndGroup(); 4175 ImGui::SetItemTooltip("First group hovered"); 4176 } 4177 // Capture the group size and create widgets using the same size 4178 ImVec2 size = ImGui::GetItemRectSize(); 4179 const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; 4180 ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); 4181 4182 ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y)); 4183 ImGui::SameLine(); 4184 ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y)); 4185 ImGui::EndGroup(); 4186 ImGui::SameLine(); 4187 4188 ImGui::Button("LEVERAGE\nBUZZWORD", size); 4189 ImGui::SameLine(); 4190 4191 if (ImGui::BeginListBox("List", size)) 4192 { 4193 ImGui::Selectable("Selected", true); 4194 ImGui::Selectable("Not Selected", false); 4195 ImGui::EndListBox(); 4196 } 4197 4198 ImGui::TreePop(); 4199 } 4200 4201 IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment"); 4202 if (ImGui::TreeNode("Text Baseline Alignment")) 4203 { 4204 { 4205 ImGui::BulletText("Text baseline:"); 4206 ImGui::SameLine(); HelpMarker( 4207 "This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. " 4208 "Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets."); 4209 ImGui::Indent(); 4210 4211 ImGui::Text("KO Blahblah"); ImGui::SameLine(); 4212 ImGui::Button("Some framed item"); ImGui::SameLine(); 4213 HelpMarker("Baseline of button will look misaligned with text.."); 4214 4215 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets. 4216 // (because we don't know what's coming after the Text() statement, we need to move the text baseline 4217 // down by FramePadding.y ahead of time) 4218 ImGui::AlignTextToFramePadding(); 4219 ImGui::Text("OK Blahblah"); ImGui::SameLine(); 4220 ImGui::Button("Some framed item"); ImGui::SameLine(); 4221 HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y"); 4222 4223 // SmallButton() uses the same vertical padding as Text 4224 ImGui::Button("TEST##1"); ImGui::SameLine(); 4225 ImGui::Text("TEST"); ImGui::SameLine(); 4226 ImGui::SmallButton("TEST##2"); 4227 4228 // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets. 4229 ImGui::AlignTextToFramePadding(); 4230 ImGui::Text("Text aligned to framed item"); ImGui::SameLine(); 4231 ImGui::Button("Item##1"); ImGui::SameLine(); 4232 ImGui::Text("Item"); ImGui::SameLine(); 4233 ImGui::SmallButton("Item##2"); ImGui::SameLine(); 4234 ImGui::Button("Item##3"); 4235 4236 ImGui::Unindent(); 4237 } 4238 4239 ImGui::Spacing(); 4240 4241 { 4242 ImGui::BulletText("Multi-line text:"); 4243 ImGui::Indent(); 4244 ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); 4245 ImGui::Text("Hello\nWorld"); ImGui::SameLine(); 4246 ImGui::Text("Banana"); 4247 4248 ImGui::Text("Banana"); ImGui::SameLine(); 4249 ImGui::Text("Hello\nWorld"); ImGui::SameLine(); 4250 ImGui::Text("One\nTwo\nThree"); 4251 4252 ImGui::Button("HOP##1"); ImGui::SameLine(); 4253 ImGui::Text("Banana"); ImGui::SameLine(); 4254 ImGui::Text("Hello\nWorld"); ImGui::SameLine(); 4255 ImGui::Text("Banana"); 4256 4257 ImGui::Button("HOP##2"); ImGui::SameLine(); 4258 ImGui::Text("Hello\nWorld"); ImGui::SameLine(); 4259 ImGui::Text("Banana"); 4260 ImGui::Unindent(); 4261 } 4262 4263 ImGui::Spacing(); 4264 4265 { 4266 ImGui::BulletText("Misc items:"); 4267 ImGui::Indent(); 4268 4269 // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button. 4270 ImGui::Button("80x80", ImVec2(80, 80)); 4271 ImGui::SameLine(); 4272 ImGui::Button("50x50", ImVec2(50, 50)); 4273 ImGui::SameLine(); 4274 ImGui::Button("Button()"); 4275 ImGui::SameLine(); 4276 ImGui::SmallButton("SmallButton()"); 4277 4278 // Tree 4279 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; 4280 ImGui::Button("Button##1"); 4281 ImGui::SameLine(0.0f, spacing); 4282 if (ImGui::TreeNode("Node##1")) 4283 { 4284 // Placeholder tree data 4285 for (int i = 0; i < 6; i++) 4286 ImGui::BulletText("Item %d..", i); 4287 ImGui::TreePop(); 4288 } 4289 4290 // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. 4291 // Otherwise you can use SmallButton() (smaller fit). 4292 ImGui::AlignTextToFramePadding(); 4293 4294 // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add 4295 // other contents below the node. 4296 bool node_open = ImGui::TreeNode("Node##2"); 4297 ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); 4298 if (node_open) 4299 { 4300 // Placeholder tree data 4301 for (int i = 0; i < 6; i++) 4302 ImGui::BulletText("Item %d..", i); 4303 ImGui::TreePop(); 4304 } 4305 4306 // Bullet 4307 ImGui::Button("Button##3"); 4308 ImGui::SameLine(0.0f, spacing); 4309 ImGui::BulletText("Bullet text"); 4310 4311 ImGui::AlignTextToFramePadding(); 4312 ImGui::BulletText("Node"); 4313 ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); 4314 ImGui::Unindent(); 4315 } 4316 4317 ImGui::TreePop(); 4318 } 4319 4320 IMGUI_DEMO_MARKER("Layout/Scrolling"); 4321 if (ImGui::TreeNode("Scrolling")) 4322 { 4323 // Vertical scroll functions 4324 IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical"); 4325 HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position."); 4326 4327 static int track_item = 50; 4328 static bool enable_track = true; 4329 static bool enable_extra_decorations = false; 4330 static float scroll_to_off_px = 0.0f; 4331 static float scroll_to_pos_px = 200.0f; 4332 4333 ImGui::Checkbox("Decoration", &enable_extra_decorations); 4334 4335 ImGui::Checkbox("Track", &enable_track); 4336 ImGui::PushItemWidth(100); 4337 ImGui::SameLine(140); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); 4338 4339 bool scroll_to_off = ImGui::Button("Scroll Offset"); 4340 ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); 4341 4342 bool scroll_to_pos = ImGui::Button("Scroll To Pos"); 4343 ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); 4344 ImGui::PopItemWidth(); 4345 4346 if (scroll_to_off || scroll_to_pos) 4347 enable_track = false; 4348 4349 ImGuiStyle& style = ImGui::GetStyle(); 4350 float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5; 4351 if (child_w < 1.0f) 4352 child_w = 1.0f; 4353 ImGui::PushID("##VerticalScrolling"); 4354 for (int i = 0; i < 5; i++) 4355 { 4356 if (i > 0) ImGui::SameLine(); 4357 ImGui::BeginGroup(); 4358 const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" }; 4359 ImGui::TextUnformatted(names[i]); 4360 4361 const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0; 4362 const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); 4363 const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Border, child_flags); 4364 if (ImGui::BeginMenuBar()) 4365 { 4366 ImGui::TextUnformatted("abc"); 4367 ImGui::EndMenuBar(); 4368 } 4369 if (scroll_to_off) 4370 ImGui::SetScrollY(scroll_to_off_px); 4371 if (scroll_to_pos) 4372 ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f); 4373 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items 4374 { 4375 for (int item = 0; item < 100; item++) 4376 { 4377 if (enable_track && item == track_item) 4378 { 4379 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); 4380 ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom 4381 } 4382 else 4383 { 4384 ImGui::Text("Item %d", item); 4385 } 4386 } 4387 } 4388 float scroll_y = ImGui::GetScrollY(); 4389 float scroll_max_y = ImGui::GetScrollMaxY(); 4390 ImGui::EndChild(); 4391 ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y); 4392 ImGui::EndGroup(); 4393 } 4394 ImGui::PopID(); 4395 4396 // Horizontal scroll functions 4397 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal"); 4398 ImGui::Spacing(); 4399 HelpMarker( 4400 "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n" 4401 "Because the clipping rectangle of most window hides half worth of WindowPadding on the " 4402 "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the " 4403 "equivalent SetScrollFromPosY(+1) wouldn't."); 4404 ImGui::PushID("##HorizontalScrolling"); 4405 for (int i = 0; i < 5; i++) 4406 { 4407 float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f; 4408 ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0); 4409 ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); 4410 bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Border, child_flags); 4411 if (scroll_to_off) 4412 ImGui::SetScrollX(scroll_to_off_px); 4413 if (scroll_to_pos) 4414 ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f); 4415 if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items 4416 { 4417 for (int item = 0; item < 100; item++) 4418 { 4419 if (item > 0) 4420 ImGui::SameLine(); 4421 if (enable_track && item == track_item) 4422 { 4423 ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); 4424 ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right 4425 } 4426 else 4427 { 4428 ImGui::Text("Item %d", item); 4429 } 4430 } 4431 } 4432 float scroll_x = ImGui::GetScrollX(); 4433 float scroll_max_x = ImGui::GetScrollMaxX(); 4434 ImGui::EndChild(); 4435 ImGui::SameLine(); 4436 const char* names[] = { "Left", "25%", "Center", "75%", "Right" }; 4437 ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x); 4438 ImGui::Spacing(); 4439 } 4440 ImGui::PopID(); 4441 4442 // Miscellaneous Horizontal Scrolling Demo 4443 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)"); 4444 HelpMarker( 4445 "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n" 4446 "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin()."); 4447 static int lines = 7; 4448 ImGui::SliderInt("Lines", &lines, 1, 15); 4449 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); 4450 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); 4451 ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30); 4452 ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Border, ImGuiWindowFlags_HorizontalScrollbar); 4453 for (int line = 0; line < lines; line++) 4454 { 4455 // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine() 4456 // If you want to create your own time line for a real application you may be better off manipulating 4457 // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets 4458 // yourself. You may also want to use the lower-level ImDrawList API. 4459 int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); 4460 for (int n = 0; n < num_buttons; n++) 4461 { 4462 if (n > 0) ImGui::SameLine(); 4463 ImGui::PushID(n + line * 1000); 4464 char num_buf[16]; 4465 sprintf(num_buf, "%d", n); 4466 const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf; 4467 float hue = n * 0.05f; 4468 ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); 4469 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); 4470 ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); 4471 ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); 4472 ImGui::PopStyleColor(3); 4473 ImGui::PopID(); 4474 } 4475 } 4476 float scroll_x = ImGui::GetScrollX(); 4477 float scroll_max_x = ImGui::GetScrollMaxX(); 4478 ImGui::EndChild(); 4479 ImGui::PopStyleVar(2); 4480 float scroll_x_delta = 0.0f; 4481 ImGui::SmallButton("<<"); 4482 if (ImGui::IsItemActive()) 4483 scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; 4484 ImGui::SameLine(); 4485 ImGui::Text("Scroll from code"); ImGui::SameLine(); 4486 ImGui::SmallButton(">>"); 4487 if (ImGui::IsItemActive()) 4488 scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; 4489 ImGui::SameLine(); 4490 ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); 4491 if (scroll_x_delta != 0.0f) 4492 { 4493 // Demonstrate a trick: you can use Begin to set yourself in the context of another window 4494 // (here we are already out of your child window) 4495 ImGui::BeginChild("scrolling"); 4496 ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); 4497 ImGui::EndChild(); 4498 } 4499 ImGui::Spacing(); 4500 4501 static bool show_horizontal_contents_size_demo_window = false; 4502 ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window); 4503 4504 if (show_horizontal_contents_size_demo_window) 4505 { 4506 static bool show_h_scrollbar = true; 4507 static bool show_button = true; 4508 static bool show_tree_nodes = true; 4509 static bool show_text_wrapped = false; 4510 static bool show_columns = true; 4511 static bool show_tab_bar = true; 4512 static bool show_child = false; 4513 static bool explicit_content_size = false; 4514 static float contents_size_x = 300.0f; 4515 if (explicit_content_size) 4516 ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f)); 4517 ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0); 4518 IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window"); 4519 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); 4520 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); 4521 HelpMarker( 4522 "Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n" 4523 "Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); 4524 ImGui::Checkbox("H-scrollbar", &show_h_scrollbar); 4525 ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten) 4526 ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width 4527 ImGui::Checkbox("Text wrapped", &show_text_wrapped);// Will grow and use contents size 4528 ImGui::Checkbox("Columns", &show_columns); // Will use contents size 4529 ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size 4530 ImGui::Checkbox("Child", &show_child); // Will grow and use contents size 4531 ImGui::Checkbox("Explicit content size", &explicit_content_size); 4532 ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY()); 4533 if (explicit_content_size) 4534 { 4535 ImGui::SameLine(); 4536 ImGui::SetNextItemWidth(100); 4537 ImGui::DragFloat("##csx", &contents_size_x); 4538 ImVec2 p = ImGui::GetCursorScreenPos(); 4539 ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE); 4540 ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE); 4541 ImGui::Dummy(ImVec2(0, 10)); 4542 } 4543 ImGui::PopStyleVar(2); 4544 ImGui::Separator(); 4545 if (show_button) 4546 { 4547 ImGui::Button("this is a 300-wide button", ImVec2(300, 0)); 4548 } 4549 if (show_tree_nodes) 4550 { 4551 bool open = true; 4552 if (ImGui::TreeNode("this is a tree node")) 4553 { 4554 if (ImGui::TreeNode("another one of those tree node...")) 4555 { 4556 ImGui::Text("Some tree contents"); 4557 ImGui::TreePop(); 4558 } 4559 ImGui::TreePop(); 4560 } 4561 ImGui::CollapsingHeader("CollapsingHeader", &open); 4562 } 4563 if (show_text_wrapped) 4564 { 4565 ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle."); 4566 } 4567 if (show_columns) 4568 { 4569 ImGui::Text("Tables:"); 4570 if (ImGui::BeginTable("table", 4, ImGuiTableFlags_Borders)) 4571 { 4572 for (int n = 0; n < 4; n++) 4573 { 4574 ImGui::TableNextColumn(); 4575 ImGui::Text("Width %.2f", ImGui::GetContentRegionAvail().x); 4576 } 4577 ImGui::EndTable(); 4578 } 4579 ImGui::Text("Columns:"); 4580 ImGui::Columns(4); 4581 for (int n = 0; n < 4; n++) 4582 { 4583 ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); 4584 ImGui::NextColumn(); 4585 } 4586 ImGui::Columns(1); 4587 } 4588 if (show_tab_bar && ImGui::BeginTabBar("Hello")) 4589 { 4590 if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); } 4591 if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); } 4592 if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); } 4593 if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); } 4594 ImGui::EndTabBar(); 4595 } 4596 if (show_child) 4597 { 4598 ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Border); 4599 ImGui::EndChild(); 4600 } 4601 ImGui::End(); 4602 } 4603 4604 ImGui::TreePop(); 4605 } 4606 4607 IMGUI_DEMO_MARKER("Layout/Text Clipping"); 4608 if (ImGui::TreeNode("Text Clipping")) 4609 { 4610 static ImVec2 size(100.0f, 100.0f); 4611 static ImVec2 offset(30.0f, 30.0f); 4612 ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f"); 4613 ImGui::TextWrapped("(Click and drag to scroll)"); 4614 4615 HelpMarker( 4616 "(Left) Using ImGui::PushClipRect():\n" 4617 "Will alter ImGui hit-testing logic + ImDrawList rendering.\n" 4618 "(use this if you want your clipping rectangle to affect interactions)\n\n" 4619 "(Center) Using ImDrawList::PushClipRect():\n" 4620 "Will alter ImDrawList rendering only.\n" 4621 "(use this as a shortcut if you are only using ImDrawList calls)\n\n" 4622 "(Right) Using ImDrawList::AddText() with a fine ClipRect:\n" 4623 "Will alter only this specific ImDrawList::AddText() rendering.\n" 4624 "This is often used internally to avoid altering the clipping rectangle and minimize draw calls."); 4625 4626 for (int n = 0; n < 3; n++) 4627 { 4628 if (n > 0) 4629 ImGui::SameLine(); 4630 4631 ImGui::PushID(n); 4632 ImGui::InvisibleButton("##canvas", size); 4633 if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) 4634 { 4635 offset.x += ImGui::GetIO().MouseDelta.x; 4636 offset.y += ImGui::GetIO().MouseDelta.y; 4637 } 4638 ImGui::PopID(); 4639 if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped. 4640 continue; 4641 4642 const ImVec2 p0 = ImGui::GetItemRectMin(); 4643 const ImVec2 p1 = ImGui::GetItemRectMax(); 4644 const char* text_str = "Line 1 hello\nLine 2 clip me!"; 4645 const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y); 4646 ImDrawList* draw_list = ImGui::GetWindowDrawList(); 4647 switch (n) 4648 { 4649 case 0: 4650 ImGui::PushClipRect(p0, p1, true); 4651 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); 4652 draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); 4653 ImGui::PopClipRect(); 4654 break; 4655 case 1: 4656 draw_list->PushClipRect(p0, p1, true); 4657 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); 4658 draw_list->AddText(text_pos, IM_COL32_WHITE, text_str); 4659 draw_list->PopClipRect(); 4660 break; 4661 case 2: 4662 ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert. 4663 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); 4664 draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect); 4665 break; 4666 } 4667 } 4668 4669 ImGui::TreePop(); 4670 } 4671 4672 IMGUI_DEMO_MARKER("Layout/Overlap Mode"); 4673 if (ImGui::TreeNode("Overlap Mode")) 4674 { 4675 static bool enable_allow_overlap = true; 4676 4677 HelpMarker( 4678 "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n" 4679 "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. " 4680 "Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state."); 4681 ImGui::Checkbox("Enable AllowOverlap", &enable_allow_overlap); 4682 4683 ImVec2 button1_pos = ImGui::GetCursorScreenPos(); 4684 ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f); 4685 if (enable_allow_overlap) 4686 ImGui::SetNextItemAllowOverlap(); 4687 ImGui::Button("Button 1", ImVec2(80, 80)); 4688 ImGui::SetCursorScreenPos(button2_pos); 4689 ImGui::Button("Button 2", ImVec2(80, 80)); 4690 4691 // This is typically used with width-spanning items. 4692 // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut 4693 // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.) 4694 if (enable_allow_overlap) 4695 ImGui::SetNextItemAllowOverlap(); 4696 ImGui::Selectable("Some Selectable", false); 4697 ImGui::SameLine(); 4698 ImGui::SmallButton("++"); 4699 4700 ImGui::TreePop(); 4701 } 4702 } 4703 4704 //----------------------------------------------------------------------------- 4705 // [SECTION] ShowDemoWindowPopups() 4706 //----------------------------------------------------------------------------- 4707 4708 static void ShowDemoWindowPopups() 4709 { 4710 IMGUI_DEMO_MARKER("Popups"); 4711 if (!ImGui::CollapsingHeader("Popups & Modal windows")) 4712 return; 4713 4714 // The properties of popups windows are: 4715 // - They block normal mouse hovering detection outside them. (*) 4716 // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE. 4717 // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as 4718 // we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup(). 4719 // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even 4720 // when normally blocked by a popup. 4721 // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close 4722 // popups at any time. 4723 4724 // Typical use for regular windows: 4725 // 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(); 4726 // Typical use for popups: 4727 // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); } 4728 4729 // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. 4730 // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. 4731 4732 IMGUI_DEMO_MARKER("Popups/Popups"); 4733 if (ImGui::TreeNode("Popups")) 4734 { 4735 ImGui::TextWrapped( 4736 "When a popup is active, it inhibits interacting with windows that are behind the popup. " 4737 "Clicking outside the popup closes it."); 4738 4739 static int selected_fish = -1; 4740 const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; 4741 static bool toggles[] = { true, false, false, false, false }; 4742 4743 // Simple selection popup (if you want to show the current selection inside the Button itself, 4744 // you may want to build a string using the "###" operator to preserve a constant ID with a variable label) 4745 if (ImGui::Button("Select..")) 4746 ImGui::OpenPopup("my_select_popup"); 4747 ImGui::SameLine(); 4748 ImGui::TextUnformatted(selected_fish == -1 ? "<None>" : names[selected_fish]); 4749 if (ImGui::BeginPopup("my_select_popup")) 4750 { 4751 ImGui::SeparatorText("Aquarium"); 4752 for (int i = 0; i < IM_ARRAYSIZE(names); i++) 4753 if (ImGui::Selectable(names[i])) 4754 selected_fish = i; 4755 ImGui::EndPopup(); 4756 } 4757 4758 // Showing a menu with toggles 4759 if (ImGui::Button("Toggle..")) 4760 ImGui::OpenPopup("my_toggle_popup"); 4761 if (ImGui::BeginPopup("my_toggle_popup")) 4762 { 4763 for (int i = 0; i < IM_ARRAYSIZE(names); i++) 4764 ImGui::MenuItem(names[i], "", &toggles[i]); 4765 if (ImGui::BeginMenu("Sub-menu")) 4766 { 4767 ImGui::MenuItem("Click me"); 4768 ImGui::EndMenu(); 4769 } 4770 4771 ImGui::Separator(); 4772 ImGui::Text("Tooltip here"); 4773 ImGui::SetItemTooltip("I am a tooltip over a popup"); 4774 4775 if (ImGui::Button("Stacked Popup")) 4776 ImGui::OpenPopup("another popup"); 4777 if (ImGui::BeginPopup("another popup")) 4778 { 4779 for (int i = 0; i < IM_ARRAYSIZE(names); i++) 4780 ImGui::MenuItem(names[i], "", &toggles[i]); 4781 if (ImGui::BeginMenu("Sub-menu")) 4782 { 4783 ImGui::MenuItem("Click me"); 4784 if (ImGui::Button("Stacked Popup")) 4785 ImGui::OpenPopup("another popup"); 4786 if (ImGui::BeginPopup("another popup")) 4787 { 4788 ImGui::Text("I am the last one here."); 4789 ImGui::EndPopup(); 4790 } 4791 ImGui::EndMenu(); 4792 } 4793 ImGui::EndPopup(); 4794 } 4795 ImGui::EndPopup(); 4796 } 4797 4798 // Call the more complete ShowExampleMenuFile which we use in various places of this demo 4799 if (ImGui::Button("With a menu..")) 4800 ImGui::OpenPopup("my_file_popup"); 4801 if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar)) 4802 { 4803 if (ImGui::BeginMenuBar()) 4804 { 4805 if (ImGui::BeginMenu("File")) 4806 { 4807 ShowExampleMenuFile(); 4808 ImGui::EndMenu(); 4809 } 4810 if (ImGui::BeginMenu("Edit")) 4811 { 4812 ImGui::MenuItem("Dummy"); 4813 ImGui::EndMenu(); 4814 } 4815 ImGui::EndMenuBar(); 4816 } 4817 ImGui::Text("Hello from popup!"); 4818 ImGui::Button("This is a dummy button.."); 4819 ImGui::EndPopup(); 4820 } 4821 4822 ImGui::TreePop(); 4823 } 4824 4825 IMGUI_DEMO_MARKER("Popups/Context menus"); 4826 if (ImGui::TreeNode("Context menus")) 4827 { 4828 HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier."); 4829 4830 // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: 4831 // if (id == 0) 4832 // id = GetItemID(); // Use last item id 4833 // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) 4834 // OpenPopup(id); 4835 // return BeginPopup(id); 4836 // For advanced uses you may want to replicate and customize this code. 4837 // See more details in BeginPopupContextItem(). 4838 4839 // Example 1 4840 // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(), 4841 // and BeginPopupContextItem() will use the last item ID as the popup ID. 4842 { 4843 const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" }; 4844 static int selected = -1; 4845 for (int n = 0; n < 5; n++) 4846 { 4847 if (ImGui::Selectable(names[n], selected == n)) 4848 selected = n; 4849 if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id 4850 { 4851 selected = n; 4852 ImGui::Text("This a popup for \"%s\"!", names[n]); 4853 if (ImGui::Button("Close")) 4854 ImGui::CloseCurrentPopup(); 4855 ImGui::EndPopup(); 4856 } 4857 ImGui::SetItemTooltip("Right-click to open popup"); 4858 } 4859 } 4860 4861 // Example 2 4862 // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem(). 4863 // Using an explicit identifier is also convenient if you want to activate the popups from different locations. 4864 { 4865 HelpMarker("Text() elements don't have stable identifiers so we need to provide one."); 4866 static float value = 0.5f; 4867 ImGui::Text("Value = %.3f <-- (1) right-click this text", value); 4868 if (ImGui::BeginPopupContextItem("my popup")) 4869 { 4870 if (ImGui::Selectable("Set to zero")) value = 0.0f; 4871 if (ImGui::Selectable("Set to PI")) value = 3.1415f; 4872 ImGui::SetNextItemWidth(-FLT_MIN); 4873 ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); 4874 ImGui::EndPopup(); 4875 } 4876 4877 // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup. 4878 // Here we make it that right-clicking this other text element opens the same popup as above. 4879 // The popup itself will be submitted by the code above. 4880 ImGui::Text("(2) Or right-click this text"); 4881 ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight); 4882 4883 // Back to square one: manually open the same popup. 4884 if (ImGui::Button("(3) Or click this button")) 4885 ImGui::OpenPopup("my popup"); 4886 } 4887 4888 // Example 3 4889 // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID), 4890 // we need to make sure your item identifier is stable. 4891 // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ). 4892 { 4893 HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator."); 4894 static char name[32] = "Label1"; 4895 char buf[64]; 4896 sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label 4897 ImGui::Button(buf); 4898 if (ImGui::BeginPopupContextItem()) 4899 { 4900 ImGui::Text("Edit name:"); 4901 ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); 4902 if (ImGui::Button("Close")) 4903 ImGui::CloseCurrentPopup(); 4904 ImGui::EndPopup(); 4905 } 4906 ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); 4907 } 4908 4909 ImGui::TreePop(); 4910 } 4911 4912 IMGUI_DEMO_MARKER("Popups/Modals"); 4913 if (ImGui::TreeNode("Modals")) 4914 { 4915 ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside."); 4916 4917 if (ImGui::Button("Delete..")) 4918 ImGui::OpenPopup("Delete?"); 4919 4920 // Always center this window when appearing 4921 ImVec2 center = ImGui::GetMainViewport()->GetCenter(); 4922 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); 4923 4924 if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) 4925 { 4926 ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!"); 4927 ImGui::Separator(); 4928 4929 //static int unused_i = 0; 4930 //ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0"); 4931 4932 static bool dont_ask_me_next_time = false; 4933 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); 4934 ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); 4935 ImGui::PopStyleVar(); 4936 4937 if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } 4938 ImGui::SetItemDefaultFocus(); 4939 ImGui::SameLine(); 4940 if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } 4941 ImGui::EndPopup(); 4942 } 4943 4944 if (ImGui::Button("Stacked modals..")) 4945 ImGui::OpenPopup("Stacked 1"); 4946 if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar)) 4947 { 4948 if (ImGui::BeginMenuBar()) 4949 { 4950 if (ImGui::BeginMenu("File")) 4951 { 4952 if (ImGui::MenuItem("Some menu item")) {} 4953 ImGui::EndMenu(); 4954 } 4955 ImGui::EndMenuBar(); 4956 } 4957 ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it."); 4958 4959 // Testing behavior of widgets stacking their own regular popups over the modal. 4960 static int item = 1; 4961 static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; 4962 ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); 4963 ImGui::ColorEdit4("Color", color); 4964 4965 if (ImGui::Button("Add another modal..")) 4966 ImGui::OpenPopup("Stacked 2"); 4967 4968 // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which 4969 // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value 4970 // of the bool actually doesn't matter here. 4971 bool unused_open = true; 4972 if (ImGui::BeginPopupModal("Stacked 2", &unused_open)) 4973 { 4974 ImGui::Text("Hello from Stacked The Second!"); 4975 ImGui::ColorEdit4("Color", color); // Allow opening another nested popup 4976 if (ImGui::Button("Close")) 4977 ImGui::CloseCurrentPopup(); 4978 ImGui::EndPopup(); 4979 } 4980 4981 if (ImGui::Button("Close")) 4982 ImGui::CloseCurrentPopup(); 4983 ImGui::EndPopup(); 4984 } 4985 4986 ImGui::TreePop(); 4987 } 4988 4989 IMGUI_DEMO_MARKER("Popups/Menus inside a regular window"); 4990 if (ImGui::TreeNode("Menus inside a regular window")) 4991 { 4992 ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); 4993 ImGui::Separator(); 4994 4995 ImGui::MenuItem("Menu item", "CTRL+M"); 4996 if (ImGui::BeginMenu("Menu inside a regular window")) 4997 { 4998 ShowExampleMenuFile(); 4999 ImGui::EndMenu(); 5000 } 5001 ImGui::Separator(); 5002 ImGui::TreePop(); 5003 } 5004 } 5005 5006 // Dummy data structure that we use for the Table demo. 5007 // (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function) 5008 namespace 5009 { 5010 // We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code. 5011 // This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID. 5012 // But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex) 5013 // If you don't use sorting, you will generally never care about giving column an ID! 5014 enum MyItemColumnID 5015 { 5016 MyItemColumnID_ID, 5017 MyItemColumnID_Name, 5018 MyItemColumnID_Action, 5019 MyItemColumnID_Quantity, 5020 MyItemColumnID_Description 5021 }; 5022 5023 struct MyItem 5024 { 5025 int ID; 5026 const char* Name; 5027 int Quantity; 5028 5029 // We have a problem which is affecting _only this demo_ and should not affect your code: 5030 // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(), 5031 // however qsort doesn't allow passing user data to comparing function. 5032 // As a workaround, we are storing the sort specs in a static/global for the comparing function to access. 5033 // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global. 5034 // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called 5035 // very often by the sorting algorithm it would be a little wasteful. 5036 static const ImGuiTableSortSpecs* s_current_sort_specs; 5037 5038 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count) 5039 { 5040 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function. 5041 if (items_count > 1) 5042 qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs); 5043 s_current_sort_specs = NULL; 5044 } 5045 5046 // Compare function to be used by qsort() 5047 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) 5048 { 5049 const MyItem* a = (const MyItem*)lhs; 5050 const MyItem* b = (const MyItem*)rhs; 5051 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) 5052 { 5053 // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn() 5054 // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler! 5055 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n]; 5056 int delta = 0; 5057 switch (sort_spec->ColumnUserID) 5058 { 5059 case MyItemColumnID_ID: delta = (a->ID - b->ID); break; 5060 case MyItemColumnID_Name: delta = (strcmp(a->Name, b->Name)); break; 5061 case MyItemColumnID_Quantity: delta = (a->Quantity - b->Quantity); break; 5062 case MyItemColumnID_Description: delta = (strcmp(a->Name, b->Name)); break; 5063 default: IM_ASSERT(0); break; 5064 } 5065 if (delta > 0) 5066 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1; 5067 if (delta < 0) 5068 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; 5069 } 5070 5071 // qsort() is instable so always return a way to differenciate items. 5072 // Your own compare function may want to avoid fallback on implicit sort specs. 5073 // e.g. a Name compare if it wasn't already part of the sort specs. 5074 return (a->ID - b->ID); 5075 } 5076 }; 5077 const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL; 5078 } 5079 5080 // Make the UI compact because there are so many fields 5081 static void PushStyleCompact() 5082 { 5083 ImGuiStyle& style = ImGui::GetStyle(); 5084 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f))); 5085 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f))); 5086 } 5087 5088 static void PopStyleCompact() 5089 { 5090 ImGui::PopStyleVar(2); 5091 } 5092 5093 // Show a combo box with a choice of sizing policies 5094 static void EditTableSizingFlags(ImGuiTableFlags* p_flags) 5095 { 5096 struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; }; 5097 static const EnumDesc policies[] = 5098 { 5099 { ImGuiTableFlags_None, "Default", "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." }, 5100 { ImGuiTableFlags_SizingFixedFit, "ImGuiTableFlags_SizingFixedFit", "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." }, 5101 { ImGuiTableFlags_SizingFixedSame, "ImGuiTableFlags_SizingFixedSame", "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." }, 5102 { ImGuiTableFlags_SizingStretchProp, "ImGuiTableFlags_SizingStretchProp", "Columns default to _WidthStretch with weights proportional to their widths." }, 5103 { ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." } 5104 }; 5105 int idx; 5106 for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++) 5107 if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_)) 5108 break; 5109 const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; 5110 if (ImGui::BeginCombo("Sizing Policy", preview_text)) 5111 { 5112 for (int n = 0; n < IM_ARRAYSIZE(policies); n++) 5113 if (ImGui::Selectable(policies[n].Name, idx == n)) 5114 *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value; 5115 ImGui::EndCombo(); 5116 } 5117 ImGui::SameLine(); 5118 ImGui::TextDisabled("(?)"); 5119 if (ImGui::BeginItemTooltip()) 5120 { 5121 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); 5122 for (int m = 0; m < IM_ARRAYSIZE(policies); m++) 5123 { 5124 ImGui::Separator(); 5125 ImGui::Text("%s:", policies[m].Name); 5126 ImGui::Separator(); 5127 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f); 5128 ImGui::TextUnformatted(policies[m].Tooltip); 5129 } 5130 ImGui::PopTextWrapPos(); 5131 ImGui::EndTooltip(); 5132 } 5133 } 5134 5135 static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) 5136 { 5137 ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)"); 5138 ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide); 5139 ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort); 5140 if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch)) 5141 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch); 5142 if (ImGui::CheckboxFlags("_WidthFixed", p_flags, ImGuiTableColumnFlags_WidthFixed)) 5143 *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed); 5144 ImGui::CheckboxFlags("_NoResize", p_flags, ImGuiTableColumnFlags_NoResize); 5145 ImGui::CheckboxFlags("_NoReorder", p_flags, ImGuiTableColumnFlags_NoReorder); 5146 ImGui::CheckboxFlags("_NoHide", p_flags, ImGuiTableColumnFlags_NoHide); 5147 ImGui::CheckboxFlags("_NoClip", p_flags, ImGuiTableColumnFlags_NoClip); 5148 ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort); 5149 ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending); 5150 ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending); 5151 ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel); 5152 ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth); 5153 ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending); 5154 ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); 5155 ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0"); 5156 ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0"); 5157 ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader); 5158 } 5159 5160 static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) 5161 { 5162 ImGui::CheckboxFlags("_IsEnabled", &flags, ImGuiTableColumnFlags_IsEnabled); 5163 ImGui::CheckboxFlags("_IsVisible", &flags, ImGuiTableColumnFlags_IsVisible); 5164 ImGui::CheckboxFlags("_IsSorted", &flags, ImGuiTableColumnFlags_IsSorted); 5165 ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered); 5166 } 5167 5168 //----------------------------------------------------------------------------- 5169 // [SECTION] ShowDemoWindowTables() 5170 //----------------------------------------------------------------------------- 5171 5172 static void ShowDemoWindowTables() 5173 { 5174 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); 5175 IMGUI_DEMO_MARKER("Tables"); 5176 if (!ImGui::CollapsingHeader("Tables & Columns")) 5177 return; 5178 5179 // Using those as a base value to create width/height that are factor of the size of our font 5180 const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; 5181 const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); 5182 5183 ImGui::PushID("Tables"); 5184 5185 int open_action = -1; 5186 if (ImGui::Button("Expand all")) 5187 open_action = 1; 5188 ImGui::SameLine(); 5189 if (ImGui::Button("Collapse all")) 5190 open_action = 0; 5191 ImGui::SameLine(); 5192 5193 // Options 5194 static bool disable_indent = false; 5195 ImGui::Checkbox("Disable tree indentation", &disable_indent); 5196 ImGui::SameLine(); 5197 HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width."); 5198 ImGui::Separator(); 5199 if (disable_indent) 5200 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f); 5201 5202 // About Styling of tables 5203 // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs. 5204 // There are however a few settings that a shared and part of the ImGuiStyle structure: 5205 // style.CellPadding // Padding within each cell 5206 // style.Colors[ImGuiCol_TableHeaderBg] // Table header background 5207 // style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders 5208 // style.Colors[ImGuiCol_TableBorderLight] // Table inner borders 5209 // style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even rows) 5210 // style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows) 5211 5212 // Demos 5213 if (open_action != -1) 5214 ImGui::SetNextItemOpen(open_action != 0); 5215 IMGUI_DEMO_MARKER("Tables/Basic"); 5216 if (ImGui::TreeNode("Basic")) 5217 { 5218 // Here we will showcase three different ways to output a table. 5219 // They are very simple variations of a same thing! 5220 5221 // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column. 5222 // In many situations, this is the most flexible and easy to use pattern. 5223 HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop."); 5224 if (ImGui::BeginTable("table1", 3)) 5225 { 5226 for (int row = 0; row < 4; row++) 5227 { 5228 ImGui::TableNextRow(); 5229 for (int column = 0; column < 3; column++) 5230 { 5231 ImGui::TableSetColumnIndex(column); 5232 ImGui::Text("Row %d Column %d", row, column); 5233 } 5234 } 5235 ImGui::EndTable(); 5236 } 5237 5238 // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex(). 5239 // This is generally more convenient when you have code manually submitting the contents of each column. 5240 HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually."); 5241 if (ImGui::BeginTable("table2", 3)) 5242 { 5243 for (int row = 0; row < 4; row++) 5244 { 5245 ImGui::TableNextRow(); 5246 ImGui::TableNextColumn(); 5247 ImGui::Text("Row %d", row); 5248 ImGui::TableNextColumn(); 5249 ImGui::Text("Some contents"); 5250 ImGui::TableNextColumn(); 5251 ImGui::Text("123.456"); 5252 } 5253 ImGui::EndTable(); 5254 } 5255 5256 // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(), 5257 // as TableNextColumn() will automatically wrap around and create new rows as needed. 5258 // This is generally more convenient when your cells all contains the same type of data. 5259 HelpMarker( 5260 "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains " 5261 "the same type of contents.\n This is also more similar to the old NextColumn() function of the " 5262 "Columns API, and provided to facilitate the Columns->Tables API transition."); 5263 if (ImGui::BeginTable("table3", 3)) 5264 { 5265 for (int item = 0; item < 14; item++) 5266 { 5267 ImGui::TableNextColumn(); 5268 ImGui::Text("Item %d", item); 5269 } 5270 ImGui::EndTable(); 5271 } 5272 5273 ImGui::TreePop(); 5274 } 5275 5276 if (open_action != -1) 5277 ImGui::SetNextItemOpen(open_action != 0); 5278 IMGUI_DEMO_MARKER("Tables/Borders, background"); 5279 if (ImGui::TreeNode("Borders, background")) 5280 { 5281 // Expose a few Borders related flags interactively 5282 enum ContentsType { CT_Text, CT_FillButton }; 5283 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; 5284 static bool display_headers = false; 5285 static int contents_type = CT_Text; 5286 5287 PushStyleCompact(); 5288 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); 5289 ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders); 5290 ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH"); 5291 ImGui::Indent(); 5292 5293 ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); 5294 ImGui::Indent(); 5295 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); 5296 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); 5297 ImGui::Unindent(); 5298 5299 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); 5300 ImGui::Indent(); 5301 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); 5302 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); 5303 ImGui::Unindent(); 5304 5305 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter); 5306 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner); 5307 ImGui::Unindent(); 5308 5309 ImGui::AlignTextToFramePadding(); ImGui::Text("Cell contents:"); 5310 ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text); 5311 ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton); 5312 ImGui::Checkbox("Display headers", &display_headers); 5313 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers"); 5314 PopStyleCompact(); 5315 5316 if (ImGui::BeginTable("table1", 3, flags)) 5317 { 5318 // Display headers so we can inspect their interaction with borders 5319 // (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) 5320 if (display_headers) 5321 { 5322 ImGui::TableSetupColumn("One"); 5323 ImGui::TableSetupColumn("Two"); 5324 ImGui::TableSetupColumn("Three"); 5325 ImGui::TableHeadersRow(); 5326 } 5327 5328 for (int row = 0; row < 5; row++) 5329 { 5330 ImGui::TableNextRow(); 5331 for (int column = 0; column < 3; column++) 5332 { 5333 ImGui::TableSetColumnIndex(column); 5334 char buf[32]; 5335 sprintf(buf, "Hello %d,%d", column, row); 5336 if (contents_type == CT_Text) 5337 ImGui::TextUnformatted(buf); 5338 else if (contents_type == CT_FillButton) 5339 ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); 5340 } 5341 } 5342 ImGui::EndTable(); 5343 } 5344 ImGui::TreePop(); 5345 } 5346 5347 if (open_action != -1) 5348 ImGui::SetNextItemOpen(open_action != 0); 5349 IMGUI_DEMO_MARKER("Tables/Resizable, stretch"); 5350 if (ImGui::TreeNode("Resizable, stretch")) 5351 { 5352 // By default, if we don't enable ScrollX the sizing policy for each column is "Stretch" 5353 // All columns maintain a sizing weight, and they will occupy all available width. 5354 static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; 5355 PushStyleCompact(); 5356 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); 5357 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); 5358 ImGui::SameLine(); HelpMarker( 5359 "Using the _Resizable flag automatically enables the _BordersInnerV flag as well, " 5360 "this is why the resize borders are still showing when unchecking this."); 5361 PopStyleCompact(); 5362 5363 if (ImGui::BeginTable("table1", 3, flags)) 5364 { 5365 for (int row = 0; row < 5; row++) 5366 { 5367 ImGui::TableNextRow(); 5368 for (int column = 0; column < 3; column++) 5369 { 5370 ImGui::TableSetColumnIndex(column); 5371 ImGui::Text("Hello %d,%d", column, row); 5372 } 5373 } 5374 ImGui::EndTable(); 5375 } 5376 ImGui::TreePop(); 5377 } 5378 5379 if (open_action != -1) 5380 ImGui::SetNextItemOpen(open_action != 0); 5381 IMGUI_DEMO_MARKER("Tables/Resizable, fixed"); 5382 if (ImGui::TreeNode("Resizable, fixed")) 5383 { 5384 // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set) 5385 // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small) 5386 // If there is not enough available width to fit all columns, they will however be resized down. 5387 // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings 5388 HelpMarker( 5389 "Using _Resizable + _SizingFixedFit flags.\n" 5390 "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n" 5391 "Double-click a column border to auto-fit the column to its contents."); 5392 PushStyleCompact(); 5393 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; 5394 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); 5395 PopStyleCompact(); 5396 5397 if (ImGui::BeginTable("table1", 3, flags)) 5398 { 5399 for (int row = 0; row < 5; row++) 5400 { 5401 ImGui::TableNextRow(); 5402 for (int column = 0; column < 3; column++) 5403 { 5404 ImGui::TableSetColumnIndex(column); 5405 ImGui::Text("Hello %d,%d", column, row); 5406 } 5407 } 5408 ImGui::EndTable(); 5409 } 5410 ImGui::TreePop(); 5411 } 5412 5413 if (open_action != -1) 5414 ImGui::SetNextItemOpen(open_action != 0); 5415 IMGUI_DEMO_MARKER("Tables/Resizable, mixed"); 5416 if (ImGui::TreeNode("Resizable, mixed")) 5417 { 5418 HelpMarker( 5419 "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n" 5420 "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch."); 5421 static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; 5422 5423 if (ImGui::BeginTable("table1", 3, flags)) 5424 { 5425 ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed); 5426 ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed); 5427 ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthStretch); 5428 ImGui::TableHeadersRow(); 5429 for (int row = 0; row < 5; row++) 5430 { 5431 ImGui::TableNextRow(); 5432 for (int column = 0; column < 3; column++) 5433 { 5434 ImGui::TableSetColumnIndex(column); 5435 ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row); 5436 } 5437 } 5438 ImGui::EndTable(); 5439 } 5440 if (ImGui::BeginTable("table2", 6, flags)) 5441 { 5442 ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed); 5443 ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed); 5444 ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide); 5445 ImGui::TableSetupColumn("DDD", ImGuiTableColumnFlags_WidthStretch); 5446 ImGui::TableSetupColumn("EEE", ImGuiTableColumnFlags_WidthStretch); 5447 ImGui::TableSetupColumn("FFF", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide); 5448 ImGui::TableHeadersRow(); 5449 for (int row = 0; row < 5; row++) 5450 { 5451 ImGui::TableNextRow(); 5452 for (int column = 0; column < 6; column++) 5453 { 5454 ImGui::TableSetColumnIndex(column); 5455 ImGui::Text("%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row); 5456 } 5457 } 5458 ImGui::EndTable(); 5459 } 5460 ImGui::TreePop(); 5461 } 5462 5463 if (open_action != -1) 5464 ImGui::SetNextItemOpen(open_action != 0); 5465 IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers"); 5466 if (ImGui::TreeNode("Reorderable, hideable, with headers")) 5467 { 5468 HelpMarker( 5469 "Click and drag column headers to reorder columns.\n\n" 5470 "Right-click on a header to open a context menu."); 5471 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; 5472 PushStyleCompact(); 5473 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); 5474 ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable); 5475 ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); 5476 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); 5477 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); 5478 ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); 5479 PopStyleCompact(); 5480 5481 if (ImGui::BeginTable("table1", 3, flags)) 5482 { 5483 // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column. 5484 // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.) 5485 ImGui::TableSetupColumn("One"); 5486 ImGui::TableSetupColumn("Two"); 5487 ImGui::TableSetupColumn("Three"); 5488 ImGui::TableHeadersRow(); 5489 for (int row = 0; row < 6; row++) 5490 { 5491 ImGui::TableNextRow(); 5492 for (int column = 0; column < 3; column++) 5493 { 5494 ImGui::TableSetColumnIndex(column); 5495 ImGui::Text("Hello %d,%d", column, row); 5496 } 5497 } 5498 ImGui::EndTable(); 5499 } 5500 5501 // Use outer_size.x == 0.0f instead of default to make the table as tight as possible 5502 // (only valid when no scrolling and no stretch column) 5503 if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f))) 5504 { 5505 ImGui::TableSetupColumn("One"); 5506 ImGui::TableSetupColumn("Two"); 5507 ImGui::TableSetupColumn("Three"); 5508 ImGui::TableHeadersRow(); 5509 for (int row = 0; row < 6; row++) 5510 { 5511 ImGui::TableNextRow(); 5512 for (int column = 0; column < 3; column++) 5513 { 5514 ImGui::TableSetColumnIndex(column); 5515 ImGui::Text("Fixed %d,%d", column, row); 5516 } 5517 } 5518 ImGui::EndTable(); 5519 } 5520 ImGui::TreePop(); 5521 } 5522 5523 if (open_action != -1) 5524 ImGui::SetNextItemOpen(open_action != 0); 5525 IMGUI_DEMO_MARKER("Tables/Padding"); 5526 if (ImGui::TreeNode("Padding")) 5527 { 5528 // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding. 5529 // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding. 5530 HelpMarker( 5531 "We often want outer padding activated when any using features which makes the edges of a column visible:\n" 5532 "e.g.:\n" 5533 "- BorderOuterV\n" 5534 "- any form of row selection\n" 5535 "Because of this, activating BorderOuterV sets the default to PadOuterX. " 5536 "Using PadOuterX or NoPadOuterX you can override the default.\n\n" 5537 "Actual padding values are using style.CellPadding.\n\n" 5538 "In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding."); 5539 5540 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV; 5541 PushStyleCompact(); 5542 ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX); 5543 ImGui::SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)"); 5544 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX); 5545 ImGui::SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)"); 5546 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX); 5547 ImGui::SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)"); 5548 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV); 5549 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV); 5550 static bool show_headers = false; 5551 ImGui::Checkbox("show_headers", &show_headers); 5552 PopStyleCompact(); 5553 5554 if (ImGui::BeginTable("table_padding", 3, flags1)) 5555 { 5556 if (show_headers) 5557 { 5558 ImGui::TableSetupColumn("One"); 5559 ImGui::TableSetupColumn("Two"); 5560 ImGui::TableSetupColumn("Three"); 5561 ImGui::TableHeadersRow(); 5562 } 5563 5564 for (int row = 0; row < 5; row++) 5565 { 5566 ImGui::TableNextRow(); 5567 for (int column = 0; column < 3; column++) 5568 { 5569 ImGui::TableSetColumnIndex(column); 5570 if (row == 0) 5571 { 5572 ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); 5573 } 5574 else 5575 { 5576 char buf[32]; 5577 sprintf(buf, "Hello %d,%d", column, row); 5578 ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f)); 5579 } 5580 //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) 5581 // ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255)); 5582 } 5583 } 5584 ImGui::EndTable(); 5585 } 5586 5587 // Second example: set style.CellPadding to (0.0) or a custom value. 5588 // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one... 5589 HelpMarker("Setting style.CellPadding to (0,0) or a custom value."); 5590 static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; 5591 static ImVec2 cell_padding(0.0f, 0.0f); 5592 static bool show_widget_frame_bg = true; 5593 5594 PushStyleCompact(); 5595 ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders); 5596 ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH); 5597 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV); 5598 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner); 5599 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter); 5600 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg); 5601 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable); 5602 ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg); 5603 ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f"); 5604 PopStyleCompact(); 5605 5606 ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding); 5607 if (ImGui::BeginTable("table_padding_2", 3, flags2)) 5608 { 5609 static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells 5610 static bool init = true; 5611 if (!show_widget_frame_bg) 5612 ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); 5613 for (int cell = 0; cell < 3 * 5; cell++) 5614 { 5615 ImGui::TableNextColumn(); 5616 if (init) 5617 strcpy(text_bufs[cell], "edit me"); 5618 ImGui::SetNextItemWidth(-FLT_MIN); 5619 ImGui::PushID(cell); 5620 ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell])); 5621 ImGui::PopID(); 5622 } 5623 if (!show_widget_frame_bg) 5624 ImGui::PopStyleColor(); 5625 init = false; 5626 ImGui::EndTable(); 5627 } 5628 ImGui::PopStyleVar(); 5629 5630 ImGui::TreePop(); 5631 } 5632 5633 if (open_action != -1) 5634 ImGui::SetNextItemOpen(open_action != 0); 5635 IMGUI_DEMO_MARKER("Tables/Explicit widths"); 5636 if (ImGui::TreeNode("Sizing policies")) 5637 { 5638 static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; 5639 PushStyleCompact(); 5640 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable); 5641 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags1, ImGuiTableFlags_NoHostExtendX); 5642 PopStyleCompact(); 5643 5644 static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; 5645 for (int table_n = 0; table_n < 4; table_n++) 5646 { 5647 ImGui::PushID(table_n); 5648 ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30); 5649 EditTableSizingFlags(&sizing_policy_flags[table_n]); 5650 5651 // To make it easier to understand the different sizing policy, 5652 // For each policy: we display one table where the columns have equal contents width, 5653 // and one where the columns have different contents width. 5654 if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1)) 5655 { 5656 for (int row = 0; row < 3; row++) 5657 { 5658 ImGui::TableNextRow(); 5659 ImGui::TableNextColumn(); ImGui::Text("Oh dear"); 5660 ImGui::TableNextColumn(); ImGui::Text("Oh dear"); 5661 ImGui::TableNextColumn(); ImGui::Text("Oh dear"); 5662 } 5663 ImGui::EndTable(); 5664 } 5665 if (ImGui::BeginTable("table2", 3, sizing_policy_flags[table_n] | flags1)) 5666 { 5667 for (int row = 0; row < 3; row++) 5668 { 5669 ImGui::TableNextRow(); 5670 ImGui::TableNextColumn(); ImGui::Text("AAAA"); 5671 ImGui::TableNextColumn(); ImGui::Text("BBBBBBBB"); 5672 ImGui::TableNextColumn(); ImGui::Text("CCCCCCCCCCCC"); 5673 } 5674 ImGui::EndTable(); 5675 } 5676 ImGui::PopID(); 5677 } 5678 5679 ImGui::Spacing(); 5680 ImGui::TextUnformatted("Advanced"); 5681 ImGui::SameLine(); 5682 HelpMarker( 5683 "This section allows you to interact and see the effect of various sizing policies " 5684 "depending on whether Scroll is enabled and the contents of your columns."); 5685 5686 enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText }; 5687 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable; 5688 static int contents_type = CT_ShowWidth; 5689 static int column_count = 3; 5690 5691 PushStyleCompact(); 5692 ImGui::PushID("Advanced"); 5693 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); 5694 EditTableSizingFlags(&flags); 5695 ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0"); 5696 if (contents_type == CT_FillButton) 5697 { 5698 ImGui::SameLine(); 5699 HelpMarker( 5700 "Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop " 5701 "where contents width can feed into auto-column width can feed into contents width."); 5702 } 5703 ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp); 5704 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); 5705 ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); 5706 ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth."); 5707 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); 5708 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); 5709 ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); 5710 ImGui::PopItemWidth(); 5711 ImGui::PopID(); 5712 PopStyleCompact(); 5713 5714 if (ImGui::BeginTable("table2", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7))) 5715 { 5716 for (int cell = 0; cell < 10 * column_count; cell++) 5717 { 5718 ImGui::TableNextColumn(); 5719 int column = ImGui::TableGetColumnIndex(); 5720 int row = ImGui::TableGetRowIndex(); 5721 5722 ImGui::PushID(cell); 5723 char label[32]; 5724 static char text_buf[32] = ""; 5725 sprintf(label, "Hello %d,%d", column, row); 5726 switch (contents_type) 5727 { 5728 case CT_ShortText: ImGui::TextUnformatted(label); break; 5729 case CT_LongText: ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break; 5730 case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break; 5731 case CT_Button: ImGui::Button(label); break; 5732 case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; 5733 case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; 5734 } 5735 ImGui::PopID(); 5736 } 5737 ImGui::EndTable(); 5738 } 5739 ImGui::TreePop(); 5740 } 5741 5742 if (open_action != -1) 5743 ImGui::SetNextItemOpen(open_action != 0); 5744 IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping"); 5745 if (ImGui::TreeNode("Vertical scrolling, with clipping")) 5746 { 5747 HelpMarker( 5748 "Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n" 5749 "We also demonstrate using ImGuiListClipper to virtualize the submission of many items."); 5750 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; 5751 5752 PushStyleCompact(); 5753 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); 5754 PopStyleCompact(); 5755 5756 // When using ScrollX or ScrollY we need to specify a size for our table container! 5757 // Otherwise by default the table will fit all available space, like a BeginChild() call. 5758 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8); 5759 if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size)) 5760 { 5761 ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible 5762 ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None); 5763 ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None); 5764 ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None); 5765 ImGui::TableHeadersRow(); 5766 5767 // Demonstrate using clipper for large vertical lists 5768 ImGuiListClipper clipper; 5769 clipper.Begin(1000); 5770 while (clipper.Step()) 5771 { 5772 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) 5773 { 5774 ImGui::TableNextRow(); 5775 for (int column = 0; column < 3; column++) 5776 { 5777 ImGui::TableSetColumnIndex(column); 5778 ImGui::Text("Hello %d,%d", column, row); 5779 } 5780 } 5781 } 5782 ImGui::EndTable(); 5783 } 5784 ImGui::TreePop(); 5785 } 5786 5787 if (open_action != -1) 5788 ImGui::SetNextItemOpen(open_action != 0); 5789 IMGUI_DEMO_MARKER("Tables/Horizontal scrolling"); 5790 if (ImGui::TreeNode("Horizontal scrolling")) 5791 { 5792 HelpMarker( 5793 "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, " 5794 "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n" 5795 "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, " 5796 "because the container window won't automatically extend vertically to fix contents " 5797 "(this may be improved in future versions)."); 5798 static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; 5799 static int freeze_cols = 1; 5800 static int freeze_rows = 1; 5801 5802 PushStyleCompact(); 5803 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); 5804 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); 5805 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); 5806 ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); 5807 ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); 5808 ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); 5809 ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); 5810 PopStyleCompact(); 5811 5812 // When using ScrollX or ScrollY we need to specify a size for our table container! 5813 // Otherwise by default the table will fit all available space, like a BeginChild() call. 5814 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8); 5815 if (ImGui::BeginTable("table_scrollx", 7, flags, outer_size)) 5816 { 5817 ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); 5818 ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze() 5819 ImGui::TableSetupColumn("One"); 5820 ImGui::TableSetupColumn("Two"); 5821 ImGui::TableSetupColumn("Three"); 5822 ImGui::TableSetupColumn("Four"); 5823 ImGui::TableSetupColumn("Five"); 5824 ImGui::TableSetupColumn("Six"); 5825 ImGui::TableHeadersRow(); 5826 for (int row = 0; row < 20; row++) 5827 { 5828 ImGui::TableNextRow(); 5829 for (int column = 0; column < 7; column++) 5830 { 5831 // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement. 5832 // Because here we know that: 5833 // - A) all our columns are contributing the same to row height 5834 // - B) column 0 is always visible, 5835 // We only always submit this one column and can skip others. 5836 // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags(). 5837 if (!ImGui::TableSetColumnIndex(column) && column > 0) 5838 continue; 5839 if (column == 0) 5840 ImGui::Text("Line %d", row); 5841 else 5842 ImGui::Text("Hello world %d,%d", column, row); 5843 } 5844 } 5845 ImGui::EndTable(); 5846 } 5847 5848 ImGui::Spacing(); 5849 ImGui::TextUnformatted("Stretch + ScrollX"); 5850 ImGui::SameLine(); 5851 HelpMarker( 5852 "Showcase using Stretch columns + ScrollX together: " 5853 "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" 5854 "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns " 5855 "along with ScrollX doesn't make sense."); 5856 static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; 5857 static float inner_width = 1000.0f; 5858 PushStyleCompact(); 5859 ImGui::PushID("flags3"); 5860 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); 5861 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX); 5862 ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f"); 5863 ImGui::PopItemWidth(); 5864 ImGui::PopID(); 5865 PopStyleCompact(); 5866 if (ImGui::BeginTable("table2", 7, flags2, outer_size, inner_width)) 5867 { 5868 for (int cell = 0; cell < 20 * 7; cell++) 5869 { 5870 ImGui::TableNextColumn(); 5871 ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex()); 5872 } 5873 ImGui::EndTable(); 5874 } 5875 ImGui::TreePop(); 5876 } 5877 5878 if (open_action != -1) 5879 ImGui::SetNextItemOpen(open_action != 0); 5880 IMGUI_DEMO_MARKER("Tables/Columns flags"); 5881 if (ImGui::TreeNode("Columns flags")) 5882 { 5883 // Create a first table just to show all the options/flags we want to make visible in our example! 5884 const int column_count = 3; 5885 const char* column_names[column_count] = { "One", "Two", "Three" }; 5886 static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide }; 5887 static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags() 5888 5889 if (ImGui::BeginTable("table_columns_flags_checkboxes", column_count, ImGuiTableFlags_None)) 5890 { 5891 PushStyleCompact(); 5892 for (int column = 0; column < column_count; column++) 5893 { 5894 ImGui::TableNextColumn(); 5895 ImGui::PushID(column); 5896 ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns 5897 ImGui::Text("'%s'", column_names[column]); 5898 ImGui::Spacing(); 5899 ImGui::Text("Input flags:"); 5900 EditTableColumnsFlags(&column_flags[column]); 5901 ImGui::Spacing(); 5902 ImGui::Text("Output flags:"); 5903 ImGui::BeginDisabled(); 5904 ShowTableColumnsStatusFlags(column_flags_out[column]); 5905 ImGui::EndDisabled(); 5906 ImGui::PopID(); 5907 } 5908 PopStyleCompact(); 5909 ImGui::EndTable(); 5910 } 5911 5912 // Create the real table we care about for the example! 5913 // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above, 5914 // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible 5915 // + resizing the parent window down). 5916 const ImGuiTableFlags flags 5917 = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY 5918 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV 5919 | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable; 5920 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9); 5921 if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size)) 5922 { 5923 bool has_angled_header = false; 5924 for (int column = 0; column < column_count; column++) 5925 { 5926 has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0; 5927 ImGui::TableSetupColumn(column_names[column], column_flags[column]); 5928 } 5929 if (has_angled_header) 5930 ImGui::TableAngledHeadersRow(); 5931 ImGui::TableHeadersRow(); 5932 for (int column = 0; column < column_count; column++) 5933 column_flags_out[column] = ImGui::TableGetColumnFlags(column); 5934 float indent_step = (float)((int)TEXT_BASE_WIDTH / 2); 5935 for (int row = 0; row < 8; row++) 5936 { 5937 // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags. 5938 ImGui::Indent(indent_step); 5939 ImGui::TableNextRow(); 5940 for (int column = 0; column < column_count; column++) 5941 { 5942 ImGui::TableSetColumnIndex(column); 5943 ImGui::Text("%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column)); 5944 } 5945 } 5946 ImGui::Unindent(indent_step * 8.0f); 5947 5948 ImGui::EndTable(); 5949 } 5950 ImGui::TreePop(); 5951 } 5952 5953 if (open_action != -1) 5954 ImGui::SetNextItemOpen(open_action != 0); 5955 IMGUI_DEMO_MARKER("Tables/Columns widths"); 5956 if (ImGui::TreeNode("Columns widths")) 5957 { 5958 HelpMarker("Using TableSetupColumn() to setup default width."); 5959 5960 static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize; 5961 PushStyleCompact(); 5962 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable); 5963 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize); 5964 PopStyleCompact(); 5965 if (ImGui::BeginTable("table1", 3, flags1)) 5966 { 5967 // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed. 5968 ImGui::TableSetupColumn("one", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f 5969 ImGui::TableSetupColumn("two", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f 5970 ImGui::TableSetupColumn("three", ImGuiTableColumnFlags_WidthFixed); // Default to auto 5971 ImGui::TableHeadersRow(); 5972 for (int row = 0; row < 4; row++) 5973 { 5974 ImGui::TableNextRow(); 5975 for (int column = 0; column < 3; column++) 5976 { 5977 ImGui::TableSetColumnIndex(column); 5978 if (row == 0) 5979 ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x); 5980 else 5981 ImGui::Text("Hello %d,%d", column, row); 5982 } 5983 } 5984 ImGui::EndTable(); 5985 } 5986 5987 HelpMarker( 5988 "Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, " 5989 "fixed columns with set width may still be shrunk down if there's not enough space in the host."); 5990 5991 static ImGuiTableFlags flags2 = ImGuiTableFlags_None; 5992 PushStyleCompact(); 5993 ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags2, ImGuiTableFlags_NoKeepColumnsVisible); 5994 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags2, ImGuiTableFlags_BordersInnerV); 5995 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags2, ImGuiTableFlags_BordersOuterV); 5996 PopStyleCompact(); 5997 if (ImGui::BeginTable("table2", 4, flags2)) 5998 { 5999 // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns 6000 // will default to ImGuiTableColumnFlags_WidthFixed. 6001 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f); 6002 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); 6003 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f); 6004 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); 6005 for (int row = 0; row < 5; row++) 6006 { 6007 ImGui::TableNextRow(); 6008 for (int column = 0; column < 4; column++) 6009 { 6010 ImGui::TableSetColumnIndex(column); 6011 if (row == 0) 6012 ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x); 6013 else 6014 ImGui::Text("Hello %d,%d", column, row); 6015 } 6016 } 6017 ImGui::EndTable(); 6018 } 6019 ImGui::TreePop(); 6020 } 6021 6022 if (open_action != -1) 6023 ImGui::SetNextItemOpen(open_action != 0); 6024 IMGUI_DEMO_MARKER("Tables/Nested tables"); 6025 if (ImGui::TreeNode("Nested tables")) 6026 { 6027 HelpMarker("This demonstrates embedding a table into another table cell."); 6028 6029 if (ImGui::BeginTable("table_nested1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) 6030 { 6031 ImGui::TableSetupColumn("A0"); 6032 ImGui::TableSetupColumn("A1"); 6033 ImGui::TableHeadersRow(); 6034 6035 ImGui::TableNextColumn(); 6036 ImGui::Text("A0 Row 0"); 6037 { 6038 float rows_height = TEXT_BASE_HEIGHT * 2; 6039 if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) 6040 { 6041 ImGui::TableSetupColumn("B0"); 6042 ImGui::TableSetupColumn("B1"); 6043 ImGui::TableHeadersRow(); 6044 6045 ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height); 6046 ImGui::TableNextColumn(); 6047 ImGui::Text("B0 Row 0"); 6048 ImGui::TableNextColumn(); 6049 ImGui::Text("B1 Row 0"); 6050 ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height); 6051 ImGui::TableNextColumn(); 6052 ImGui::Text("B0 Row 1"); 6053 ImGui::TableNextColumn(); 6054 ImGui::Text("B1 Row 1"); 6055 6056 ImGui::EndTable(); 6057 } 6058 } 6059 ImGui::TableNextColumn(); ImGui::Text("A1 Row 0"); 6060 ImGui::TableNextColumn(); ImGui::Text("A0 Row 1"); 6061 ImGui::TableNextColumn(); ImGui::Text("A1 Row 1"); 6062 ImGui::EndTable(); 6063 } 6064 ImGui::TreePop(); 6065 } 6066 6067 if (open_action != -1) 6068 ImGui::SetNextItemOpen(open_action != 0); 6069 IMGUI_DEMO_MARKER("Tables/Row height"); 6070 if (ImGui::TreeNode("Row height")) 6071 { 6072 HelpMarker( 6073 "You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, " 6074 "so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n" 6075 "We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row."); 6076 if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_Borders)) 6077 { 6078 for (int row = 0; row < 8; row++) 6079 { 6080 float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row); 6081 ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height); 6082 ImGui::TableNextColumn(); 6083 ImGui::Text("min_row_height = %.2f", min_row_height); 6084 } 6085 ImGui::EndTable(); 6086 } 6087 6088 HelpMarker( 6089 "Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n" 6090 "Please note that Tables Row Height is not the same thing as Current Line Height, " 6091 "as a table cell may contains multiple lines."); 6092 if (ImGui::BeginTable("table_share_lineheight", 2, ImGuiTableFlags_Borders)) 6093 { 6094 ImGui::TableNextRow(); 6095 ImGui::TableNextColumn(); 6096 ImGui::ColorButton("##1", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40)); 6097 ImGui::TableNextColumn(); 6098 ImGui::Text("Line 1"); 6099 ImGui::Text("Line 2"); 6100 6101 ImGui::TableNextRow(); 6102 ImGui::TableNextColumn(); 6103 ImGui::ColorButton("##2", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40)); 6104 ImGui::TableNextColumn(); 6105 ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column 6106 ImGui::Text("Line 1, with SameLine(0,0)"); 6107 ImGui::Text("Line 2"); 6108 6109 ImGui::EndTable(); 6110 } 6111 6112 HelpMarker("Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table."); 6113 if (ImGui::BeginTable("table_changing_cellpadding_y", 1, ImGuiTableFlags_Borders)) 6114 { 6115 ImGuiStyle& style = ImGui::GetStyle(); 6116 for (int row = 0; row < 8; row++) 6117 { 6118 if ((row % 3) == 2) 6119 ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(style.CellPadding.x, 20.0f)); 6120 ImGui::TableNextRow(ImGuiTableRowFlags_None); 6121 ImGui::TableNextColumn(); 6122 ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y); 6123 if ((row % 3) == 2) 6124 ImGui::PopStyleVar(); 6125 } 6126 ImGui::EndTable(); 6127 } 6128 6129 ImGui::TreePop(); 6130 } 6131 6132 if (open_action != -1) 6133 ImGui::SetNextItemOpen(open_action != 0); 6134 IMGUI_DEMO_MARKER("Tables/Outer size"); 6135 if (ImGui::TreeNode("Outer size")) 6136 { 6137 // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY 6138 // Important to that note how the two flags have slightly different behaviors! 6139 ImGui::Text("Using NoHostExtendX and NoHostExtendY:"); 6140 PushStyleCompact(); 6141 static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX; 6142 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); 6143 ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used."); 6144 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); 6145 ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible."); 6146 PopStyleCompact(); 6147 6148 ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f); 6149 if (ImGui::BeginTable("table1", 3, flags, outer_size)) 6150 { 6151 for (int row = 0; row < 10; row++) 6152 { 6153 ImGui::TableNextRow(); 6154 for (int column = 0; column < 3; column++) 6155 { 6156 ImGui::TableNextColumn(); 6157 ImGui::Text("Cell %d,%d", column, row); 6158 } 6159 } 6160 ImGui::EndTable(); 6161 } 6162 ImGui::SameLine(); 6163 ImGui::Text("Hello!"); 6164 6165 ImGui::Spacing(); 6166 6167 ImGui::Text("Using explicit size:"); 6168 if (ImGui::BeginTable("table2", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) 6169 { 6170 for (int row = 0; row < 5; row++) 6171 { 6172 ImGui::TableNextRow(); 6173 for (int column = 0; column < 3; column++) 6174 { 6175 ImGui::TableNextColumn(); 6176 ImGui::Text("Cell %d,%d", column, row); 6177 } 6178 } 6179 ImGui::EndTable(); 6180 } 6181 ImGui::SameLine(); 6182 if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) 6183 { 6184 for (int row = 0; row < 3; row++) 6185 { 6186 ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f); 6187 for (int column = 0; column < 3; column++) 6188 { 6189 ImGui::TableNextColumn(); 6190 ImGui::Text("Cell %d,%d", column, row); 6191 } 6192 } 6193 ImGui::EndTable(); 6194 } 6195 6196 ImGui::TreePop(); 6197 } 6198 6199 if (open_action != -1) 6200 ImGui::SetNextItemOpen(open_action != 0); 6201 IMGUI_DEMO_MARKER("Tables/Background color"); 6202 if (ImGui::TreeNode("Background color")) 6203 { 6204 static ImGuiTableFlags flags = ImGuiTableFlags_RowBg; 6205 static int row_bg_type = 1; 6206 static int row_bg_target = 1; 6207 static int cell_bg_type = 1; 6208 6209 PushStyleCompact(); 6210 ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders); 6211 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); 6212 ImGui::SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style."); 6213 ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0"); 6214 ImGui::Combo("row bg target", (int*)&row_bg_target, "RowBg0\0RowBg1\0"); ImGui::SameLine(); HelpMarker("Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them."); 6215 ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0"); ImGui::SameLine(); HelpMarker("We are colorizing cells to B1->C2 here."); 6216 IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2); 6217 IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1); 6218 IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1); 6219 PopStyleCompact(); 6220 6221 if (ImGui::BeginTable("table1", 5, flags)) 6222 { 6223 for (int row = 0; row < 6; row++) 6224 { 6225 ImGui::TableNextRow(); 6226 6227 // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)' 6228 // 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. 6229 if (row_bg_type != 0) 6230 { 6231 ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient? 6232 ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color); 6233 } 6234 6235 // Fill cells 6236 for (int column = 0; column < 5; column++) 6237 { 6238 ImGui::TableSetColumnIndex(column); 6239 ImGui::Text("%c%c", 'A' + row, '0' + column); 6240 6241 // Change background of Cells B1->C2 6242 // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)' 6243 // (the CellBg color will be blended over the RowBg and ColumnBg colors) 6244 // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop. 6245 if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1) 6246 { 6247 ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f)); 6248 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color); 6249 } 6250 } 6251 } 6252 ImGui::EndTable(); 6253 } 6254 ImGui::TreePop(); 6255 } 6256 6257 if (open_action != -1) 6258 ImGui::SetNextItemOpen(open_action != 0); 6259 IMGUI_DEMO_MARKER("Tables/Tree view"); 6260 if (ImGui::TreeNode("Tree view")) 6261 { 6262 static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; 6263 6264 static ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAllColumns; 6265 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags, ImGuiTreeNodeFlags_SpanFullWidth); 6266 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanTextWidth", &tree_node_flags, ImGuiTreeNodeFlags_SpanTextWidth); 6267 ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags, ImGuiTreeNodeFlags_SpanAllColumns); 6268 6269 HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns."); 6270 if (ImGui::BeginTable("3ways", 3, flags)) 6271 { 6272 // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On 6273 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); 6274 ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); 6275 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); 6276 ImGui::TableHeadersRow(); 6277 6278 // Simple storage to output a dummy file-system. 6279 struct MyTreeNode 6280 { 6281 const char* Name; 6282 const char* Type; 6283 int Size; 6284 int ChildIdx; 6285 int ChildCount; 6286 static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes) 6287 { 6288 ImGui::TableNextRow(); 6289 ImGui::TableNextColumn(); 6290 const bool is_folder = (node->ChildCount > 0); 6291 if (is_folder) 6292 { 6293 bool open = ImGui::TreeNodeEx(node->Name, tree_node_flags); 6294 ImGui::TableNextColumn(); 6295 ImGui::TextDisabled("--"); 6296 ImGui::TableNextColumn(); 6297 ImGui::TextUnformatted(node->Type); 6298 if (open) 6299 { 6300 for (int child_n = 0; child_n < node->ChildCount; child_n++) 6301 DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes); 6302 ImGui::TreePop(); 6303 } 6304 } 6305 else 6306 { 6307 ImGui::TreeNodeEx(node->Name, tree_node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen); 6308 ImGui::TableNextColumn(); 6309 ImGui::Text("%d", node->Size); 6310 ImGui::TableNextColumn(); 6311 ImGui::TextUnformatted(node->Type); 6312 } 6313 } 6314 }; 6315 static const MyTreeNode nodes[] = 6316 { 6317 { "Root", "Folder", -1, 1, 3 }, // 0 6318 { "Music", "Folder", -1, 4, 2 }, // 1 6319 { "Textures", "Folder", -1, 6, 3 }, // 2 6320 { "desktop.ini", "System file", 1024, -1,-1 }, // 3 6321 { "File1_a.wav", "Audio file", 123000, -1,-1 }, // 4 6322 { "File1_b.wav", "Audio file", 456000, -1,-1 }, // 5 6323 { "Image001.png", "Image file", 203128, -1,-1 }, // 6 6324 { "Copy of Image001.png", "Image file", 203256, -1,-1 }, // 7 6325 { "Copy of Image001 (Final2).png","Image file", 203512, -1,-1 }, // 8 6326 }; 6327 6328 MyTreeNode::DisplayNode(&nodes[0], nodes); 6329 6330 ImGui::EndTable(); 6331 } 6332 ImGui::TreePop(); 6333 } 6334 6335 if (open_action != -1) 6336 ImGui::SetNextItemOpen(open_action != 0); 6337 IMGUI_DEMO_MARKER("Tables/Item width"); 6338 if (ImGui::TreeNode("Item width")) 6339 { 6340 HelpMarker( 6341 "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n" 6342 "Note that on auto-resizing non-resizable fixed columns, querying the content width for " 6343 "e.g. right-alignment doesn't make sense."); 6344 if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders)) 6345 { 6346 ImGui::TableSetupColumn("small"); 6347 ImGui::TableSetupColumn("half"); 6348 ImGui::TableSetupColumn("right-align"); 6349 ImGui::TableHeadersRow(); 6350 6351 for (int row = 0; row < 3; row++) 6352 { 6353 ImGui::TableNextRow(); 6354 if (row == 0) 6355 { 6356 // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient) 6357 ImGui::TableSetColumnIndex(0); 6358 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small 6359 ImGui::TableSetColumnIndex(1); 6360 ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); 6361 ImGui::TableSetColumnIndex(2); 6362 ImGui::PushItemWidth(-FLT_MIN); // Right-aligned 6363 } 6364 6365 // Draw our contents 6366 static float dummy_f = 0.0f; 6367 ImGui::PushID(row); 6368 ImGui::TableSetColumnIndex(0); 6369 ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f); 6370 ImGui::TableSetColumnIndex(1); 6371 ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f); 6372 ImGui::TableSetColumnIndex(2); 6373 ImGui::SliderFloat("##float2", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned 6374 ImGui::PopID(); 6375 } 6376 ImGui::EndTable(); 6377 } 6378 ImGui::TreePop(); 6379 } 6380 6381 // Demonstrate using TableHeader() calls instead of TableHeadersRow() 6382 if (open_action != -1) 6383 ImGui::SetNextItemOpen(open_action != 0); 6384 IMGUI_DEMO_MARKER("Tables/Custom headers"); 6385 if (ImGui::TreeNode("Custom headers")) 6386 { 6387 const int COLUMNS_COUNT = 3; 6388 if (ImGui::BeginTable("table_custom_headers", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) 6389 { 6390 ImGui::TableSetupColumn("Apricot"); 6391 ImGui::TableSetupColumn("Banana"); 6392 ImGui::TableSetupColumn("Cherry"); 6393 6394 // Dummy entire-column selection storage 6395 // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox. 6396 static bool column_selected[3] = {}; 6397 6398 // Instead of calling TableHeadersRow() we'll submit custom headers ourselves 6399 ImGui::TableNextRow(ImGuiTableRowFlags_Headers); 6400 for (int column = 0; column < COLUMNS_COUNT; column++) 6401 { 6402 ImGui::TableSetColumnIndex(column); 6403 const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() 6404 ImGui::PushID(column); 6405 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); 6406 ImGui::Checkbox("##checkall", &column_selected[column]); 6407 ImGui::PopStyleVar(); 6408 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); 6409 ImGui::TableHeader(column_name); 6410 ImGui::PopID(); 6411 } 6412 6413 for (int row = 0; row < 5; row++) 6414 { 6415 ImGui::TableNextRow(); 6416 for (int column = 0; column < 3; column++) 6417 { 6418 char buf[32]; 6419 sprintf(buf, "Cell %d,%d", column, row); 6420 ImGui::TableSetColumnIndex(column); 6421 ImGui::Selectable(buf, column_selected[column]); 6422 } 6423 } 6424 ImGui::EndTable(); 6425 } 6426 ImGui::TreePop(); 6427 } 6428 6429 // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers 6430 if (open_action != -1) 6431 ImGui::SetNextItemOpen(open_action != 0); 6432 IMGUI_DEMO_MARKER("Tables/Angled headers"); 6433 if (ImGui::TreeNode("Angled headers")) 6434 { 6435 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" }; 6436 const int columns_count = IM_ARRAYSIZE(column_names); 6437 const int rows_count = 12; 6438 6439 static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn; 6440 static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed; 6441 static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage 6442 static int frozen_cols = 1; 6443 static int frozen_rows = 2; 6444 ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX); 6445 ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY); 6446 ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable); 6447 ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody); 6448 ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn); 6449 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 6450 ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2); 6451 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 6452 ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2); 6453 ImGui::CheckboxFlags("Disable header contributing to column width", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth); 6454 6455 if (ImGui::TreeNode("Style settings")) 6456 { 6457 ImGui::SameLine(); 6458 HelpMarker("Giving access to some ImGuiStyle value in this demo for convenience."); 6459 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 6460 ImGui::SliderAngle("style.TableAngledHeadersAngle", &ImGui::GetStyle().TableAngledHeadersAngle, -50.0f, +50.0f); 6461 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 6462 ImGui::SliderFloat2("style.TableAngledHeadersTextAlign", (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f"); 6463 ImGui::TreePop(); 6464 } 6465 6466 if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12))) 6467 { 6468 ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder); 6469 for (int n = 1; n < columns_count; n++) 6470 ImGui::TableSetupColumn(column_names[n], column_flags); 6471 ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows); 6472 6473 ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag. 6474 ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions. 6475 for (int row = 0; row < rows_count; row++) 6476 { 6477 ImGui::PushID(row); 6478 ImGui::TableNextRow(); 6479 ImGui::TableSetColumnIndex(0); 6480 ImGui::AlignTextToFramePadding(); 6481 ImGui::Text("Track %d", row); 6482 for (int column = 1; column < columns_count; column++) 6483 if (ImGui::TableSetColumnIndex(column)) 6484 { 6485 ImGui::PushID(column); 6486 ImGui::Checkbox("", &bools[row * columns_count + column]); 6487 ImGui::PopID(); 6488 } 6489 ImGui::PopID(); 6490 } 6491 ImGui::EndTable(); 6492 } 6493 ImGui::TreePop(); 6494 } 6495 6496 // Demonstrate creating custom context menus inside columns, 6497 // while playing it nice with context menus provided by TableHeadersRow()/TableHeader() 6498 if (open_action != -1) 6499 ImGui::SetNextItemOpen(open_action != 0); 6500 IMGUI_DEMO_MARKER("Tables/Context menus"); 6501 if (ImGui::TreeNode("Context menus")) 6502 { 6503 HelpMarker( 6504 "By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n" 6505 "Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); 6506 static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody; 6507 6508 PushStyleCompact(); 6509 ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags1, ImGuiTableFlags_ContextMenuInBody); 6510 PopStyleCompact(); 6511 6512 // Context Menus: first example 6513 // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu. 6514 // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set) 6515 const int COLUMNS_COUNT = 3; 6516 if (ImGui::BeginTable("table_context_menu", COLUMNS_COUNT, flags1)) 6517 { 6518 ImGui::TableSetupColumn("One"); 6519 ImGui::TableSetupColumn("Two"); 6520 ImGui::TableSetupColumn("Three"); 6521 6522 // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu. 6523 ImGui::TableHeadersRow(); 6524 6525 // Submit dummy contents 6526 for (int row = 0; row < 4; row++) 6527 { 6528 ImGui::TableNextRow(); 6529 for (int column = 0; column < COLUMNS_COUNT; column++) 6530 { 6531 ImGui::TableSetColumnIndex(column); 6532 ImGui::Text("Cell %d,%d", column, row); 6533 } 6534 } 6535 ImGui::EndTable(); 6536 } 6537 6538 // Context Menus: second example 6539 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. 6540 // [2.2] Right-click on the ".." to open a custom popup 6541 // [2.3] Right-click in columns to open another custom popup 6542 HelpMarker( 6543 "Demonstrate mixing table context menu (over header), item context button (over button) " 6544 "and custom per-colunm context menu (over column body)."); 6545 ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; 6546 if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2)) 6547 { 6548 ImGui::TableSetupColumn("One"); 6549 ImGui::TableSetupColumn("Two"); 6550 ImGui::TableSetupColumn("Three"); 6551 6552 // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. 6553 ImGui::TableHeadersRow(); 6554 for (int row = 0; row < 4; row++) 6555 { 6556 ImGui::TableNextRow(); 6557 for (int column = 0; column < COLUMNS_COUNT; column++) 6558 { 6559 // Submit dummy contents 6560 ImGui::TableSetColumnIndex(column); 6561 ImGui::Text("Cell %d,%d", column, row); 6562 ImGui::SameLine(); 6563 6564 // [2.2] Right-click on the ".." to open a custom popup 6565 ImGui::PushID(row * COLUMNS_COUNT + column); 6566 ImGui::SmallButton(".."); 6567 if (ImGui::BeginPopupContextItem()) 6568 { 6569 ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row); 6570 if (ImGui::Button("Close")) 6571 ImGui::CloseCurrentPopup(); 6572 ImGui::EndPopup(); 6573 } 6574 ImGui::PopID(); 6575 } 6576 } 6577 6578 // [2.3] Right-click anywhere in columns to open another custom popup 6579 // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup 6580 // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping) 6581 int hovered_column = -1; 6582 for (int column = 0; column < COLUMNS_COUNT + 1; column++) 6583 { 6584 ImGui::PushID(column); 6585 if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered) 6586 hovered_column = column; 6587 if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1)) 6588 ImGui::OpenPopup("MyPopup"); 6589 if (ImGui::BeginPopup("MyPopup")) 6590 { 6591 if (column == COLUMNS_COUNT) 6592 ImGui::Text("This is a custom popup for unused space after the last column."); 6593 else 6594 ImGui::Text("This is a custom popup for Column %d", column); 6595 if (ImGui::Button("Close")) 6596 ImGui::CloseCurrentPopup(); 6597 ImGui::EndPopup(); 6598 } 6599 ImGui::PopID(); 6600 } 6601 6602 ImGui::EndTable(); 6603 ImGui::Text("Hovered column: %d", hovered_column); 6604 } 6605 ImGui::TreePop(); 6606 } 6607 6608 // Demonstrate creating multiple tables with the same ID 6609 if (open_action != -1) 6610 ImGui::SetNextItemOpen(open_action != 0); 6611 IMGUI_DEMO_MARKER("Tables/Synced instances"); 6612 if (ImGui::TreeNode("Synced instances")) 6613 { 6614 HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); 6615 6616 static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings; 6617 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); 6618 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); 6619 ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit); 6620 ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); 6621 for (int n = 0; n < 3; n++) 6622 { 6623 char buf[32]; 6624 sprintf(buf, "Synced Table %d", n); 6625 bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen); 6626 if (open && ImGui::BeginTable("Table", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5))) 6627 { 6628 ImGui::TableSetupColumn("One"); 6629 ImGui::TableSetupColumn("Two"); 6630 ImGui::TableSetupColumn("Three"); 6631 ImGui::TableHeadersRow(); 6632 const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions. 6633 for (int cell = 0; cell < cell_count; cell++) 6634 { 6635 ImGui::TableNextColumn(); 6636 ImGui::Text("this cell %d", cell); 6637 } 6638 ImGui::EndTable(); 6639 } 6640 } 6641 ImGui::TreePop(); 6642 } 6643 6644 // Demonstrate using Sorting facilities 6645 // This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting. 6646 // Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified) 6647 static const char* template_items_names[] = 6648 { 6649 "Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango", 6650 "Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot" 6651 }; 6652 if (open_action != -1) 6653 ImGui::SetNextItemOpen(open_action != 0); 6654 IMGUI_DEMO_MARKER("Tables/Sorting"); 6655 if (ImGui::TreeNode("Sorting")) 6656 { 6657 // Create item list 6658 static ImVector<MyItem> items; 6659 if (items.Size == 0) 6660 { 6661 items.resize(50, MyItem()); 6662 for (int n = 0; n < items.Size; n++) 6663 { 6664 const int template_n = n % IM_ARRAYSIZE(template_items_names); 6665 MyItem& item = items[n]; 6666 item.ID = n; 6667 item.Name = template_items_names[template_n]; 6668 item.Quantity = (n * n - n) % 20; // Assign default quantities 6669 } 6670 } 6671 6672 // Options 6673 static ImGuiTableFlags flags = 6674 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti 6675 | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody 6676 | ImGuiTableFlags_ScrollY; 6677 PushStyleCompact(); 6678 ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti); 6679 ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1)."); 6680 ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); 6681 ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); 6682 PopStyleCompact(); 6683 6684 if (ImGui::BeginTable("table_sorting", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f)) 6685 { 6686 // Declare columns 6687 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. 6688 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! 6689 // Demonstrate using a mixture of flags among available sort-related flags: 6690 // - ImGuiTableColumnFlags_DefaultSort 6691 // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending 6692 // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending 6693 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_ID); 6694 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); 6695 ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); 6696 ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity); 6697 ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible 6698 ImGui::TableHeadersRow(); 6699 6700 // Sort our data if sort specs have been changed! 6701 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) 6702 if (sort_specs->SpecsDirty) 6703 { 6704 MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size); 6705 sort_specs->SpecsDirty = false; 6706 } 6707 6708 // Demonstrate using clipper for large vertical lists 6709 ImGuiListClipper clipper; 6710 clipper.Begin(items.Size); 6711 while (clipper.Step()) 6712 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) 6713 { 6714 // Display a data item 6715 MyItem* item = &items[row_n]; 6716 ImGui::PushID(item->ID); 6717 ImGui::TableNextRow(); 6718 ImGui::TableNextColumn(); 6719 ImGui::Text("%04d", item->ID); 6720 ImGui::TableNextColumn(); 6721 ImGui::TextUnformatted(item->Name); 6722 ImGui::TableNextColumn(); 6723 ImGui::SmallButton("None"); 6724 ImGui::TableNextColumn(); 6725 ImGui::Text("%d", item->Quantity); 6726 ImGui::PopID(); 6727 } 6728 ImGui::EndTable(); 6729 } 6730 ImGui::TreePop(); 6731 } 6732 6733 // In this example we'll expose most table flags and settings. 6734 // For specific flags and settings refer to the corresponding section for more detailed explanation. 6735 // This section is mostly useful to experiment with combining certain flags or settings with each others. 6736 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG] 6737 if (open_action != -1) 6738 ImGui::SetNextItemOpen(open_action != 0); 6739 IMGUI_DEMO_MARKER("Tables/Advanced"); 6740 if (ImGui::TreeNode("Advanced")) 6741 { 6742 static ImGuiTableFlags flags = 6743 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable 6744 | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti 6745 | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody 6746 | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY 6747 | ImGuiTableFlags_SizingFixedFit; 6748 static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None; 6749 6750 enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; 6751 static int contents_type = CT_SelectableSpanRow; 6752 const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }; 6753 static int freeze_cols = 1; 6754 static int freeze_rows = 1; 6755 static int items_count = IM_ARRAYSIZE(template_items_names) * 2; 6756 static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12); 6757 static float row_min_height = 0.0f; // Auto 6758 static float inner_width_with_scroll = 0.0f; // Auto-extend 6759 static bool outer_size_enabled = true; 6760 static bool show_headers = true; 6761 static bool show_wrapped_text = false; 6762 //static ImGuiTextFilter filter; 6763 //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing 6764 if (ImGui::TreeNode("Options")) 6765 { 6766 // Make the UI compact because there are so many fields 6767 PushStyleCompact(); 6768 ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f); 6769 6770 if (ImGui::TreeNodeEx("Features:", ImGuiTreeNodeFlags_DefaultOpen)) 6771 { 6772 ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); 6773 ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable); 6774 ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); 6775 ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", &flags, ImGuiTableFlags_Sortable); 6776 ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", &flags, ImGuiTableFlags_NoSavedSettings); 6777 ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags, ImGuiTableFlags_ContextMenuInBody); 6778 ImGui::TreePop(); 6779 } 6780 6781 if (ImGui::TreeNodeEx("Decorations:", ImGuiTreeNodeFlags_DefaultOpen)) 6782 { 6783 ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); 6784 ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); 6785 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); 6786 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); 6787 ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); 6788 ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); 6789 ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); 6790 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers"); 6791 ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); 6792 ImGui::TreePop(); 6793 } 6794 6795 if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen)) 6796 { 6797 EditTableSizingFlags(&flags); 6798 ImGui::SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical."); 6799 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); 6800 ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used."); 6801 ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); 6802 ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible."); 6803 ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); 6804 ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); 6805 ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); 6806 ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth."); 6807 ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); 6808 ImGui::SameLine(); HelpMarker("Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options."); 6809 ImGui::TreePop(); 6810 } 6811 6812 if (ImGui::TreeNodeEx("Padding:", ImGuiTreeNodeFlags_DefaultOpen)) 6813 { 6814 ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX); 6815 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX); 6816 ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX); 6817 ImGui::TreePop(); 6818 } 6819 6820 if (ImGui::TreeNodeEx("Scrolling:", ImGuiTreeNodeFlags_DefaultOpen)) 6821 { 6822 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); 6823 ImGui::SameLine(); 6824 ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); 6825 ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); 6826 ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); 6827 ImGui::SameLine(); 6828 ImGui::SetNextItemWidth(ImGui::GetFrameHeight()); 6829 ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput); 6830 ImGui::TreePop(); 6831 } 6832 6833 if (ImGui::TreeNodeEx("Sorting:", ImGuiTreeNodeFlags_DefaultOpen)) 6834 { 6835 ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti); 6836 ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1)."); 6837 ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); 6838 ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); 6839 ImGui::TreePop(); 6840 } 6841 6842 if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen)) 6843 { 6844 ImGui::Checkbox("show_headers", &show_headers); 6845 ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); 6846 ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader); 6847 ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo)."); 6848 ImGui::TreePop(); 6849 } 6850 6851 if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) 6852 { 6853 ImGui::Checkbox("show_wrapped_text", &show_wrapped_text); 6854 6855 ImGui::DragFloat2("##OuterSize", &outer_size_value.x); 6856 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); 6857 ImGui::Checkbox("outer_size", &outer_size_enabled); 6858 ImGui::SameLine(); 6859 HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n" 6860 "- The table is output directly in the parent window.\n" 6861 "- OuterSize.x < 0.0f will right-align the table.\n" 6862 "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n" 6863 "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set)."); 6864 6865 // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling. 6866 // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled. 6867 ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX); 6868 6869 ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX); 6870 ImGui::SameLine(); HelpMarker("Specify height of the Selectable item."); 6871 6872 ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999); 6873 ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_ARRAYSIZE(contents_type_names)); 6874 //filter.Draw("filter"); 6875 ImGui::TreePop(); 6876 } 6877 6878 ImGui::PopItemWidth(); 6879 PopStyleCompact(); 6880 ImGui::Spacing(); 6881 ImGui::TreePop(); 6882 } 6883 6884 // Update item list if we changed the number of items 6885 static ImVector<MyItem> items; 6886 static ImVector<int> selection; 6887 static bool items_need_sort = false; 6888 if (items.Size != items_count) 6889 { 6890 items.resize(items_count, MyItem()); 6891 for (int n = 0; n < items_count; n++) 6892 { 6893 const int template_n = n % IM_ARRAYSIZE(template_items_names); 6894 MyItem& item = items[n]; 6895 item.ID = n; 6896 item.Name = template_items_names[template_n]; 6897 item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities 6898 } 6899 } 6900 6901 const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList(); 6902 const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size; 6903 ImVec2 table_scroll_cur, table_scroll_max; // For debug display 6904 const ImDrawList* table_draw_list = NULL; // " 6905 6906 // Submit table 6907 const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f; 6908 if (ImGui::BeginTable("table_advanced", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use)) 6909 { 6910 // Declare columns 6911 // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. 6912 // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! 6913 ImGui::TableSetupColumn("ID", columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); 6914 ImGui::TableSetupColumn("Name", columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); 6915 ImGui::TableSetupColumn("Action", columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); 6916 ImGui::TableSetupColumn("Quantity", columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); 6917 ImGui::TableSetupColumn("Description", columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description); 6918 ImGui::TableSetupColumn("Hidden", columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); 6919 ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); 6920 6921 // Sort our data if sort specs have been changed! 6922 ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs(); 6923 if (sort_specs && sort_specs->SpecsDirty) 6924 items_need_sort = true; 6925 if (sort_specs && items_need_sort && items.Size > 1) 6926 { 6927 MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size); 6928 sort_specs->SpecsDirty = false; 6929 } 6930 items_need_sort = false; 6931 6932 // Take note of whether we are currently sorting based on the Quantity field, 6933 // we will use this to trigger sorting when we know the data of this column has been modified. 6934 const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0; 6935 6936 // Show headers 6937 if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0) 6938 ImGui::TableAngledHeadersRow(); 6939 if (show_headers) 6940 ImGui::TableHeadersRow(); 6941 6942 // Show data 6943 // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here? 6944 #if 1 6945 // Demonstrate using clipper for large vertical lists 6946 ImGuiListClipper clipper; 6947 clipper.Begin(items.Size); 6948 while (clipper.Step()) 6949 { 6950 for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) 6951 #else 6952 // Without clipper 6953 { 6954 for (int row_n = 0; row_n < items.Size; row_n++) 6955 #endif 6956 { 6957 MyItem* item = &items[row_n]; 6958 //if (!filter.PassFilter(item->Name)) 6959 // continue; 6960 6961 const bool item_is_selected = selection.contains(item->ID); 6962 ImGui::PushID(item->ID); 6963 ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height); 6964 6965 // For the demo purpose we can select among different type of items submitted in the first column 6966 ImGui::TableSetColumnIndex(0); 6967 char label[32]; 6968 sprintf(label, "%04d", item->ID); 6969 if (contents_type == CT_Text) 6970 ImGui::TextUnformatted(label); 6971 else if (contents_type == CT_Button) 6972 ImGui::Button(label); 6973 else if (contents_type == CT_SmallButton) 6974 ImGui::SmallButton(label); 6975 else if (contents_type == CT_FillButton) 6976 ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); 6977 else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow) 6978 { 6979 ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None; 6980 if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height))) 6981 { 6982 if (ImGui::GetIO().KeyCtrl) 6983 { 6984 if (item_is_selected) 6985 selection.find_erase_unsorted(item->ID); 6986 else 6987 selection.push_back(item->ID); 6988 } 6989 else 6990 { 6991 selection.clear(); 6992 selection.push_back(item->ID); 6993 } 6994 } 6995 } 6996 6997 if (ImGui::TableSetColumnIndex(1)) 6998 ImGui::TextUnformatted(item->Name); 6999 7000 // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity, 7001 // and we are currently sorting on the column showing the Quantity. 7002 // To avoid triggering a sort while holding the button, we only trigger it when the button has been released. 7003 // You will probably need some extra logic if you want to automatically sort when a specific entry changes. 7004 if (ImGui::TableSetColumnIndex(2)) 7005 { 7006 if (ImGui::SmallButton("Chop")) { item->Quantity += 1; } 7007 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; } 7008 ImGui::SameLine(); 7009 if (ImGui::SmallButton("Eat")) { item->Quantity -= 1; } 7010 if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; } 7011 } 7012 7013 if (ImGui::TableSetColumnIndex(3)) 7014 ImGui::Text("%d", item->Quantity); 7015 7016 ImGui::TableSetColumnIndex(4); 7017 if (show_wrapped_text) 7018 ImGui::TextWrapped("Lorem ipsum dolor sit amet"); 7019 else 7020 ImGui::Text("Lorem ipsum dolor sit amet"); 7021 7022 if (ImGui::TableSetColumnIndex(5)) 7023 ImGui::Text("1234"); 7024 7025 ImGui::PopID(); 7026 } 7027 } 7028 7029 // Store some info to display debug details below 7030 table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); 7031 table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY()); 7032 table_draw_list = ImGui::GetWindowDrawList(); 7033 ImGui::EndTable(); 7034 } 7035 static bool show_debug_details = false; 7036 ImGui::Checkbox("Debug details", &show_debug_details); 7037 if (show_debug_details && table_draw_list) 7038 { 7039 ImGui::SameLine(0.0f, 0.0f); 7040 const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size; 7041 if (table_draw_list == parent_draw_list) 7042 ImGui::Text(": DrawCmd: +%d (in same window)", 7043 table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count); 7044 else 7045 ImGui::Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)", 7046 table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y); 7047 } 7048 ImGui::TreePop(); 7049 } 7050 7051 ImGui::PopID(); 7052 7053 ShowDemoWindowColumns(); 7054 7055 if (disable_indent) 7056 ImGui::PopStyleVar(); 7057 } 7058 7059 // Demonstrate old/legacy Columns API! 7060 // [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!] 7061 static void ShowDemoWindowColumns() 7062 { 7063 IMGUI_DEMO_MARKER("Columns (legacy API)"); 7064 bool open = ImGui::TreeNode("Legacy Columns API"); 7065 ImGui::SameLine(); 7066 HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!"); 7067 if (!open) 7068 return; 7069 7070 // Basic columns 7071 IMGUI_DEMO_MARKER("Columns (legacy API)/Basic"); 7072 if (ImGui::TreeNode("Basic")) 7073 { 7074 ImGui::Text("Without border:"); 7075 ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border 7076 ImGui::Separator(); 7077 for (int n = 0; n < 14; n++) 7078 { 7079 char label[32]; 7080 sprintf(label, "Item %d", n); 7081 if (ImGui::Selectable(label)) {} 7082 //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {} 7083 ImGui::NextColumn(); 7084 } 7085 ImGui::Columns(1); 7086 ImGui::Separator(); 7087 7088 ImGui::Text("With border:"); 7089 ImGui::Columns(4, "mycolumns"); // 4-ways, with border 7090 ImGui::Separator(); 7091 ImGui::Text("ID"); ImGui::NextColumn(); 7092 ImGui::Text("Name"); ImGui::NextColumn(); 7093 ImGui::Text("Path"); ImGui::NextColumn(); 7094 ImGui::Text("Hovered"); ImGui::NextColumn(); 7095 ImGui::Separator(); 7096 const char* names[3] = { "One", "Two", "Three" }; 7097 const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; 7098 static int selected = -1; 7099 for (int i = 0; i < 3; i++) 7100 { 7101 char label[32]; 7102 sprintf(label, "%04d", i); 7103 if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) 7104 selected = i; 7105 bool hovered = ImGui::IsItemHovered(); 7106 ImGui::NextColumn(); 7107 ImGui::Text(names[i]); ImGui::NextColumn(); 7108 ImGui::Text(paths[i]); ImGui::NextColumn(); 7109 ImGui::Text("%d", hovered); ImGui::NextColumn(); 7110 } 7111 ImGui::Columns(1); 7112 ImGui::Separator(); 7113 ImGui::TreePop(); 7114 } 7115 7116 IMGUI_DEMO_MARKER("Columns (legacy API)/Borders"); 7117 if (ImGui::TreeNode("Borders")) 7118 { 7119 // NB: Future columns API should allow automatic horizontal borders. 7120 static bool h_borders = true; 7121 static bool v_borders = true; 7122 static int columns_count = 4; 7123 const int lines_count = 3; 7124 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); 7125 ImGui::DragInt("##columns_count", &columns_count, 0.1f, 2, 10, "%d columns"); 7126 if (columns_count < 2) 7127 columns_count = 2; 7128 ImGui::SameLine(); 7129 ImGui::Checkbox("horizontal", &h_borders); 7130 ImGui::SameLine(); 7131 ImGui::Checkbox("vertical", &v_borders); 7132 ImGui::Columns(columns_count, NULL, v_borders); 7133 for (int i = 0; i < columns_count * lines_count; i++) 7134 { 7135 if (h_borders && ImGui::GetColumnIndex() == 0) 7136 ImGui::Separator(); 7137 ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i); 7138 ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); 7139 ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); 7140 ImGui::Text("Offset %.2f", ImGui::GetColumnOffset()); 7141 ImGui::Text("Long text that is likely to clip"); 7142 ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f)); 7143 ImGui::NextColumn(); 7144 } 7145 ImGui::Columns(1); 7146 if (h_borders) 7147 ImGui::Separator(); 7148 ImGui::TreePop(); 7149 } 7150 7151 // Create multiple items in a same cell before switching to next column 7152 IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items"); 7153 if (ImGui::TreeNode("Mixed items")) 7154 { 7155 ImGui::Columns(3, "mixed"); 7156 ImGui::Separator(); 7157 7158 ImGui::Text("Hello"); 7159 ImGui::Button("Banana"); 7160 ImGui::NextColumn(); 7161 7162 ImGui::Text("ImGui"); 7163 ImGui::Button("Apple"); 7164 static float foo = 1.0f; 7165 ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f"); 7166 ImGui::Text("An extra line here."); 7167 ImGui::NextColumn(); 7168 7169 ImGui::Text("Sailor"); 7170 ImGui::Button("Corniflower"); 7171 static float bar = 1.0f; 7172 ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f"); 7173 ImGui::NextColumn(); 7174 7175 if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); 7176 if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); 7177 if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); 7178 ImGui::Columns(1); 7179 ImGui::Separator(); 7180 ImGui::TreePop(); 7181 } 7182 7183 // Word wrapping 7184 IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping"); 7185 if (ImGui::TreeNode("Word-wrapping")) 7186 { 7187 ImGui::Columns(2, "word-wrapping"); 7188 ImGui::Separator(); 7189 ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); 7190 ImGui::TextWrapped("Hello Left"); 7191 ImGui::NextColumn(); 7192 ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); 7193 ImGui::TextWrapped("Hello Right"); 7194 ImGui::Columns(1); 7195 ImGui::Separator(); 7196 ImGui::TreePop(); 7197 } 7198 7199 IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling"); 7200 if (ImGui::TreeNode("Horizontal Scrolling")) 7201 { 7202 ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); 7203 ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f); 7204 ImGui::BeginChild("##ScrollingRegion", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar); 7205 ImGui::Columns(10); 7206 7207 // Also demonstrate using clipper for large vertical lists 7208 int ITEMS_COUNT = 2000; 7209 ImGuiListClipper clipper; 7210 clipper.Begin(ITEMS_COUNT); 7211 while (clipper.Step()) 7212 { 7213 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) 7214 for (int j = 0; j < 10; j++) 7215 { 7216 ImGui::Text("Line %d Column %d...", i, j); 7217 ImGui::NextColumn(); 7218 } 7219 } 7220 ImGui::Columns(1); 7221 ImGui::EndChild(); 7222 ImGui::TreePop(); 7223 } 7224 7225 IMGUI_DEMO_MARKER("Columns (legacy API)/Tree"); 7226 if (ImGui::TreeNode("Tree")) 7227 { 7228 ImGui::Columns(2, "tree", true); 7229 for (int x = 0; x < 3; x++) 7230 { 7231 bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x); 7232 ImGui::NextColumn(); 7233 ImGui::Text("Node contents"); 7234 ImGui::NextColumn(); 7235 if (open1) 7236 { 7237 for (int y = 0; y < 3; y++) 7238 { 7239 bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y); 7240 ImGui::NextColumn(); 7241 ImGui::Text("Node contents"); 7242 if (open2) 7243 { 7244 ImGui::Text("Even more contents"); 7245 if (ImGui::TreeNode("Tree in column")) 7246 { 7247 ImGui::Text("The quick brown fox jumps over the lazy dog"); 7248 ImGui::TreePop(); 7249 } 7250 } 7251 ImGui::NextColumn(); 7252 if (open2) 7253 ImGui::TreePop(); 7254 } 7255 ImGui::TreePop(); 7256 } 7257 } 7258 ImGui::Columns(1); 7259 ImGui::TreePop(); 7260 } 7261 7262 ImGui::TreePop(); 7263 } 7264 7265 //----------------------------------------------------------------------------- 7266 // [SECTION] ShowDemoWindowInputs() 7267 //----------------------------------------------------------------------------- 7268 7269 static void ShowDemoWindowInputs() 7270 { 7271 IMGUI_DEMO_MARKER("Inputs & Focus"); 7272 if (ImGui::CollapsingHeader("Inputs & Focus")) 7273 { 7274 ImGuiIO& io = ImGui::GetIO(); 7275 7276 // Display inputs submitted to ImGuiIO 7277 IMGUI_DEMO_MARKER("Inputs & Focus/Inputs"); 7278 ImGui::SetNextItemOpen(true, ImGuiCond_Once); 7279 bool inputs_opened = ImGui::TreeNode("Inputs"); 7280 ImGui::SameLine(); 7281 HelpMarker( 7282 "This is a simplified view. See more detailed input state:\n" 7283 "- in 'Tools->Metrics/Debugger->Inputs'.\n" 7284 "- in 'Tools->Debug Log->IO'."); 7285 if (inputs_opened) 7286 { 7287 if (ImGui::IsMousePosValid()) 7288 ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); 7289 else 7290 ImGui::Text("Mouse pos: <INVALID>"); 7291 ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); 7292 ImGui::Text("Mouse down:"); 7293 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } 7294 ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); 7295 7296 // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows 7297 // displaying the data for old/new backends. 7298 // User code should never have to go through such hoops! 7299 // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END. 7300 #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO 7301 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; 7302 ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN; 7303 #else 7304 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array 7305 ImGuiKey start_key = (ImGuiKey)0; 7306 #endif 7307 ImGui::Text("Keys down:"); for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); } 7308 ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); 7309 ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. 7310 7311 ImGui::TreePop(); 7312 } 7313 7314 // Display ImGuiIO output flags 7315 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs"); 7316 ImGui::SetNextItemOpen(true, ImGuiCond_Once); 7317 bool outputs_opened = ImGui::TreeNode("Outputs"); 7318 ImGui::SameLine(); 7319 HelpMarker( 7320 "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " 7321 "to instruct your application of how to route inputs. Typically, when a value is true, it means " 7322 "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" 7323 "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " 7324 "and underlying application should ignore mouse inputs (in practice there are many and more subtle " 7325 "rules leading to how those flags are set)."); 7326 if (outputs_opened) 7327 { 7328 ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse); 7329 ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); 7330 ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard); 7331 ImGui::Text("io.WantTextInput: %d", io.WantTextInput); 7332 ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos); 7333 ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible); 7334 7335 IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override"); 7336 if (ImGui::TreeNode("WantCapture override")) 7337 { 7338 HelpMarker( 7339 "Hovering the colored canvas will override io.WantCaptureXXX fields.\n" 7340 "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering " 7341 "and true when clicking."); 7342 static int capture_override_mouse = -1; 7343 static int capture_override_keyboard = -1; 7344 const char* capture_override_desc[] = { "None", "Set to false", "Set to true" }; 7345 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); 7346 ImGui::SliderInt("SetNextFrameWantCaptureMouse() on hover", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp); 7347 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15); 7348 ImGui::SliderInt("SetNextFrameWantCaptureKeyboard() on hover", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp); 7349 7350 ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item 7351 if (ImGui::IsItemHovered() && capture_override_mouse != -1) 7352 ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1); 7353 if (ImGui::IsItemHovered() && capture_override_keyboard != -1) 7354 ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1); 7355 7356 ImGui::TreePop(); 7357 } 7358 ImGui::TreePop(); 7359 } 7360 7361 // Demonstrate using Shortcut() and Routing Policies. 7362 // The general flow is: 7363 // - Code interested in a chord (e.g. "Ctrl+A") declares their intent. 7364 // - Multiple locations may be interested in same chord! Routing helps find a winner. 7365 // - Every frame, we resolve all claims and assign one owner if the modifiers are matching. 7366 // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route. 7367 // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags. 7368 // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused. 7369 // TL;DR: Most uses will simply be: 7370 // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy. 7371 IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts"); 7372 if (ImGui::TreeNode("Shortcuts")) 7373 { 7374 static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat; 7375 static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused; 7376 ImGui::CheckboxFlags("ImGuiInputFlags_Repeat", &route_options, ImGuiInputFlags_Repeat); 7377 ImGui::RadioButton("ImGuiInputFlags_RouteActive", &route_type, ImGuiInputFlags_RouteActive); 7378 ImGui::RadioButton("ImGuiInputFlags_RouteFocused (default)", &route_type, ImGuiInputFlags_RouteFocused); 7379 ImGui::RadioButton("ImGuiInputFlags_RouteGlobal", &route_type, ImGuiInputFlags_RouteGlobal); 7380 ImGui::Indent(); 7381 ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal); 7382 ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverFocused", &route_options, ImGuiInputFlags_RouteOverFocused); 7383 ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive", &route_options, ImGuiInputFlags_RouteOverActive); 7384 ImGui::CheckboxFlags("ImGuiInputFlags_RouteUnlessBgFocused", &route_options, ImGuiInputFlags_RouteUnlessBgFocused); 7385 ImGui::EndDisabled(); 7386 ImGui::Unindent(); 7387 ImGui::RadioButton("ImGuiInputFlags_RouteAlways", &route_type, ImGuiInputFlags_RouteAlways); 7388 ImGuiInputFlags flags = route_type | route_options; // Merged flags 7389 if (route_type != ImGuiInputFlags_RouteGlobal) 7390 flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused); 7391 7392 ImGui::SeparatorText("Using SetNextItemShortcut()"); 7393 ImGui::Text("Ctrl+S"); 7394 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, flags | ImGuiInputFlags_Tooltip); 7395 ImGui::Button("Save"); 7396 ImGui::Text("Alt+F"); 7397 ImGui::SetNextItemShortcut(ImGuiMod_Alt | ImGuiKey_F, flags | ImGuiInputFlags_Tooltip); 7398 static float f = 0.5f; 7399 ImGui::SliderFloat("Factor", &f, 0.0f, 1.0f); 7400 7401 ImGui::SeparatorText("Using Shortcut()"); 7402 const float line_height = ImGui::GetTextLineHeightWithSpacing(); 7403 const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A; 7404 7405 ImGui::Text("Ctrl+A"); 7406 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); 7407 7408 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f)); 7409 7410 ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true); 7411 ImGui::Text("Press CTRL+A and see who receives it!"); 7412 ImGui::Separator(); 7413 7414 // 1: Window polling for CTRL+A 7415 ImGui::Text("(in WindowA)"); 7416 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); 7417 7418 // 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active) 7419 // (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h) 7420 //char str[16] = "Press CTRL+A"; 7421 //ImGui::Spacing(); 7422 //ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); 7423 //ImGuiID item_id = ImGui::GetItemID(); 7424 //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused"); 7425 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "..."); 7426 7427 // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA 7428 ImGui::BeginChild("ChildD", ImVec2(-FLT_MIN, line_height * 4), true); 7429 ImGui::Text("(in ChildD: not using same Shortcut)"); 7430 ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused()); 7431 ImGui::EndChild(); 7432 7433 // 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused. 7434 ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true); 7435 ImGui::Text("(in ChildE: using same Shortcut)"); 7436 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); 7437 ImGui::EndChild(); 7438 7439 // 5: In a popup 7440 if (ImGui::Button("Open Popup")) 7441 ImGui::OpenPopup("PopupF"); 7442 if (ImGui::BeginPopup("PopupF")) 7443 { 7444 ImGui::Text("(in PopupF)"); 7445 ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); 7446 // (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h) 7447 //ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); 7448 //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "..."); 7449 ImGui::EndPopup(); 7450 } 7451 ImGui::EndChild(); 7452 ImGui::PopStyleColor(); 7453 7454 ImGui::TreePop(); 7455 } 7456 7457 // Display mouse cursors 7458 IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors"); 7459 if (ImGui::TreeNode("Mouse Cursors")) 7460 { 7461 const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed" }; 7462 IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT); 7463 7464 ImGuiMouseCursor current = ImGui::GetMouseCursor(); 7465 ImGui::Text("Current mouse cursor = %d: %s", current, mouse_cursors_names[current]); 7466 ImGui::BeginDisabled(true); 7467 ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); 7468 ImGui::EndDisabled(); 7469 7470 ImGui::Text("Hover to see mouse cursors:"); 7471 ImGui::SameLine(); HelpMarker( 7472 "Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. " 7473 "If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, " 7474 "otherwise your backend needs to handle it."); 7475 for (int i = 0; i < ImGuiMouseCursor_COUNT; i++) 7476 { 7477 char label[32]; 7478 sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]); 7479 ImGui::Bullet(); ImGui::Selectable(label, false); 7480 if (ImGui::IsItemHovered()) 7481 ImGui::SetMouseCursor(i); 7482 } 7483 ImGui::TreePop(); 7484 } 7485 7486 IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing"); 7487 if (ImGui::TreeNode("Tabbing")) 7488 { 7489 ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); 7490 static char buf[32] = "hello"; 7491 ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); 7492 ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); 7493 ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); 7494 ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); 7495 ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); 7496 ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); 7497 ImGui::PopItemFlag(); 7498 ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); 7499 ImGui::TreePop(); 7500 } 7501 7502 IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code"); 7503 if (ImGui::TreeNode("Focus from code")) 7504 { 7505 bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); 7506 bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine(); 7507 bool focus_3 = ImGui::Button("Focus on 3"); 7508 int has_focus = 0; 7509 static char buf[128] = "click on a button to set focus"; 7510 7511 if (focus_1) ImGui::SetKeyboardFocusHere(); 7512 ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); 7513 if (ImGui::IsItemActive()) has_focus = 1; 7514 7515 if (focus_2) ImGui::SetKeyboardFocusHere(); 7516 ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); 7517 if (ImGui::IsItemActive()) has_focus = 2; 7518 7519 ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); 7520 if (focus_3) ImGui::SetKeyboardFocusHere(); 7521 ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); 7522 if (ImGui::IsItemActive()) has_focus = 3; 7523 ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); 7524 ImGui::PopItemFlag(); 7525 7526 if (has_focus) 7527 ImGui::Text("Item with focus: %d", has_focus); 7528 else 7529 ImGui::Text("Item with focus: <none>"); 7530 7531 // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item 7532 static float f3[3] = { 0.0f, 0.0f, 0.0f }; 7533 int focus_ahead = -1; 7534 if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine(); 7535 if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine(); 7536 if (ImGui::Button("Focus on Z")) { focus_ahead = 2; } 7537 if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); 7538 ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); 7539 7540 ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code."); 7541 ImGui::TreePop(); 7542 } 7543 7544 IMGUI_DEMO_MARKER("Inputs & Focus/Dragging"); 7545 if (ImGui::TreeNode("Dragging")) 7546 { 7547 ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); 7548 for (int button = 0; button < 3; button++) 7549 { 7550 ImGui::Text("IsMouseDragging(%d):", button); 7551 ImGui::Text(" w/ default threshold: %d,", ImGui::IsMouseDragging(button)); 7552 ImGui::Text(" w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f)); 7553 ImGui::Text(" w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f)); 7554 } 7555 7556 ImGui::Button("Drag Me"); 7557 if (ImGui::IsItemActive()) 7558 ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor 7559 7560 // Drag operations gets "unlocked" when the mouse has moved past a certain threshold 7561 // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher 7562 // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta(). 7563 ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); 7564 ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); 7565 ImVec2 mouse_delta = io.MouseDelta; 7566 ImGui::Text("GetMouseDragDelta(0):"); 7567 ImGui::Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y); 7568 ImGui::Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y); 7569 ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y); 7570 ImGui::TreePop(); 7571 } 7572 } 7573 } 7574 7575 //----------------------------------------------------------------------------- 7576 // [SECTION] About Window / ShowAboutWindow() 7577 // Access from Dear ImGui Demo -> Tools -> About 7578 //----------------------------------------------------------------------------- 7579 7580 void ImGui::ShowAboutWindow(bool* p_open) 7581 { 7582 if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize)) 7583 { 7584 ImGui::End(); 7585 return; 7586 } 7587 IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); 7588 ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); 7589 7590 ImGui::TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui"); 7591 ImGui::SameLine(); 7592 ImGui::TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md"); 7593 ImGui::SameLine(); 7594 ImGui::TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki"); 7595 ImGui::SameLine(); 7596 ImGui::TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases"); 7597 ImGui::SameLine(); 7598 ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding"); 7599 7600 ImGui::Separator(); 7601 ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); 7602 ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); 7603 ImGui::Text("If your company uses this, please consider funding the project."); 7604 7605 static bool show_config_info = false; 7606 ImGui::Checkbox("Config/Build Information", &show_config_info); 7607 if (show_config_info) 7608 { 7609 ImGuiIO& io = ImGui::GetIO(); 7610 ImGuiStyle& style = ImGui::GetStyle(); 7611 7612 bool copy_to_clipboard = ImGui::Button("Copy to clipboard"); 7613 ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18); 7614 ImGui::BeginChild(ImGui::GetID("cfg_infos"), child_size, ImGuiChildFlags_FrameStyle); 7615 if (copy_to_clipboard) 7616 { 7617 ImGui::LogToClipboard(); 7618 ImGui::LogText("```\n"); // Back quotes will make text appears without formatting when pasting on GitHub 7619 } 7620 7621 ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); 7622 ImGui::Separator(); 7623 ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert)); 7624 ImGui::Text("define: __cplusplus=%d", (int)__cplusplus); 7625 #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 7626 ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS"); 7627 #endif 7628 #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO 7629 ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_KEYIO"); 7630 #endif 7631 #ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS 7632 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS"); 7633 #endif 7634 #ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS 7635 ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS"); 7636 #endif 7637 #ifdef IMGUI_DISABLE_WIN32_FUNCTIONS 7638 ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS"); 7639 #endif 7640 #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 7641 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS"); 7642 #endif 7643 #ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS 7644 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS"); 7645 #endif 7646 #ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS 7647 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS"); 7648 #endif 7649 #ifdef IMGUI_DISABLE_FILE_FUNCTIONS 7650 ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS"); 7651 #endif 7652 #ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS 7653 ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS"); 7654 #endif 7655 #ifdef IMGUI_USE_BGRA_PACKED_COLOR 7656 ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR"); 7657 #endif 7658 #ifdef _WIN32 7659 ImGui::Text("define: _WIN32"); 7660 #endif 7661 #ifdef _WIN64 7662 ImGui::Text("define: _WIN64"); 7663 #endif 7664 #ifdef __linux__ 7665 ImGui::Text("define: __linux__"); 7666 #endif 7667 #ifdef __APPLE__ 7668 ImGui::Text("define: __APPLE__"); 7669 #endif 7670 #ifdef _MSC_VER 7671 ImGui::Text("define: _MSC_VER=%d", _MSC_VER); 7672 #endif 7673 #ifdef _MSVC_LANG 7674 ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG); 7675 #endif 7676 #ifdef __MINGW32__ 7677 ImGui::Text("define: __MINGW32__"); 7678 #endif 7679 #ifdef __MINGW64__ 7680 ImGui::Text("define: __MINGW64__"); 7681 #endif 7682 #ifdef __GNUC__ 7683 ImGui::Text("define: __GNUC__=%d", (int)__GNUC__); 7684 #endif 7685 #ifdef __clang_version__ 7686 ImGui::Text("define: __clang_version__=%s", __clang_version__); 7687 #endif 7688 #ifdef __EMSCRIPTEN__ 7689 ImGui::Text("define: __EMSCRIPTEN__"); 7690 #endif 7691 ImGui::Separator(); 7692 ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); 7693 ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL"); 7694 ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags); 7695 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard"); 7696 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad"); 7697 if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) ImGui::Text(" NavEnableSetMousePos"); 7698 if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); 7699 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); 7700 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); 7701 if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); 7702 if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); 7703 if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); 7704 if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges"); 7705 if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly"); 7706 if (io.ConfigMemoryCompactTimer >= 0.0f) ImGui::Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer); 7707 ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags); 7708 if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); 7709 if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); 7710 if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); 7711 if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset"); 7712 ImGui::Separator(); 7713 ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); 7714 ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); 7715 ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); 7716 ImGui::Separator(); 7717 ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y); 7718 ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize); 7719 ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y); 7720 ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding); 7721 ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize); 7722 ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y); 7723 ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y); 7724 7725 if (copy_to_clipboard) 7726 { 7727 ImGui::LogText("\n```\n"); 7728 ImGui::LogFinish(); 7729 } 7730 ImGui::EndChild(); 7731 } 7732 ImGui::End(); 7733 } 7734 7735 //----------------------------------------------------------------------------- 7736 // [SECTION] Style Editor / ShowStyleEditor() 7737 //----------------------------------------------------------------------------- 7738 // - ShowFontSelector() 7739 // - ShowStyleSelector() 7740 // - ShowStyleEditor() 7741 //----------------------------------------------------------------------------- 7742 7743 // Forward declare ShowFontAtlas() which isn't worth putting in public API yet 7744 namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); } 7745 7746 // Demo helper function to select among loaded fonts. 7747 // Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one. 7748 void ImGui::ShowFontSelector(const char* label) 7749 { 7750 ImGuiIO& io = ImGui::GetIO(); 7751 ImFont* font_current = ImGui::GetFont(); 7752 if (ImGui::BeginCombo(label, font_current->GetDebugName())) 7753 { 7754 for (ImFont* font : io.Fonts->Fonts) 7755 { 7756 ImGui::PushID((void*)font); 7757 if (ImGui::Selectable(font->GetDebugName(), font == font_current)) 7758 io.FontDefault = font; 7759 ImGui::PopID(); 7760 } 7761 ImGui::EndCombo(); 7762 } 7763 ImGui::SameLine(); 7764 HelpMarker( 7765 "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n" 7766 "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" 7767 "- Read FAQ and docs/FONTS.md for more details.\n" 7768 "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); 7769 } 7770 7771 // Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. 7772 // Here we use the simplified Combo() api that packs items into a single literal string. 7773 // Useful for quick combo boxes where the choices are known locally. 7774 bool ImGui::ShowStyleSelector(const char* label) 7775 { 7776 static int style_idx = -1; 7777 if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) 7778 { 7779 switch (style_idx) 7780 { 7781 case 0: ImGui::StyleColorsDark(); break; 7782 case 1: ImGui::StyleColorsLight(); break; 7783 case 2: ImGui::StyleColorsClassic(); break; 7784 } 7785 return true; 7786 } 7787 return false; 7788 } 7789 7790 void ImGui::ShowStyleEditor(ImGuiStyle* ref) 7791 { 7792 IMGUI_DEMO_MARKER("Tools/Style Editor"); 7793 // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to 7794 // (without a reference style pointer, we will use one compared locally as a reference) 7795 ImGuiStyle& style = ImGui::GetStyle(); 7796 static ImGuiStyle ref_saved_style; 7797 7798 // Default to using internal storage as reference 7799 static bool init = true; 7800 if (init && ref == NULL) 7801 ref_saved_style = style; 7802 init = false; 7803 if (ref == NULL) 7804 ref = &ref_saved_style; 7805 7806 ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f); 7807 7808 if (ImGui::ShowStyleSelector("Colors##Selector")) 7809 ref_saved_style = style; 7810 ImGui::ShowFontSelector("Fonts##Selector"); 7811 7812 // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f) 7813 if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f")) 7814 style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding 7815 { bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } } 7816 ImGui::SameLine(); 7817 { bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } } 7818 ImGui::SameLine(); 7819 { bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } } 7820 7821 // Save/Revert button 7822 if (ImGui::Button("Save Ref")) 7823 *ref = ref_saved_style = style; 7824 ImGui::SameLine(); 7825 if (ImGui::Button("Revert Ref")) 7826 style = *ref; 7827 ImGui::SameLine(); 7828 HelpMarker( 7829 "Save/Revert in local non-persistent storage. Default Colors definition are not affected. " 7830 "Use \"Export\" below to save them somewhere."); 7831 7832 ImGui::Separator(); 7833 7834 if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) 7835 { 7836 if (ImGui::BeginTabItem("Sizes")) 7837 { 7838 ImGui::SeparatorText("Main"); 7839 ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); 7840 ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); 7841 ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); 7842 ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); 7843 ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); 7844 ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); 7845 ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); 7846 ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); 7847 7848 ImGui::SeparatorText("Borders"); 7849 ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); 7850 ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); 7851 ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); 7852 ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); 7853 ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); 7854 ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f"); 7855 ImGui::SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 2.0f, "%.0f"); 7856 ImGui::SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set."); 7857 7858 ImGui::SeparatorText("Rounding"); 7859 ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); 7860 ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f"); 7861 ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); 7862 ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); 7863 ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); 7864 ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); 7865 ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); 7866 7867 ImGui::SeparatorText("Tables"); 7868 ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); 7869 ImGui::SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f); 7870 ImGui::SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f"); 7871 7872 ImGui::SeparatorText("Widgets"); 7873 ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); 7874 int window_menu_button_position = style.WindowMenuButtonPosition + 1; 7875 if (ImGui::Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0")) 7876 style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1); 7877 ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0"); 7878 ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); 7879 ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); 7880 ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); 7881 ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); 7882 ImGui::SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f"); 7883 ImGui::SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f"); 7884 ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); 7885 ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); 7886 7887 ImGui::SeparatorText("Tooltips"); 7888 for (int n = 0; n < 2; n++) 7889 if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav")) 7890 { 7891 ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav; 7892 ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone); 7893 ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort); 7894 ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal); 7895 ImGui::CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary); 7896 ImGui::CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay); 7897 ImGui::TreePop(); 7898 } 7899 7900 ImGui::SeparatorText("Misc"); 7901 ImGui::SliderFloat2("DisplayWindowPadding", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen."); 7902 ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); 7903 7904 ImGui::EndTabItem(); 7905 } 7906 7907 if (ImGui::BeginTabItem("Colors")) 7908 { 7909 static int output_dest = 0; 7910 static bool output_only_modified = true; 7911 if (ImGui::Button("Export")) 7912 { 7913 if (output_dest == 0) 7914 ImGui::LogToClipboard(); 7915 else 7916 ImGui::LogToTTY(); 7917 ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE); 7918 for (int i = 0; i < ImGuiCol_COUNT; i++) 7919 { 7920 const ImVec4& col = style.Colors[i]; 7921 const char* name = ImGui::GetStyleColorName(i); 7922 if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) 7923 ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, 7924 name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w); 7925 } 7926 ImGui::LogFinish(); 7927 } 7928 ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); 7929 ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); 7930 7931 static ImGuiTextFilter filter; 7932 filter.Draw("Filter colors", ImGui::GetFontSize() * 16); 7933 7934 static ImGuiColorEditFlags alpha_flags = 0; 7935 if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); 7936 if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); 7937 if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); 7938 HelpMarker( 7939 "In the color list:\n" 7940 "Left-click on color square to open color picker,\n" 7941 "Right-click to open edit options menu."); 7942 7943 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX)); 7944 ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); 7945 ImGui::PushItemWidth(ImGui::GetFontSize() * -12); 7946 for (int i = 0; i < ImGuiCol_COUNT; i++) 7947 { 7948 const char* name = ImGui::GetStyleColorName(i); 7949 if (!filter.PassFilter(name)) 7950 continue; 7951 ImGui::PushID(i); 7952 #ifndef IMGUI_DISABLE_DEBUG_TOOLS 7953 if (ImGui::Button("?")) 7954 ImGui::DebugFlashStyleColor((ImGuiCol)i); 7955 ImGui::SetItemTooltip("Flash given color to identify places where it is used."); 7956 ImGui::SameLine(); 7957 #endif 7958 ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); 7959 if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) 7960 { 7961 // Tips: in a real user application, you may want to merge and use an icon font into the main font, 7962 // so instead of "Save"/"Revert" you'd use icons! 7963 // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient! 7964 ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; } 7965 ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; } 7966 } 7967 ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); 7968 ImGui::TextUnformatted(name); 7969 ImGui::PopID(); 7970 } 7971 ImGui::PopItemWidth(); 7972 ImGui::EndChild(); 7973 7974 ImGui::EndTabItem(); 7975 } 7976 7977 if (ImGui::BeginTabItem("Fonts")) 7978 { 7979 ImGuiIO& io = ImGui::GetIO(); 7980 ImFontAtlas* atlas = io.Fonts; 7981 HelpMarker("Read FAQ and docs/FONTS.md for details on font loading."); 7982 ImGui::ShowFontAtlas(atlas); 7983 7984 // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below. 7985 // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds). 7986 const float MIN_SCALE = 0.3f; 7987 const float MAX_SCALE = 2.0f; 7988 HelpMarker( 7989 "Those are old settings provided for convenience.\n" 7990 "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, " 7991 "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n" 7992 "Using those settings here will give you poor quality results."); 7993 static float window_scale = 1.0f; 7994 ImGui::PushItemWidth(ImGui::GetFontSize() * 8); 7995 if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window 7996 ImGui::SetWindowFontScale(window_scale); 7997 ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything 7998 ImGui::PopItemWidth(); 7999 8000 ImGui::EndTabItem(); 8001 } 8002 8003 if (ImGui::BeginTabItem("Rendering")) 8004 { 8005 ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); 8006 ImGui::SameLine(); 8007 HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); 8008 8009 ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex); 8010 ImGui::SameLine(); 8011 HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering)."); 8012 8013 ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); 8014 ImGui::PushItemWidth(ImGui::GetFontSize() * 8); 8015 ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); 8016 if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; 8017 8018 // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles. 8019 ImGui::DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp); 8020 const bool show_samples = ImGui::IsItemActive(); 8021 if (show_samples) 8022 ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos()); 8023 if (show_samples && ImGui::BeginTooltip()) 8024 { 8025 ImGui::TextUnformatted("(R = radius, N = approx number of segments)"); 8026 ImGui::Spacing(); 8027 ImDrawList* draw_list = ImGui::GetWindowDrawList(); 8028 const float min_widget_width = ImGui::CalcTextSize("R: MMM\nN: MMM").x; 8029 for (int n = 0; n < 8; n++) 8030 { 8031 const float RAD_MIN = 5.0f; 8032 const float RAD_MAX = 70.0f; 8033 const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f); 8034 8035 ImGui::BeginGroup(); 8036 8037 // N is not always exact here due to how PathArcTo() function work internally 8038 ImGui::Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad)); 8039 8040 const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f); 8041 const float offset_x = floorf(canvas_width * 0.5f); 8042 const float offset_y = floorf(RAD_MAX); 8043 8044 const ImVec2 p1 = ImGui::GetCursorScreenPos(); 8045 draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text)); 8046 ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2)); 8047 8048 /* 8049 const ImVec2 p2 = ImGui::GetCursorScreenPos(); 8050 draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text)); 8051 ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2)); 8052 */ 8053 8054 ImGui::EndGroup(); 8055 ImGui::SameLine(); 8056 } 8057 ImGui::EndTooltip(); 8058 } 8059 ImGui::SameLine(); 8060 HelpMarker("When drawing circle primitives with \"num_segments == 0\" tesselation will be calculated automatically."); 8061 8062 ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. 8063 ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha)."); 8064 ImGui::PopItemWidth(); 8065 8066 ImGui::EndTabItem(); 8067 } 8068 8069 ImGui::EndTabBar(); 8070 } 8071 8072 ImGui::PopItemWidth(); 8073 } 8074 8075 //----------------------------------------------------------------------------- 8076 // [SECTION] User Guide / ShowUserGuide() 8077 //----------------------------------------------------------------------------- 8078 8079 void ImGui::ShowUserGuide() 8080 { 8081 ImGuiIO& io = ImGui::GetIO(); 8082 ImGui::BulletText("Double-click on title bar to collapse window."); 8083 ImGui::BulletText( 8084 "Click and drag on lower corner to resize window\n" 8085 "(double-click to auto fit window to its contents)."); 8086 ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); 8087 ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); 8088 ImGui::BulletText("CTRL+Tab to select a window."); 8089 if (io.FontAllowUserScaling) 8090 ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); 8091 ImGui::BulletText("While inputing text:\n"); 8092 ImGui::Indent(); 8093 ImGui::BulletText("CTRL+Left/Right to word jump."); 8094 ImGui::BulletText("CTRL+A or double-click to select all."); 8095 ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste."); 8096 ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo."); 8097 ImGui::BulletText("ESCAPE to revert."); 8098 ImGui::Unindent(); 8099 ImGui::BulletText("With keyboard navigation enabled:"); 8100 ImGui::Indent(); 8101 ImGui::BulletText("Arrow keys to navigate."); 8102 ImGui::BulletText("Space to activate a widget."); 8103 ImGui::BulletText("Return to input text into a widget."); 8104 ImGui::BulletText("Escape to deactivate a widget, close popup, exit child window."); 8105 ImGui::BulletText("Alt to jump to the menu layer of a window."); 8106 ImGui::Unindent(); 8107 } 8108 8109 //----------------------------------------------------------------------------- 8110 // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() 8111 //----------------------------------------------------------------------------- 8112 // - ShowExampleAppMainMenuBar() 8113 // - ShowExampleMenuFile() 8114 //----------------------------------------------------------------------------- 8115 8116 // Demonstrate creating a "main" fullscreen menu bar and populating it. 8117 // Note the difference between BeginMainMenuBar() and BeginMenuBar(): 8118 // - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!) 8119 // - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it. 8120 static void ShowExampleAppMainMenuBar() 8121 { 8122 if (ImGui::BeginMainMenuBar()) 8123 { 8124 if (ImGui::BeginMenu("File")) 8125 { 8126 ShowExampleMenuFile(); 8127 ImGui::EndMenu(); 8128 } 8129 if (ImGui::BeginMenu("Edit")) 8130 { 8131 if (ImGui::MenuItem("Undo", "CTRL+Z")) {} 8132 if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item 8133 ImGui::Separator(); 8134 if (ImGui::MenuItem("Cut", "CTRL+X")) {} 8135 if (ImGui::MenuItem("Copy", "CTRL+C")) {} 8136 if (ImGui::MenuItem("Paste", "CTRL+V")) {} 8137 ImGui::EndMenu(); 8138 } 8139 ImGui::EndMainMenuBar(); 8140 } 8141 } 8142 8143 // Note that shortcuts are currently provided for display only 8144 // (future version will add explicit flags to BeginMenu() to request processing shortcuts) 8145 static void ShowExampleMenuFile() 8146 { 8147 IMGUI_DEMO_MARKER("Examples/Menu"); 8148 ImGui::MenuItem("(demo menu)", NULL, false, false); 8149 if (ImGui::MenuItem("New")) {} 8150 if (ImGui::MenuItem("Open", "Ctrl+O")) {} 8151 if (ImGui::BeginMenu("Open Recent")) 8152 { 8153 ImGui::MenuItem("fish_hat.c"); 8154 ImGui::MenuItem("fish_hat.inl"); 8155 ImGui::MenuItem("fish_hat.h"); 8156 if (ImGui::BeginMenu("More..")) 8157 { 8158 ImGui::MenuItem("Hello"); 8159 ImGui::MenuItem("Sailor"); 8160 if (ImGui::BeginMenu("Recurse..")) 8161 { 8162 ShowExampleMenuFile(); 8163 ImGui::EndMenu(); 8164 } 8165 ImGui::EndMenu(); 8166 } 8167 ImGui::EndMenu(); 8168 } 8169 if (ImGui::MenuItem("Save", "Ctrl+S")) {} 8170 if (ImGui::MenuItem("Save As..")) {} 8171 8172 ImGui::Separator(); 8173 IMGUI_DEMO_MARKER("Examples/Menu/Options"); 8174 if (ImGui::BeginMenu("Options")) 8175 { 8176 static bool enabled = true; 8177 ImGui::MenuItem("Enabled", "", &enabled); 8178 ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Border); 8179 for (int i = 0; i < 10; i++) 8180 ImGui::Text("Scrolling Text %d", i); 8181 ImGui::EndChild(); 8182 static float f = 0.5f; 8183 static int n = 0; 8184 ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); 8185 ImGui::InputFloat("Input", &f, 0.1f); 8186 ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); 8187 ImGui::EndMenu(); 8188 } 8189 8190 IMGUI_DEMO_MARKER("Examples/Menu/Colors"); 8191 if (ImGui::BeginMenu("Colors")) 8192 { 8193 float sz = ImGui::GetTextLineHeight(); 8194 for (int i = 0; i < ImGuiCol_COUNT; i++) 8195 { 8196 const char* name = ImGui::GetStyleColorName((ImGuiCol)i); 8197 ImVec2 p = ImGui::GetCursorScreenPos(); 8198 ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i)); 8199 ImGui::Dummy(ImVec2(sz, sz)); 8200 ImGui::SameLine(); 8201 ImGui::MenuItem(name); 8202 } 8203 ImGui::EndMenu(); 8204 } 8205 8206 // Here we demonstrate appending again to the "Options" menu (which we already created above) 8207 // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice. 8208 // In a real code-base using it would make senses to use this feature from very different code locations. 8209 if (ImGui::BeginMenu("Options")) // <-- Append! 8210 { 8211 IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu"); 8212 static bool b = true; 8213 ImGui::Checkbox("SomeOption", &b); 8214 ImGui::EndMenu(); 8215 } 8216 8217 if (ImGui::BeginMenu("Disabled", false)) // Disabled 8218 { 8219 IM_ASSERT(0); 8220 } 8221 if (ImGui::MenuItem("Checked", NULL, true)) {} 8222 ImGui::Separator(); 8223 if (ImGui::MenuItem("Quit", "Alt+F4")) {} 8224 } 8225 8226 //----------------------------------------------------------------------------- 8227 // [SECTION] Example App: Debug Console / ShowExampleAppConsole() 8228 //----------------------------------------------------------------------------- 8229 8230 // Demonstrate creating a simple console window, with scrolling, filtering, completion and history. 8231 // For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions. 8232 struct ExampleAppConsole 8233 { 8234 char InputBuf[256]; 8235 ImVector<char*> Items; 8236 ImVector<const char*> Commands; 8237 ImVector<char*> History; 8238 int HistoryPos; // -1: new line, 0..History.Size-1 browsing history. 8239 ImGuiTextFilter Filter; 8240 bool AutoScroll; 8241 bool ScrollToBottom; 8242 8243 ExampleAppConsole() 8244 { 8245 IMGUI_DEMO_MARKER("Examples/Console"); 8246 ClearLog(); 8247 memset(InputBuf, 0, sizeof(InputBuf)); 8248 HistoryPos = -1; 8249 8250 // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches. 8251 Commands.push_back("HELP"); 8252 Commands.push_back("HISTORY"); 8253 Commands.push_back("CLEAR"); 8254 Commands.push_back("CLASSIFY"); 8255 AutoScroll = true; 8256 ScrollToBottom = false; 8257 AddLog("Welcome to Dear ImGui!"); 8258 } 8259 ~ExampleAppConsole() 8260 { 8261 ClearLog(); 8262 for (int i = 0; i < History.Size; i++) 8263 ImGui::MemFree(History[i]); 8264 } 8265 8266 // Portable helpers 8267 static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; } 8268 static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; } 8269 static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); } 8270 static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } 8271 8272 void ClearLog() 8273 { 8274 for (int i = 0; i < Items.Size; i++) 8275 ImGui::MemFree(Items[i]); 8276 Items.clear(); 8277 } 8278 8279 void AddLog(const char* fmt, ...) IM_FMTARGS(2) 8280 { 8281 // FIXME-OPT 8282 char buf[1024]; 8283 va_list args; 8284 va_start(args, fmt); 8285 vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); 8286 buf[IM_ARRAYSIZE(buf)-1] = 0; 8287 va_end(args); 8288 Items.push_back(Strdup(buf)); 8289 } 8290 8291 void Draw(const char* title, bool* p_open) 8292 { 8293 ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); 8294 if (!ImGui::Begin(title, p_open)) 8295 { 8296 ImGui::End(); 8297 return; 8298 } 8299 8300 // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar. 8301 // So e.g. IsItemHovered() will return true when hovering the title bar. 8302 // Here we create a context menu only available from the title bar. 8303 if (ImGui::BeginPopupContextItem()) 8304 { 8305 if (ImGui::MenuItem("Close Console")) 8306 *p_open = false; 8307 ImGui::EndPopup(); 8308 } 8309 8310 ImGui::TextWrapped( 8311 "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate " 8312 "implementation may want to store entries along with extra data such as timestamp, emitter, etc."); 8313 ImGui::TextWrapped("Enter 'HELP' for help."); 8314 8315 // TODO: display items starting from the bottom 8316 8317 if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } 8318 ImGui::SameLine(); 8319 if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); } 8320 ImGui::SameLine(); 8321 if (ImGui::SmallButton("Clear")) { ClearLog(); } 8322 ImGui::SameLine(); 8323 bool copy_to_clipboard = ImGui::SmallButton("Copy"); 8324 //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } 8325 8326 ImGui::Separator(); 8327 8328 // Options menu 8329 if (ImGui::BeginPopup("Options")) 8330 { 8331 ImGui::Checkbox("Auto-scroll", &AutoScroll); 8332 ImGui::EndPopup(); 8333 } 8334 8335 // Options, Filter 8336 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_O, ImGuiInputFlags_Tooltip); 8337 if (ImGui::Button("Options")) 8338 ImGui::OpenPopup("Options"); 8339 ImGui::SameLine(); 8340 Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); 8341 ImGui::Separator(); 8342 8343 // Reserve enough left-over height for 1 separator + 1 input text 8344 const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); 8345 if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar)) 8346 { 8347 if (ImGui::BeginPopupContextWindow()) 8348 { 8349 if (ImGui::Selectable("Clear")) ClearLog(); 8350 ImGui::EndPopup(); 8351 } 8352 8353 // Display every line as a separate entry so we can change their color or add custom widgets. 8354 // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end()); 8355 // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping 8356 // to only process visible items. The clipper will automatically measure the height of your first item and then 8357 // "seek" to display only items in the visible area. 8358 // To use the clipper we can replace your standard loop: 8359 // for (int i = 0; i < Items.Size; i++) 8360 // With: 8361 // ImGuiListClipper clipper; 8362 // clipper.Begin(Items.Size); 8363 // while (clipper.Step()) 8364 // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) 8365 // - That your items are evenly spaced (same height) 8366 // - That you have cheap random access to your elements (you can access them given their index, 8367 // without processing all the ones before) 8368 // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property. 8369 // We would need random-access on the post-filtered list. 8370 // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices 8371 // or offsets of items that passed the filtering test, recomputing this array when user changes the filter, 8372 // and appending newly elements as they are inserted. This is left as a task to the user until we can manage 8373 // to improve this example code! 8374 // If your items are of variable height: 8375 // - Split them into same height items would be simpler and facilitate random-seeking into your list. 8376 // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items. 8377 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing 8378 if (copy_to_clipboard) 8379 ImGui::LogToClipboard(); 8380 for (const char* item : Items) 8381 { 8382 if (!Filter.PassFilter(item)) 8383 continue; 8384 8385 // Normally you would store more information in your item than just a string. 8386 // (e.g. make Items[] an array of structure, store color/type etc.) 8387 ImVec4 color; 8388 bool has_color = false; 8389 if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; } 8390 else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; } 8391 if (has_color) 8392 ImGui::PushStyleColor(ImGuiCol_Text, color); 8393 ImGui::TextUnformatted(item); 8394 if (has_color) 8395 ImGui::PopStyleColor(); 8396 } 8397 if (copy_to_clipboard) 8398 ImGui::LogFinish(); 8399 8400 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame. 8401 // Using a scrollbar or mouse-wheel will take away from the bottom edge. 8402 if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) 8403 ImGui::SetScrollHereY(1.0f); 8404 ScrollToBottom = false; 8405 8406 ImGui::PopStyleVar(); 8407 } 8408 ImGui::EndChild(); 8409 ImGui::Separator(); 8410 8411 // Command-line 8412 bool reclaim_focus = false; 8413 ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; 8414 if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) 8415 { 8416 char* s = InputBuf; 8417 Strtrim(s); 8418 if (s[0]) 8419 ExecCommand(s); 8420 strcpy(s, ""); 8421 reclaim_focus = true; 8422 } 8423 8424 // Auto-focus on window apparition 8425 ImGui::SetItemDefaultFocus(); 8426 if (reclaim_focus) 8427 ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget 8428 8429 ImGui::End(); 8430 } 8431 8432 void ExecCommand(const char* command_line) 8433 { 8434 AddLog("# %s\n", command_line); 8435 8436 // Insert into history. First find match and delete it so it can be pushed to the back. 8437 // This isn't trying to be smart or optimal. 8438 HistoryPos = -1; 8439 for (int i = History.Size - 1; i >= 0; i--) 8440 if (Stricmp(History[i], command_line) == 0) 8441 { 8442 ImGui::MemFree(History[i]); 8443 History.erase(History.begin() + i); 8444 break; 8445 } 8446 History.push_back(Strdup(command_line)); 8447 8448 // Process command 8449 if (Stricmp(command_line, "CLEAR") == 0) 8450 { 8451 ClearLog(); 8452 } 8453 else if (Stricmp(command_line, "HELP") == 0) 8454 { 8455 AddLog("Commands:"); 8456 for (int i = 0; i < Commands.Size; i++) 8457 AddLog("- %s", Commands[i]); 8458 } 8459 else if (Stricmp(command_line, "HISTORY") == 0) 8460 { 8461 int first = History.Size - 10; 8462 for (int i = first > 0 ? first : 0; i < History.Size; i++) 8463 AddLog("%3d: %s\n", i, History[i]); 8464 } 8465 else 8466 { 8467 AddLog("Unknown command: '%s'\n", command_line); 8468 } 8469 8470 // On command input, we scroll to bottom even if AutoScroll==false 8471 ScrollToBottom = true; 8472 } 8473 8474 // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks 8475 static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) 8476 { 8477 ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; 8478 return console->TextEditCallback(data); 8479 } 8480 8481 int TextEditCallback(ImGuiInputTextCallbackData* data) 8482 { 8483 //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); 8484 switch (data->EventFlag) 8485 { 8486 case ImGuiInputTextFlags_CallbackCompletion: 8487 { 8488 // Example of TEXT COMPLETION 8489 8490 // Locate beginning of current word 8491 const char* word_end = data->Buf + data->CursorPos; 8492 const char* word_start = word_end; 8493 while (word_start > data->Buf) 8494 { 8495 const char c = word_start[-1]; 8496 if (c == ' ' || c == '\t' || c == ',' || c == ';') 8497 break; 8498 word_start--; 8499 } 8500 8501 // Build a list of candidates 8502 ImVector<const char*> candidates; 8503 for (int i = 0; i < Commands.Size; i++) 8504 if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0) 8505 candidates.push_back(Commands[i]); 8506 8507 if (candidates.Size == 0) 8508 { 8509 // No match 8510 AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start); 8511 } 8512 else if (candidates.Size == 1) 8513 { 8514 // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing. 8515 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start)); 8516 data->InsertChars(data->CursorPos, candidates[0]); 8517 data->InsertChars(data->CursorPos, " "); 8518 } 8519 else 8520 { 8521 // Multiple matches. Complete as much as we can.. 8522 // So inputing "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches. 8523 int match_len = (int)(word_end - word_start); 8524 for (;;) 8525 { 8526 int c = 0; 8527 bool all_candidates_matches = true; 8528 for (int i = 0; i < candidates.Size && all_candidates_matches; i++) 8529 if (i == 0) 8530 c = toupper(candidates[i][match_len]); 8531 else if (c == 0 || c != toupper(candidates[i][match_len])) 8532 all_candidates_matches = false; 8533 if (!all_candidates_matches) 8534 break; 8535 match_len++; 8536 } 8537 8538 if (match_len > 0) 8539 { 8540 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start)); 8541 data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len); 8542 } 8543 8544 // List matches 8545 AddLog("Possible matches:\n"); 8546 for (int i = 0; i < candidates.Size; i++) 8547 AddLog("- %s\n", candidates[i]); 8548 } 8549 8550 break; 8551 } 8552 case ImGuiInputTextFlags_CallbackHistory: 8553 { 8554 // Example of HISTORY 8555 const int prev_history_pos = HistoryPos; 8556 if (data->EventKey == ImGuiKey_UpArrow) 8557 { 8558 if (HistoryPos == -1) 8559 HistoryPos = History.Size - 1; 8560 else if (HistoryPos > 0) 8561 HistoryPos--; 8562 } 8563 else if (data->EventKey == ImGuiKey_DownArrow) 8564 { 8565 if (HistoryPos != -1) 8566 if (++HistoryPos >= History.Size) 8567 HistoryPos = -1; 8568 } 8569 8570 // A better implementation would preserve the data on the current input line along with cursor position. 8571 if (prev_history_pos != HistoryPos) 8572 { 8573 const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : ""; 8574 data->DeleteChars(0, data->BufTextLen); 8575 data->InsertChars(0, history_str); 8576 } 8577 } 8578 } 8579 return 0; 8580 } 8581 }; 8582 8583 static void ShowExampleAppConsole(bool* p_open) 8584 { 8585 static ExampleAppConsole console; 8586 console.Draw("Example: Console", p_open); 8587 } 8588 8589 //----------------------------------------------------------------------------- 8590 // [SECTION] Example App: Debug Log / ShowExampleAppLog() 8591 //----------------------------------------------------------------------------- 8592 8593 // Usage: 8594 // static ExampleAppLog my_log; 8595 // my_log.AddLog("Hello %d world\n", 123); 8596 // my_log.Draw("title"); 8597 struct ExampleAppLog 8598 { 8599 ImGuiTextBuffer Buf; 8600 ImGuiTextFilter Filter; 8601 ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls. 8602 bool AutoScroll; // Keep scrolling if already at the bottom. 8603 8604 ExampleAppLog() 8605 { 8606 AutoScroll = true; 8607 Clear(); 8608 } 8609 8610 void Clear() 8611 { 8612 Buf.clear(); 8613 LineOffsets.clear(); 8614 LineOffsets.push_back(0); 8615 } 8616 8617 void AddLog(const char* fmt, ...) IM_FMTARGS(2) 8618 { 8619 int old_size = Buf.size(); 8620 va_list args; 8621 va_start(args, fmt); 8622 Buf.appendfv(fmt, args); 8623 va_end(args); 8624 for (int new_size = Buf.size(); old_size < new_size; old_size++) 8625 if (Buf[old_size] == '\n') 8626 LineOffsets.push_back(old_size + 1); 8627 } 8628 8629 void Draw(const char* title, bool* p_open = NULL) 8630 { 8631 if (!ImGui::Begin(title, p_open)) 8632 { 8633 ImGui::End(); 8634 return; 8635 } 8636 8637 // Options menu 8638 if (ImGui::BeginPopup("Options")) 8639 { 8640 ImGui::Checkbox("Auto-scroll", &AutoScroll); 8641 ImGui::EndPopup(); 8642 } 8643 8644 // Main window 8645 if (ImGui::Button("Options")) 8646 ImGui::OpenPopup("Options"); 8647 ImGui::SameLine(); 8648 bool clear = ImGui::Button("Clear"); 8649 ImGui::SameLine(); 8650 bool copy = ImGui::Button("Copy"); 8651 ImGui::SameLine(); 8652 Filter.Draw("Filter", -100.0f); 8653 8654 ImGui::Separator(); 8655 8656 if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) 8657 { 8658 if (clear) 8659 Clear(); 8660 if (copy) 8661 ImGui::LogToClipboard(); 8662 8663 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); 8664 const char* buf = Buf.begin(); 8665 const char* buf_end = Buf.end(); 8666 if (Filter.IsActive()) 8667 { 8668 // In this example we don't use the clipper when Filter is enabled. 8669 // This is because we don't have random access to the result of our filter. 8670 // A real application processing logs with ten of thousands of entries may want to store the result of 8671 // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp). 8672 for (int line_no = 0; line_no < LineOffsets.Size; line_no++) 8673 { 8674 const char* line_start = buf + LineOffsets[line_no]; 8675 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; 8676 if (Filter.PassFilter(line_start, line_end)) 8677 ImGui::TextUnformatted(line_start, line_end); 8678 } 8679 } 8680 else 8681 { 8682 // The simplest and easy way to display the entire buffer: 8683 // ImGui::TextUnformatted(buf_begin, buf_end); 8684 // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward 8685 // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are 8686 // within the visible area. 8687 // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them 8688 // on your side is recommended. Using ImGuiListClipper requires 8689 // - A) random access into your data 8690 // - B) items all being the same height, 8691 // both of which we can handle since we have an array pointing to the beginning of each line of text. 8692 // When using the filter (in the block of code above) we don't have random access into the data to display 8693 // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make 8694 // it possible (and would be recommended if you want to search through tens of thousands of entries). 8695 ImGuiListClipper clipper; 8696 clipper.Begin(LineOffsets.Size); 8697 while (clipper.Step()) 8698 { 8699 for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) 8700 { 8701 const char* line_start = buf + LineOffsets[line_no]; 8702 const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; 8703 ImGui::TextUnformatted(line_start, line_end); 8704 } 8705 } 8706 clipper.End(); 8707 } 8708 ImGui::PopStyleVar(); 8709 8710 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame. 8711 // Using a scrollbar or mouse-wheel will take away from the bottom edge. 8712 if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) 8713 ImGui::SetScrollHereY(1.0f); 8714 } 8715 ImGui::EndChild(); 8716 ImGui::End(); 8717 } 8718 }; 8719 8720 // Demonstrate creating a simple log window with basic filtering. 8721 static void ShowExampleAppLog(bool* p_open) 8722 { 8723 static ExampleAppLog log; 8724 8725 // For the demo: add a debug button _BEFORE_ the normal log window contents 8726 // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window. 8727 // Most of the contents of the window will be added by the log.Draw() call. 8728 ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); 8729 ImGui::Begin("Example: Log", p_open); 8730 IMGUI_DEMO_MARKER("Examples/Log"); 8731 if (ImGui::SmallButton("[Debug] Add 5 entries")) 8732 { 8733 static int counter = 0; 8734 const char* categories[3] = { "info", "warn", "error" }; 8735 const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }; 8736 for (int n = 0; n < 5; n++) 8737 { 8738 const char* category = categories[counter % IM_ARRAYSIZE(categories)]; 8739 const char* word = words[counter % IM_ARRAYSIZE(words)]; 8740 log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", 8741 ImGui::GetFrameCount(), category, ImGui::GetTime(), word); 8742 counter++; 8743 } 8744 } 8745 ImGui::End(); 8746 8747 // Actually call in the regular Log helper (which will Begin() into the same window as we just did) 8748 log.Draw("Example: Log", p_open); 8749 } 8750 8751 //----------------------------------------------------------------------------- 8752 // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() 8753 //----------------------------------------------------------------------------- 8754 8755 // Demonstrate create a window with multiple child windows. 8756 static void ShowExampleAppLayout(bool* p_open) 8757 { 8758 ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); 8759 if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) 8760 { 8761 IMGUI_DEMO_MARKER("Examples/Simple layout"); 8762 if (ImGui::BeginMenuBar()) 8763 { 8764 if (ImGui::BeginMenu("File")) 8765 { 8766 if (ImGui::MenuItem("Close", "Ctrl+W")) { *p_open = false; } 8767 ImGui::EndMenu(); 8768 } 8769 ImGui::EndMenuBar(); 8770 } 8771 8772 // Left 8773 static int selected = 0; 8774 { 8775 ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX); 8776 for (int i = 0; i < 100; i++) 8777 { 8778 // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav 8779 char label[128]; 8780 sprintf(label, "MyObject %d", i); 8781 if (ImGui::Selectable(label, selected == i)) 8782 selected = i; 8783 } 8784 ImGui::EndChild(); 8785 } 8786 ImGui::SameLine(); 8787 8788 // Right 8789 { 8790 ImGui::BeginGroup(); 8791 ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us 8792 ImGui::Text("MyObject: %d", selected); 8793 ImGui::Separator(); 8794 if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None)) 8795 { 8796 if (ImGui::BeginTabItem("Description")) 8797 { 8798 ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); 8799 ImGui::EndTabItem(); 8800 } 8801 if (ImGui::BeginTabItem("Details")) 8802 { 8803 ImGui::Text("ID: 0123456789"); 8804 ImGui::EndTabItem(); 8805 } 8806 ImGui::EndTabBar(); 8807 } 8808 ImGui::EndChild(); 8809 if (ImGui::Button("Revert")) {} 8810 ImGui::SameLine(); 8811 if (ImGui::Button("Save")) {} 8812 ImGui::EndGroup(); 8813 } 8814 } 8815 ImGui::End(); 8816 } 8817 8818 //----------------------------------------------------------------------------- 8819 // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() 8820 //----------------------------------------------------------------------------- 8821 // Some of the interactions are a bit lack-luster: 8822 // - We would want pressing validating or leaving the filter to somehow restore focus. 8823 // - We may want more advanced filtering (child nodes) and clipper support: both will need extra work. 8824 // - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties. 8825 //----------------------------------------------------------------------------- 8826 8827 struct ExampleAppPropertyEditor 8828 { 8829 ImGuiTextFilter Filter; 8830 ExampleTreeNode* VisibleNode = NULL; 8831 8832 void Draw(ExampleTreeNode* root_node) 8833 { 8834 // Left side: draw tree 8835 // - Currently using a table to benefit from RowBg feature 8836 if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened)) 8837 { 8838 ImGui::SetNextItemWidth(-FLT_MIN); 8839 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); 8840 ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); 8841 if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) 8842 Filter.Build(); 8843 ImGui::PopItemFlag(); 8844 8845 if (ImGui::BeginTable("##bg", 1, ImGuiTableFlags_RowBg)) 8846 { 8847 for (ExampleTreeNode* node : root_node->Childs) 8848 if (Filter.PassFilter(node->Name)) // Filter root node 8849 DrawTreeNode(node); 8850 ImGui::EndTable(); 8851 } 8852 } 8853 ImGui::EndChild(); 8854 8855 // Right side: draw properties 8856 ImGui::SameLine(); 8857 8858 ImGui::BeginGroup(); // Lock X position 8859 if (ExampleTreeNode* node = VisibleNode) 8860 { 8861 ImGui::Text("%s", node->Name); 8862 ImGui::TextDisabled("UID: 0x%08X", node->UID); 8863 ImGui::Separator(); 8864 if (ImGui::BeginTable("##properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) 8865 { 8866 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); 8867 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger 8868 if (node->HasData) 8869 { 8870 // In a typical application, the structure description would be derived from a data-driven system. 8871 // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. 8872 // - Limits and some details are hard-coded to simplify the demo. 8873 for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) 8874 { 8875 ImGui::TableNextRow(); 8876 ImGui::PushID(field_desc.Name); 8877 ImGui::TableNextColumn(); 8878 ImGui::AlignTextToFramePadding(); 8879 ImGui::TextUnformatted(field_desc.Name); 8880 ImGui::TableNextColumn(); 8881 void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); 8882 switch (field_desc.DataType) 8883 { 8884 case ImGuiDataType_Bool: 8885 { 8886 IM_ASSERT(field_desc.DataCount == 1); 8887 ImGui::Checkbox("##Editor", (bool*)field_ptr); 8888 break; 8889 } 8890 case ImGuiDataType_S32: 8891 { 8892 int v_min = INT_MIN, v_max = INT_MAX; 8893 ImGui::SetNextItemWidth(-FLT_MIN); 8894 ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); 8895 break; 8896 } 8897 case ImGuiDataType_Float: 8898 { 8899 float v_min = 0.0f, v_max = 1.0f; 8900 ImGui::SetNextItemWidth(-FLT_MIN); 8901 ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); 8902 break; 8903 } 8904 } 8905 ImGui::PopID(); 8906 } 8907 } 8908 ImGui::EndTable(); 8909 } 8910 } 8911 ImGui::EndGroup(); 8912 } 8913 8914 void DrawTreeNode(ExampleTreeNode* node) 8915 { 8916 ImGui::TableNextRow(); 8917 ImGui::TableNextColumn(); 8918 ImGui::PushID(node->UID); 8919 ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; 8920 tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards 8921 tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support 8922 if (node == VisibleNode) 8923 tree_flags |= ImGuiTreeNodeFlags_Selected; 8924 if (node->Childs.Size == 0) 8925 tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet; 8926 if (node->DataMyBool == false) 8927 ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); 8928 bool node_open = ImGui::TreeNodeEx("", tree_flags, "%s", node->Name); 8929 if (node->DataMyBool == false) 8930 ImGui::PopStyleColor(); 8931 if (ImGui::IsItemFocused()) 8932 VisibleNode = node; 8933 if (node_open) 8934 { 8935 for (ExampleTreeNode* child : node->Childs) 8936 DrawTreeNode(child); 8937 ImGui::TreePop(); 8938 } 8939 ImGui::PopID(); 8940 } 8941 }; 8942 8943 // Demonstrate creating a simple property editor. 8944 static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data) 8945 { 8946 ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); 8947 if (!ImGui::Begin("Example: Property editor", p_open)) 8948 { 8949 ImGui::End(); 8950 return; 8951 } 8952 8953 IMGUI_DEMO_MARKER("Examples/Property Editor"); 8954 static ExampleAppPropertyEditor property_editor; 8955 if (demo_data->DemoTree == NULL) 8956 demo_data->DemoTree = ExampleTree_CreateDemoTree(); 8957 property_editor.Draw(demo_data->DemoTree); 8958 8959 ImGui::End(); 8960 } 8961 8962 //----------------------------------------------------------------------------- 8963 // [SECTION] Example App: Long Text / ShowExampleAppLongText() 8964 //----------------------------------------------------------------------------- 8965 8966 // Demonstrate/test rendering huge amount of text, and the incidence of clipping. 8967 static void ShowExampleAppLongText(bool* p_open) 8968 { 8969 ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); 8970 if (!ImGui::Begin("Example: Long text display", p_open)) 8971 { 8972 ImGui::End(); 8973 return; 8974 } 8975 IMGUI_DEMO_MARKER("Examples/Long text display"); 8976 8977 static int test_type = 0; 8978 static ImGuiTextBuffer log; 8979 static int lines = 0; 8980 ImGui::Text("Printing unusually long amount of text."); 8981 ImGui::Combo("Test type", &test_type, 8982 "Single call to TextUnformatted()\0" 8983 "Multiple calls to Text(), clipped\0" 8984 "Multiple calls to Text(), not clipped (slow)\0"); 8985 ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); 8986 if (ImGui::Button("Clear")) { log.clear(); lines = 0; } 8987 ImGui::SameLine(); 8988 if (ImGui::Button("Add 1000 lines")) 8989 { 8990 for (int i = 0; i < 1000; i++) 8991 log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i); 8992 lines += 1000; 8993 } 8994 ImGui::BeginChild("Log"); 8995 switch (test_type) 8996 { 8997 case 0: 8998 // Single call to TextUnformatted() with a big buffer 8999 ImGui::TextUnformatted(log.begin(), log.end()); 9000 break; 9001 case 1: 9002 { 9003 // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper. 9004 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); 9005 ImGuiListClipper clipper; 9006 clipper.Begin(lines); 9007 while (clipper.Step()) 9008 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) 9009 ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); 9010 ImGui::PopStyleVar(); 9011 break; 9012 } 9013 case 2: 9014 // Multiple calls to Text(), not clipped (slow) 9015 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); 9016 for (int i = 0; i < lines; i++) 9017 ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); 9018 ImGui::PopStyleVar(); 9019 break; 9020 } 9021 ImGui::EndChild(); 9022 ImGui::End(); 9023 } 9024 9025 //----------------------------------------------------------------------------- 9026 // [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() 9027 //----------------------------------------------------------------------------- 9028 9029 // Demonstrate creating a window which gets auto-resized according to its content. 9030 static void ShowExampleAppAutoResize(bool* p_open) 9031 { 9032 if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize)) 9033 { 9034 ImGui::End(); 9035 return; 9036 } 9037 IMGUI_DEMO_MARKER("Examples/Auto-resizing window"); 9038 9039 static int lines = 10; 9040 ImGui::TextUnformatted( 9041 "Window will resize every-frame to the size of its content.\n" 9042 "Note that you probably don't want to query the window size to\n" 9043 "output your content because that would create a feedback loop."); 9044 ImGui::SliderInt("Number of lines", &lines, 1, 20); 9045 for (int i = 0; i < lines; i++) 9046 ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally 9047 ImGui::End(); 9048 } 9049 9050 //----------------------------------------------------------------------------- 9051 // [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() 9052 //----------------------------------------------------------------------------- 9053 9054 // Demonstrate creating a window with custom resize constraints. 9055 // Note that size constraints currently don't work on a docked window (when in 'docking' branch) 9056 static void ShowExampleAppConstrainedResize(bool* p_open) 9057 { 9058 struct CustomConstraints 9059 { 9060 // Helper functions to demonstrate programmatic constraints 9061 // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier. 9062 // FIXME: None of the three demos works consistently when resizing from borders. 9063 static void AspectRatio(ImGuiSizeCallbackData* data) 9064 { 9065 float aspect_ratio = *(float*)data->UserData; 9066 data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio); 9067 } 9068 static void Square(ImGuiSizeCallbackData* data) 9069 { 9070 data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y); 9071 } 9072 static void Step(ImGuiSizeCallbackData* data) 9073 { 9074 float step = *(float*)data->UserData; 9075 data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); 9076 } 9077 }; 9078 9079 const char* test_desc[] = 9080 { 9081 "Between 100x100 and 500x500", 9082 "At least 100x100", 9083 "Resize vertical + lock current width", 9084 "Resize horizontal + lock current height", 9085 "Width Between 400 and 500", 9086 "Height at least 400", 9087 "Custom: Aspect Ratio 16:9", 9088 "Custom: Always Square", 9089 "Custom: Fixed Steps (100)", 9090 }; 9091 9092 // Options 9093 static bool auto_resize = false; 9094 static bool window_padding = true; 9095 static int type = 6; // Aspect Ratio 9096 static int display_lines = 10; 9097 9098 // Submit constraint 9099 float aspect_ratio = 16.0f / 9.0f; 9100 float fixed_step = 100.0f; 9101 if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500)); // Between 100x100 and 500x500 9102 if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 9103 if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width 9104 if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height 9105 if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width Between and 400 and 500 9106 if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, FLT_MAX)); // Height at least 400 9107 if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio); // Aspect ratio 9108 if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square 9109 if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step); // Fixed Step 9110 9111 // Submit window 9112 if (!window_padding) 9113 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); 9114 const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; 9115 const bool window_open = ImGui::Begin("Example: Constrained Resize", p_open, window_flags); 9116 if (!window_padding) 9117 ImGui::PopStyleVar(); 9118 if (window_open) 9119 { 9120 IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); 9121 if (ImGui::GetIO().KeyShift) 9122 { 9123 // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture. 9124 ImVec2 avail_size = ImGui::GetContentRegionAvail(); 9125 ImVec2 pos = ImGui::GetCursorScreenPos(); 9126 ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size); 9127 ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10)); 9128 ImGui::Text("%.2f x %.2f", avail_size.x, avail_size.y); 9129 } 9130 else 9131 { 9132 ImGui::Text("(Hold SHIFT to display a dummy viewport)"); 9133 if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); 9134 if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); 9135 if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } 9136 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); 9137 ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc)); 9138 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); 9139 ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); 9140 ImGui::Checkbox("Auto-resize", &auto_resize); 9141 ImGui::Checkbox("Window padding", &window_padding); 9142 for (int i = 0; i < display_lines; i++) 9143 ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, ""); 9144 } 9145 } 9146 ImGui::End(); 9147 } 9148 9149 //----------------------------------------------------------------------------- 9150 // [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay() 9151 //----------------------------------------------------------------------------- 9152 9153 // Demonstrate creating a simple static window with no decoration 9154 // + a context-menu to choose which corner of the screen to use. 9155 static void ShowExampleAppSimpleOverlay(bool* p_open) 9156 { 9157 static int location = 0; 9158 ImGuiIO& io = ImGui::GetIO(); 9159 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; 9160 if (location >= 0) 9161 { 9162 const float PAD = 10.0f; 9163 const ImGuiViewport* viewport = ImGui::GetMainViewport(); 9164 ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! 9165 ImVec2 work_size = viewport->WorkSize; 9166 ImVec2 window_pos, window_pos_pivot; 9167 window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD); 9168 window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD); 9169 window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f; 9170 window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f; 9171 ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); 9172 window_flags |= ImGuiWindowFlags_NoMove; 9173 } 9174 else if (location == -2) 9175 { 9176 // Center window 9177 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 9178 window_flags |= ImGuiWindowFlags_NoMove; 9179 } 9180 ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background 9181 if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) 9182 { 9183 IMGUI_DEMO_MARKER("Examples/Simple Overlay"); 9184 ImGui::Text("Simple overlay\n" "(right-click to change position)"); 9185 ImGui::Separator(); 9186 if (ImGui::IsMousePosValid()) 9187 ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); 9188 else 9189 ImGui::Text("Mouse Position: <invalid>"); 9190 if (ImGui::BeginPopupContextWindow()) 9191 { 9192 if (ImGui::MenuItem("Custom", NULL, location == -1)) location = -1; 9193 if (ImGui::MenuItem("Center", NULL, location == -2)) location = -2; 9194 if (ImGui::MenuItem("Top-left", NULL, location == 0)) location = 0; 9195 if (ImGui::MenuItem("Top-right", NULL, location == 1)) location = 1; 9196 if (ImGui::MenuItem("Bottom-left", NULL, location == 2)) location = 2; 9197 if (ImGui::MenuItem("Bottom-right", NULL, location == 3)) location = 3; 9198 if (p_open && ImGui::MenuItem("Close")) *p_open = false; 9199 ImGui::EndPopup(); 9200 } 9201 } 9202 ImGui::End(); 9203 } 9204 9205 //----------------------------------------------------------------------------- 9206 // [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen() 9207 //----------------------------------------------------------------------------- 9208 9209 // Demonstrate creating a window covering the entire screen/viewport 9210 static void ShowExampleAppFullscreen(bool* p_open) 9211 { 9212 static bool use_work_area = true; 9213 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; 9214 9215 // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.) 9216 // Based on your use case you may want one or the other. 9217 const ImGuiViewport* viewport = ImGui::GetMainViewport(); 9218 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos); 9219 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size); 9220 9221 if (ImGui::Begin("Example: Fullscreen window", p_open, flags)) 9222 { 9223 ImGui::Checkbox("Use work area instead of main area", &use_work_area); 9224 ImGui::SameLine(); 9225 HelpMarker("Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference."); 9226 9227 ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground); 9228 ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration); 9229 ImGui::Indent(); 9230 ImGui::CheckboxFlags("ImGuiWindowFlags_NoTitleBar", &flags, ImGuiWindowFlags_NoTitleBar); 9231 ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse); 9232 ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar); 9233 ImGui::Unindent(); 9234 9235 if (p_open && ImGui::Button("Close this window")) 9236 *p_open = false; 9237 } 9238 ImGui::End(); 9239 } 9240 9241 //----------------------------------------------------------------------------- 9242 // [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() 9243 //----------------------------------------------------------------------------- 9244 9245 // Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation. 9246 // This applies to all regular items as well. 9247 // Read FAQ section "How can I have multiple widgets with the same label?" for details. 9248 static void ShowExampleAppWindowTitles(bool*) 9249 { 9250 const ImGuiViewport* viewport = ImGui::GetMainViewport(); 9251 const ImVec2 base_pos = viewport->Pos; 9252 9253 // By default, Windows are uniquely identified by their title. 9254 // You can use the "##" and "###" markers to manipulate the display/ID. 9255 9256 // Using "##" to display same title but have unique identifier. 9257 ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); 9258 ImGui::Begin("Same title as another window##1"); 9259 IMGUI_DEMO_MARKER("Examples/Manipulating window titles"); 9260 ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); 9261 ImGui::End(); 9262 9263 ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver); 9264 ImGui::Begin("Same title as another window##2"); 9265 ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); 9266 ImGui::End(); 9267 9268 // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" 9269 char buf[128]; 9270 sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount()); 9271 ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver); 9272 ImGui::Begin(buf); 9273 ImGui::Text("This window has a changing title."); 9274 ImGui::End(); 9275 } 9276 9277 //----------------------------------------------------------------------------- 9278 // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() 9279 //----------------------------------------------------------------------------- 9280 9281 // Add a |_| looking shape 9282 static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz) 9283 { 9284 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 } }; 9285 for (const ImVec2& p : pos_norms) 9286 draw_list->PathLineTo(ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y))); 9287 } 9288 9289 // Demonstrate using the low-level ImDrawList to draw custom shapes. 9290 static void ShowExampleAppCustomRendering(bool* p_open) 9291 { 9292 if (!ImGui::Begin("Example: Custom rendering", p_open)) 9293 { 9294 ImGui::End(); 9295 return; 9296 } 9297 IMGUI_DEMO_MARKER("Examples/Custom Rendering"); 9298 9299 // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of 9300 // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your 9301 // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not 9302 // exposed outside (to avoid messing with your types) In this example we are not using the maths operators! 9303 9304 if (ImGui::BeginTabBar("##TabBar")) 9305 { 9306 if (ImGui::BeginTabItem("Primitives")) 9307 { 9308 ImGui::PushItemWidth(-ImGui::GetFontSize() * 15); 9309 ImDrawList* draw_list = ImGui::GetWindowDrawList(); 9310 9311 // Draw gradients 9312 // (note that those are currently exacerbating our sRGB/Linear issues) 9313 // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well.. 9314 ImGui::Text("Gradients"); 9315 ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight()); 9316 { 9317 ImVec2 p0 = ImGui::GetCursorScreenPos(); 9318 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y); 9319 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255)); 9320 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255)); 9321 draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); 9322 ImGui::InvisibleButton("##gradient1", gradient_size); 9323 } 9324 { 9325 ImVec2 p0 = ImGui::GetCursorScreenPos(); 9326 ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y); 9327 ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255)); 9328 ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255)); 9329 draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); 9330 ImGui::InvisibleButton("##gradient2", gradient_size); 9331 } 9332 9333 // Draw a bunch of primitives 9334 ImGui::Text("All primitives"); 9335 static float sz = 36.0f; 9336 static float thickness = 3.0f; 9337 static int ngon_sides = 6; 9338 static bool circle_segments_override = false; 9339 static int circle_segments_override_v = 12; 9340 static bool curve_segments_override = false; 9341 static int curve_segments_override_v = 8; 9342 static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); 9343 ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 100.0f, "%.0f"); 9344 ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); 9345 ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12); 9346 ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override); 9347 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); 9348 circle_segments_override |= ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40); 9349 ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override); 9350 ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); 9351 curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40); 9352 ImGui::ColorEdit4("Color", &colf.x); 9353 9354 const ImVec2 p = ImGui::GetCursorScreenPos(); 9355 const ImU32 col = ImColor(colf); 9356 const float spacing = 10.0f; 9357 const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight; 9358 const float rounding = sz / 5.0f; 9359 const int circle_segments = circle_segments_override ? circle_segments_override_v : 0; 9360 const int curve_segments = curve_segments_override ? curve_segments_override_v : 0; 9361 const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves 9362 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) }; 9363 9364 float x = p.x + 4.0f; 9365 float y = p.y + 4.0f; 9366 for (int n = 0; n < 2; n++) 9367 { 9368 // First line uses a thickness of 1.0f, second line uses the configurable thickness 9369 float th = (n == 0) ? 1.0f : thickness; 9370 draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon 9371 draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle 9372 draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse 9373 draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square 9374 draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners 9375 draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners 9376 draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle 9377 //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 9378 PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape 9379 //draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th); 9380 draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) 9381 draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) 9382 draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line 9383 9384 // Path 9385 draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f); 9386 draw_list->PathStroke(col, ImDrawFlags_None, th); 9387 x += sz + spacing; 9388 9389 // Quadratic Bezier Curve (3 control points) 9390 draw_list->AddBezierQuadratic(ImVec2(x + cp3[0].x, y + cp3[0].y), ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), col, th, curve_segments); 9391 x += sz + spacing; 9392 9393 // Cubic Bezier Curve (4 control points) 9394 draw_list->AddBezierCubic(ImVec2(x + cp4[0].x, y + cp4[0].y), ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), col, th, curve_segments); 9395 9396 x = p.x + 4; 9397 y += sz + spacing; 9398 } 9399 9400 // Filled shapes 9401 draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides); x += sz + spacing; // N-gon 9402 draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments); x += sz + spacing; // Circle 9403 draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), ImVec2(sz * 0.5f, sz * 0.3f), col, -0.3f, circle_segments); x += sz + spacing;// Ellipse 9404 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square 9405 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners 9406 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners 9407 draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle 9408 //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 9409 PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col); x += sz + spacing; // Concave shape 9410 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) 9411 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness) 9412 draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine) 9413 9414 // Path 9415 draw_list->PathArcTo(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, 3.141592f * -0.5f, 3.141592f); 9416 draw_list->PathFillConvex(col); 9417 x += sz + spacing; 9418 9419 // Quadratic Bezier Curve (3 control points) 9420 draw_list->PathLineTo(ImVec2(x + cp3[0].x, y + cp3[0].y)); 9421 draw_list->PathBezierQuadraticCurveTo(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), curve_segments); 9422 draw_list->PathFillConvex(col); 9423 x += sz + spacing; 9424 9425 draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); 9426 x += sz + spacing; 9427 9428 ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f)); 9429 ImGui::PopItemWidth(); 9430 ImGui::EndTabItem(); 9431 } 9432 9433 if (ImGui::BeginTabItem("Canvas")) 9434 { 9435 static ImVector<ImVec2> points; 9436 static ImVec2 scrolling(0.0f, 0.0f); 9437 static bool opt_enable_grid = true; 9438 static bool opt_enable_context_menu = true; 9439 static bool adding_line = false; 9440 9441 ImGui::Checkbox("Enable grid", &opt_enable_grid); 9442 ImGui::Checkbox("Enable context menu", &opt_enable_context_menu); 9443 ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu."); 9444 9445 // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling. 9446 // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls. 9447 // To use a child window instead we could use, e.g: 9448 // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding 9449 // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color 9450 // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove); 9451 // ImGui::PopStyleColor(); 9452 // ImGui::PopStyleVar(); 9453 // [...] 9454 // ImGui::EndChild(); 9455 9456 // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive() 9457 ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! 9458 ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available 9459 if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f; 9460 if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f; 9461 ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); 9462 9463 // Draw border and background color 9464 ImGuiIO& io = ImGui::GetIO(); 9465 ImDrawList* draw_list = ImGui::GetWindowDrawList(); 9466 draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255)); 9467 draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255)); 9468 9469 // This will catch our interactions 9470 ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); 9471 const bool is_hovered = ImGui::IsItemHovered(); // Hovered 9472 const bool is_active = ImGui::IsItemActive(); // Held 9473 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin 9474 const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y); 9475 9476 // Add first and second point 9477 if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) 9478 { 9479 points.push_back(mouse_pos_in_canvas); 9480 points.push_back(mouse_pos_in_canvas); 9481 adding_line = true; 9482 } 9483 if (adding_line) 9484 { 9485 points.back() = mouse_pos_in_canvas; 9486 if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) 9487 adding_line = false; 9488 } 9489 9490 // Pan (we use a zero mouse threshold when there's no context menu) 9491 // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc. 9492 const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f; 9493 if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) 9494 { 9495 scrolling.x += io.MouseDelta.x; 9496 scrolling.y += io.MouseDelta.y; 9497 } 9498 9499 // Context menu (under default mouse threshold) 9500 ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right); 9501 if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f) 9502 ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); 9503 if (ImGui::BeginPopup("context")) 9504 { 9505 if (adding_line) 9506 points.resize(points.size() - 2); 9507 adding_line = false; 9508 if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); } 9509 if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0)) { points.clear(); } 9510 ImGui::EndPopup(); 9511 } 9512 9513 // Draw grid + all lines in the canvas 9514 draw_list->PushClipRect(canvas_p0, canvas_p1, true); 9515 if (opt_enable_grid) 9516 { 9517 const float GRID_STEP = 64.0f; 9518 for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP) 9519 draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40)); 9520 for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP) 9521 draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40)); 9522 } 9523 for (int n = 0; n < points.Size; n += 2) 9524 draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); 9525 draw_list->PopClipRect(); 9526 9527 ImGui::EndTabItem(); 9528 } 9529 9530 if (ImGui::BeginTabItem("BG/FG draw lists")) 9531 { 9532 static bool draw_bg = true; 9533 static bool draw_fg = true; 9534 ImGui::Checkbox("Draw in Background draw list", &draw_bg); 9535 ImGui::SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows."); 9536 ImGui::Checkbox("Draw in Foreground draw list", &draw_fg); 9537 ImGui::SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows."); 9538 ImVec2 window_pos = ImGui::GetWindowPos(); 9539 ImVec2 window_size = ImGui::GetWindowSize(); 9540 ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f); 9541 if (draw_bg) 9542 ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4); 9543 if (draw_fg) 9544 ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10); 9545 ImGui::EndTabItem(); 9546 } 9547 9548 // Demonstrate out-of-order rendering via channels splitting 9549 // We use functions in ImDrawList as each draw list contains a convenience splitter, 9550 // but you can also instantiate your own ImDrawListSplitter if you need to nest them. 9551 if (ImGui::BeginTabItem("Draw Channels")) 9552 { 9553 ImDrawList* draw_list = ImGui::GetWindowDrawList(); 9554 { 9555 ImGui::Text("Blue shape is drawn first: appears in back"); 9556 ImGui::Text("Red shape is drawn after: appears in front"); 9557 ImVec2 p0 = ImGui::GetCursorScreenPos(); 9558 draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue 9559 draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red 9560 ImGui::Dummy(ImVec2(75, 75)); 9561 } 9562 ImGui::Separator(); 9563 { 9564 ImGui::Text("Blue shape is drawn first, into channel 1: appears in front"); 9565 ImGui::Text("Red shape is drawn after, into channel 0: appears in back"); 9566 ImVec2 p1 = ImGui::GetCursorScreenPos(); 9567 9568 // Create 2 channels and draw a Blue shape THEN a Red shape. 9569 // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls. 9570 draw_list->ChannelsSplit(2); 9571 draw_list->ChannelsSetCurrent(1); 9572 draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue 9573 draw_list->ChannelsSetCurrent(0); 9574 draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red 9575 9576 // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1. 9577 // This works by copying draw indices only (vertices are not copied). 9578 draw_list->ChannelsMerge(); 9579 ImGui::Dummy(ImVec2(75, 75)); 9580 ImGui::Text("After reordering, contents of channel 0 appears below channel 1."); 9581 } 9582 ImGui::EndTabItem(); 9583 } 9584 9585 ImGui::EndTabBar(); 9586 } 9587 9588 ImGui::End(); 9589 } 9590 9591 //----------------------------------------------------------------------------- 9592 // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() 9593 //----------------------------------------------------------------------------- 9594 9595 // Simplified structure to mimic a Document model 9596 struct MyDocument 9597 { 9598 char Name[32]; // Document title 9599 int UID; // Unique ID (necessary as we can change title) 9600 bool Open; // Set when open (we keep an array of all available documents to simplify demo code!) 9601 bool OpenPrev; // Copy of Open from last update. 9602 bool Dirty; // Set when the document has been modified 9603 ImVec4 Color; // An arbitrary variable associated to the document 9604 9605 MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f)) 9606 { 9607 UID = uid; 9608 snprintf(Name, sizeof(Name), "%s", name); 9609 Open = OpenPrev = open; 9610 Dirty = false; 9611 Color = color; 9612 } 9613 void DoOpen() { Open = true; } 9614 void DoForceClose() { Open = false; Dirty = false; } 9615 void DoSave() { Dirty = false; } 9616 }; 9617 9618 struct ExampleAppDocuments 9619 { 9620 ImVector<MyDocument> Documents; 9621 ImVector<MyDocument*> CloseQueue; 9622 MyDocument* RenamingDoc = NULL; 9623 bool RenamingStarted = false; 9624 9625 ExampleAppDocuments() 9626 { 9627 Documents.push_back(MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); 9628 Documents.push_back(MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); 9629 Documents.push_back(MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); 9630 Documents.push_back(MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); 9631 Documents.push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f))); 9632 Documents.push_back(MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f))); 9633 } 9634 9635 // As we allow to change document name, we append a never-changing document ID so tabs are stable 9636 void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size) 9637 { 9638 snprintf(out_buf, out_buf_size, "%s###doc%d", doc->Name, doc->UID); 9639 } 9640 9641 // Display placeholder contents for the Document 9642 void DisplayDocContents(MyDocument* doc) 9643 { 9644 ImGui::PushID(doc); 9645 ImGui::Text("Document \"%s\"", doc->Name); 9646 ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); 9647 ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); 9648 ImGui::PopStyleColor(); 9649 9650 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip); 9651 if (ImGui::Button("Rename..")) 9652 { 9653 RenamingDoc = doc; 9654 RenamingStarted = true; 9655 } 9656 ImGui::SameLine(); 9657 9658 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip); 9659 if (ImGui::Button("Modify")) 9660 doc->Dirty = true; 9661 9662 ImGui::SameLine(); 9663 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip); 9664 if (ImGui::Button("Save")) 9665 doc->DoSave(); 9666 9667 ImGui::SameLine(); 9668 ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip); 9669 if (ImGui::Button("Close")) 9670 CloseQueue.push_back(doc); 9671 ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior. 9672 ImGui::PopID(); 9673 } 9674 9675 // Display context menu for the Document 9676 void DisplayDocContextMenu(MyDocument* doc) 9677 { 9678 if (!ImGui::BeginPopupContextItem()) 9679 return; 9680 9681 char buf[256]; 9682 sprintf(buf, "Save %s", doc->Name); 9683 if (ImGui::MenuItem(buf, "Ctrl+S", false, doc->Open)) 9684 doc->DoSave(); 9685 if (ImGui::MenuItem("Rename...", "Ctrl+R", false, doc->Open)) 9686 RenamingDoc = doc; 9687 if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open)) 9688 CloseQueue.push_back(doc); 9689 ImGui::EndPopup(); 9690 } 9691 9692 // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface. 9693 // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, 9694 // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for 9695 // 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 9696 // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively 9697 // give the impression of a flicker for one frame. 9698 // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. 9699 // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. 9700 void NotifyOfDocumentsClosedElsewhere() 9701 { 9702 for (MyDocument& doc : Documents) 9703 { 9704 if (!doc.Open && doc.OpenPrev) 9705 ImGui::SetTabItemClosed(doc.Name); 9706 doc.OpenPrev = doc.Open; 9707 } 9708 } 9709 }; 9710 9711 void ShowExampleAppDocuments(bool* p_open) 9712 { 9713 static ExampleAppDocuments app; 9714 9715 // Options 9716 static bool opt_reorderable = true; 9717 static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; 9718 9719 bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar); 9720 if (!window_contents_visible) 9721 { 9722 ImGui::End(); 9723 return; 9724 } 9725 9726 // Menu 9727 if (ImGui::BeginMenuBar()) 9728 { 9729 if (ImGui::BeginMenu("File")) 9730 { 9731 int open_count = 0; 9732 for (MyDocument& doc : app.Documents) 9733 open_count += doc.Open ? 1 : 0; 9734 9735 if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) 9736 { 9737 for (MyDocument& doc : app.Documents) 9738 if (!doc.Open && ImGui::MenuItem(doc.Name)) 9739 doc.DoOpen(); 9740 ImGui::EndMenu(); 9741 } 9742 if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) 9743 for (MyDocument& doc : app.Documents) 9744 app.CloseQueue.push_back(&doc); 9745 if (ImGui::MenuItem("Exit") && p_open) 9746 *p_open = false; 9747 ImGui::EndMenu(); 9748 } 9749 ImGui::EndMenuBar(); 9750 } 9751 9752 // [Debug] List documents with one checkbox for each 9753 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) 9754 { 9755 MyDocument& doc = app.Documents[doc_n]; 9756 if (doc_n > 0) 9757 ImGui::SameLine(); 9758 ImGui::PushID(&doc); 9759 if (ImGui::Checkbox(doc.Name, &doc.Open)) 9760 if (!doc.Open) 9761 doc.DoForceClose(); 9762 ImGui::PopID(); 9763 } 9764 9765 ImGui::Separator(); 9766 9767 // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags. 9768 // They have multiple effects: 9769 // - Display a dot next to the title. 9770 // - Tab is selected when clicking the X close button. 9771 // - Closure is not assumed (will wait for user to stop submitting the tab). 9772 // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. 9773 // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty 9774 // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. 9775 // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole. 9776 9777 // Submit Tab Bar and Tabs 9778 { 9779 ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); 9780 tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline; 9781 if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) 9782 { 9783 if (opt_reorderable) 9784 app.NotifyOfDocumentsClosedElsewhere(); 9785 9786 // [DEBUG] Stress tests 9787 //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. 9788 //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. 9789 9790 // Submit Tabs 9791 for (MyDocument& doc : app.Documents) 9792 { 9793 if (!doc.Open) 9794 continue; 9795 9796 // As we allow to change document name, we append a never-changing document id so tabs are stable 9797 char doc_name_buf[64]; 9798 app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf)); 9799 ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); 9800 bool visible = ImGui::BeginTabItem(doc_name_buf, &doc.Open, tab_flags); 9801 9802 // Cancel attempt to close when unsaved add to save queue so we can display a popup. 9803 if (!doc.Open && doc.Dirty) 9804 { 9805 doc.Open = true; 9806 app.CloseQueue.push_back(&doc); 9807 } 9808 9809 app.DisplayDocContextMenu(&doc); 9810 if (visible) 9811 { 9812 app.DisplayDocContents(&doc); 9813 ImGui::EndTabItem(); 9814 } 9815 } 9816 9817 ImGui::EndTabBar(); 9818 } 9819 } 9820 9821 // Display renaming UI 9822 if (app.RenamingDoc != NULL) 9823 { 9824 if (app.RenamingStarted) 9825 ImGui::OpenPopup("Rename"); 9826 if (ImGui::BeginPopup("Rename")) 9827 { 9828 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30); 9829 if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue)) 9830 { 9831 ImGui::CloseCurrentPopup(); 9832 app.RenamingDoc = NULL; 9833 } 9834 if (app.RenamingStarted) 9835 ImGui::SetKeyboardFocusHere(-1); 9836 ImGui::EndPopup(); 9837 } 9838 else 9839 { 9840 app.RenamingDoc = NULL; 9841 } 9842 app.RenamingStarted = false; 9843 } 9844 9845 // Display closing confirmation UI 9846 if (!app.CloseQueue.empty()) 9847 { 9848 int close_queue_unsaved_documents = 0; 9849 for (int n = 0; n < app.CloseQueue.Size; n++) 9850 if (app.CloseQueue[n]->Dirty) 9851 close_queue_unsaved_documents++; 9852 9853 if (close_queue_unsaved_documents == 0) 9854 { 9855 // Close documents when all are unsaved 9856 for (int n = 0; n < app.CloseQueue.Size; n++) 9857 app.CloseQueue[n]->DoForceClose(); 9858 app.CloseQueue.clear(); 9859 } 9860 else 9861 { 9862 if (!ImGui::IsPopupOpen("Save?")) 9863 ImGui::OpenPopup("Save?"); 9864 if (ImGui::BeginPopupModal("Save?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) 9865 { 9866 ImGui::Text("Save change to the following items?"); 9867 float item_height = ImGui::GetTextLineHeightWithSpacing(); 9868 if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle)) 9869 for (MyDocument* doc : app.CloseQueue) 9870 if (doc->Dirty) 9871 ImGui::Text("%s", doc->Name); 9872 ImGui::EndChild(); 9873 9874 ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f); 9875 if (ImGui::Button("Yes", button_size)) 9876 { 9877 for (MyDocument* doc : app.CloseQueue) 9878 { 9879 if (doc->Dirty) 9880 doc->DoSave(); 9881 doc->DoForceClose(); 9882 } 9883 app.CloseQueue.clear(); 9884 ImGui::CloseCurrentPopup(); 9885 } 9886 ImGui::SameLine(); 9887 if (ImGui::Button("No", button_size)) 9888 { 9889 for (MyDocument* doc : app.CloseQueue) 9890 doc->DoForceClose(); 9891 app.CloseQueue.clear(); 9892 ImGui::CloseCurrentPopup(); 9893 } 9894 ImGui::SameLine(); 9895 if (ImGui::Button("Cancel", button_size)) 9896 { 9897 app.CloseQueue.clear(); 9898 ImGui::CloseCurrentPopup(); 9899 } 9900 ImGui::EndPopup(); 9901 } 9902 } 9903 } 9904 9905 ImGui::End(); 9906 } 9907 9908 //----------------------------------------------------------------------------- 9909 // [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser() 9910 //----------------------------------------------------------------------------- 9911 9912 //#include "imgui_internal.h" // NavMoveRequestTryWrapping() 9913 9914 struct ExampleAsset 9915 { 9916 ImGuiID ID; 9917 int Type; 9918 9919 ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; } 9920 9921 static const ImGuiTableSortSpecs* s_current_sort_specs; 9922 9923 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count) 9924 { 9925 s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function. 9926 if (items_count > 1) 9927 qsort(items, (size_t)items_count, sizeof(items[0]), ExampleAsset::CompareWithSortSpecs); 9928 s_current_sort_specs = NULL; 9929 } 9930 9931 // Compare function to be used by qsort() 9932 static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) 9933 { 9934 const ExampleAsset* a = (const ExampleAsset*)lhs; 9935 const ExampleAsset* b = (const ExampleAsset*)rhs; 9936 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) 9937 { 9938 const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n]; 9939 int delta = 0; 9940 if (sort_spec->ColumnIndex == 0) 9941 delta = ((int)a->ID - (int)b->ID); 9942 else if (sort_spec->ColumnIndex == 1) 9943 delta = (a->Type - b->Type); 9944 if (delta > 0) 9945 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1; 9946 if (delta < 0) 9947 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; 9948 } 9949 return ((int)a->ID - (int)b->ID); 9950 } 9951 }; 9952 const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL; 9953 9954 struct ExampleAssetsBrowser 9955 { 9956 // Options 9957 bool ShowTypeOverlay = true; 9958 bool AllowSorting = true; 9959 bool AllowDragUnselected = false; 9960 bool AllowBoxSelect = true; 9961 float IconSize = 32.0f; 9962 int IconSpacing = 10; 9963 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. 9964 bool StretchSpacing = true; 9965 9966 // State 9967 ImVector<ExampleAsset> Items; // Our items 9968 ExampleSelectionWithDeletion Selection; // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion) 9969 ImGuiID NextItemId = 0; // Unique identifier when creating new items 9970 bool RequestDelete = false; // Deferred deletion request 9971 bool RequestSort = false; // Deferred sort request 9972 float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better 9973 9974 // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way. 9975 ImVec2 LayoutItemSize; 9976 ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing 9977 float LayoutItemSpacing = 0.0f; 9978 float LayoutSelectableSpacing = 0.0f; 9979 float LayoutOuterPadding = 0.0f; 9980 int LayoutColumnCount = 0; 9981 int LayoutLineCount = 0; 9982 9983 // Functions 9984 ExampleAssetsBrowser() 9985 { 9986 AddItems(10000); 9987 } 9988 void AddItems(int count) 9989 { 9990 if (Items.Size == 0) 9991 NextItemId = 0; 9992 Items.reserve(Items.Size + count); 9993 for (int n = 0; n < count; n++, NextItemId++) 9994 Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2)); 9995 RequestSort = true; 9996 } 9997 void ClearItems() 9998 { 9999 Items.clear(); 10000 Selection.Clear(); 10001 } 10002 10003 // Logic would be written in the main code BeginChild() and outputing to local variables. 10004 // We extracted it into a function so we can call it easily from multiple places. 10005 void UpdateLayoutSizes(float avail_width) 10006 { 10007 // Layout: when not stretching: allow extending into right-most spacing. 10008 LayoutItemSpacing = (float)IconSpacing; 10009 if (StretchSpacing == false) 10010 avail_width += floorf(LayoutItemSpacing * 0.5f); 10011 10012 // Layout: calculate number of icon per line and number of lines 10013 LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize)); 10014 LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1); 10015 LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount; 10016 10017 // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer. 10018 if (StretchSpacing && LayoutColumnCount > 1) 10019 LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount; 10020 10021 LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing); 10022 LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f); 10023 LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f); 10024 } 10025 10026 void Draw(const char* title, bool* p_open) 10027 { 10028 ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver); 10029 if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar)) 10030 { 10031 ImGui::End(); 10032 return; 10033 } 10034 10035 // Menu bar 10036 if (ImGui::BeginMenuBar()) 10037 { 10038 if (ImGui::BeginMenu("File")) 10039 { 10040 if (ImGui::MenuItem("Add 10000 items")) 10041 AddItems(10000); 10042 if (ImGui::MenuItem("Clear items")) 10043 ClearItems(); 10044 ImGui::Separator(); 10045 if (ImGui::MenuItem("Close", NULL, false, p_open != NULL)) 10046 *p_open = false; 10047 ImGui::EndMenu(); 10048 } 10049 if (ImGui::BeginMenu("Edit")) 10050 { 10051 if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0)) 10052 RequestDelete = true; 10053 ImGui::EndMenu(); 10054 } 10055 if (ImGui::BeginMenu("Options")) 10056 { 10057 ImGui::PushItemWidth(ImGui::GetFontSize() * 10); 10058 10059 ImGui::SeparatorText("Contents"); 10060 ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay); 10061 ImGui::Checkbox("Allow Sorting", &AllowSorting); 10062 10063 ImGui::SeparatorText("Selection Behavior"); 10064 ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected); 10065 ImGui::Checkbox("Allow box-selection", &AllowBoxSelect); 10066 10067 ImGui::SeparatorText("Layout"); 10068 ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); 10069 ImGui::SameLine(); HelpMarker("Use CTRL+Wheel to zoom"); 10070 ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); 10071 ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32); 10072 ImGui::Checkbox("Stretch Spacing", &StretchSpacing); 10073 ImGui::PopItemWidth(); 10074 ImGui::EndMenu(); 10075 } 10076 ImGui::EndMenuBar(); 10077 } 10078 10079 // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI 10080 if (AllowSorting) 10081 { 10082 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); 10083 ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders; 10084 if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight()))) 10085 { 10086 ImGui::TableSetupColumn("Index"); 10087 ImGui::TableSetupColumn("Type"); 10088 ImGui::TableHeadersRow(); 10089 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) 10090 if (sort_specs->SpecsDirty || RequestSort) 10091 { 10092 ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size); 10093 sort_specs->SpecsDirty = RequestSort = false; 10094 } 10095 ImGui::EndTable(); 10096 } 10097 ImGui::PopStyleVar(); 10098 } 10099 10100 ImGuiIO& io = ImGui::GetIO(); 10101 ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.x + LayoutItemSpacing))); 10102 if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove)) 10103 { 10104 ImDrawList* draw_list = ImGui::GetWindowDrawList(); 10105 10106 const float avail_width = ImGui::GetContentRegionAvail().x; 10107 UpdateLayoutSizes(avail_width); 10108 10109 // Calculate and store start position. 10110 ImVec2 start_pos = ImGui::GetCursorScreenPos(); 10111 start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding); 10112 ImGui::SetCursorScreenPos(start_pos); 10113 10114 // Multi-select 10115 ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid; 10116 10117 // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items) 10118 if (AllowBoxSelect) 10119 ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; 10120 10121 // - This feature allows dragging an unselected item without selecting it (rarely used) 10122 if (AllowDragUnselected) 10123 ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; 10124 10125 // - Enable keyboard wrapping on X axis 10126 // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing: 10127 // ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); 10128 // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system) 10129 ms_flags |= ImGuiMultiSelectFlags_NavWrapX; 10130 10131 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size); 10132 10133 // Use custom selection adapter: store ID in selection (recommended) 10134 Selection.UserData = this; 10135 Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; }; 10136 Selection.ApplyRequests(ms_io); 10137 10138 const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete; 10139 const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1; 10140 RequestDelete = false; 10141 10142 // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items) 10143 // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... 10144 // But it is necessary for two reasons: 10145 // - Selectables uses it by default to visually fill the space between two items. 10146 // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do). 10147 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing)); 10148 10149 // Rendering parameters 10150 const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) }; 10151 const ImU32 icon_bg_color = ImGui::GetColorU32(ImGuiCol_MenuBarBg); 10152 const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f); 10153 const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x); 10154 10155 const int column_count = LayoutColumnCount; 10156 ImGuiListClipper clipper; 10157 clipper.Begin(LayoutLineCount, LayoutItemStep.y); 10158 if (item_curr_idx_to_focus != -1) 10159 clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped. 10160 if (ms_io->RangeSrcItem != -1) 10161 clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped. 10162 while (clipper.Step()) 10163 { 10164 for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++) 10165 { 10166 const int item_min_idx_for_current_line = line_idx * column_count; 10167 const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size); 10168 for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx) 10169 { 10170 ExampleAsset* item_data = &Items[item_idx]; 10171 ImGui::PushID((int)item_data->ID); 10172 10173 // Position item 10174 ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y); 10175 ImGui::SetCursorScreenPos(pos); 10176 10177 ImGui::SetNextItemSelectionUserData(item_idx); 10178 bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID); 10179 bool item_is_visible = ImGui::IsRectVisible(LayoutItemSize); 10180 ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize); 10181 10182 // Update our selection state immediately (without waiting for EndMultiSelect() requests) 10183 // because we use this to alter the color of our text/icon. 10184 if (ImGui::IsItemToggledSelection()) 10185 item_is_selected = !item_is_selected; 10186 10187 // Focus (for after deletion) 10188 if (item_curr_idx_to_focus == item_idx) 10189 ImGui::SetKeyboardFocusHere(-1); 10190 10191 // Drag and drop 10192 if (ImGui::BeginDragDropSource()) 10193 { 10194 // Create payload with full selection OR single unselected item. 10195 // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) 10196 if (ImGui::GetDragDropPayload() == NULL) 10197 { 10198 ImVector<ImGuiID> payload_items; 10199 void* it = NULL; 10200 ImGuiID id = 0; 10201 if (!item_is_selected) 10202 payload_items.push_back(item_data->ID); 10203 else 10204 while (Selection.GetNextSelectedItem(&it, &id)) 10205 payload_items.push_back(id); 10206 ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); 10207 } 10208 10209 // Display payload content in tooltip, by extracting it from the payload data 10210 // (we could read from selection, but it is more correct and reusable to read from payload) 10211 const ImGuiPayload* payload = ImGui::GetDragDropPayload(); 10212 const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID); 10213 ImGui::Text("%d assets", payload_count); 10214 10215 ImGui::EndDragDropSource(); 10216 } 10217 10218 // Render icon (a real app would likely display an image/thumbnail here) 10219 // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well. 10220 if (item_is_visible) 10221 { 10222 ImVec2 box_min(pos.x - 1, pos.y - 1); 10223 ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious 10224 draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color 10225 if (ShowTypeOverlay && item_data->Type != 0) 10226 { 10227 ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)]; 10228 draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col); 10229 } 10230 if (display_label) 10231 { 10232 ImU32 label_col = ImGui::GetColorU32(item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled); 10233 char label[32]; 10234 sprintf(label, "%d", item_data->ID); 10235 draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label); 10236 } 10237 } 10238 10239 ImGui::PopID(); 10240 } 10241 } 10242 } 10243 clipper.End(); 10244 ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing 10245 10246 // Context menu 10247 if (ImGui::BeginPopupContextWindow()) 10248 { 10249 ImGui::Text("Selection: %d items", Selection.Size); 10250 ImGui::Separator(); 10251 if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0)) 10252 RequestDelete = true; 10253 ImGui::EndPopup(); 10254 } 10255 10256 ms_io = ImGui::EndMultiSelect(); 10257 Selection.ApplyRequests(ms_io); 10258 if (want_delete) 10259 Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus); 10260 10261 // Zooming with CTRL+Wheel 10262 if (ImGui::IsWindowAppearing()) 10263 ZoomWheelAccum = 0.0f; 10264 if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) 10265 { 10266 ZoomWheelAccum += io.MouseWheel; 10267 if (fabsf(ZoomWheelAccum) >= 1.0f) 10268 { 10269 // Calculate hovered item index from mouse location 10270 // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it. 10271 const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x; 10272 const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y; 10273 const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx; 10274 //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 10275 10276 // Zoom 10277 IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum); 10278 IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f); 10279 ZoomWheelAccum -= (int)ZoomWheelAccum; 10280 UpdateLayoutSizes(avail_width); 10281 10282 // Manipulate scroll to that we will land at the same Y location of currently hovered item. 10283 // - Calculate next frame position of item under mouse 10284 // - Set new scroll position to be used in next ImGui::BeginChild() call. 10285 float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(hovered_item_ny, 1.0f)) * LayoutItemStep.y; 10286 hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y; 10287 float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y; 10288 ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y); 10289 } 10290 } 10291 } 10292 ImGui::EndChild(); 10293 10294 ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size); 10295 ImGui::End(); 10296 } 10297 }; 10298 10299 void ShowExampleAppAssetsBrowser(bool* p_open) 10300 { 10301 IMGUI_DEMO_MARKER("Examples/Assets Browser"); 10302 static ExampleAssetsBrowser assets_browser; 10303 assets_browser.Draw("Example: Assets Browser", p_open); 10304 } 10305 10306 // End of Demo code 10307 #else 10308 10309 void ImGui::ShowAboutWindow(bool*) {} 10310 void ImGui::ShowDemoWindow(bool*) {} 10311 void ImGui::ShowUserGuide() {} 10312 void ImGui::ShowStyleEditor(ImGuiStyle*) {} 10313 10314 #endif 10315 10316 #endif // #ifndef IMGUI_DISABLE