mirror of https://github.com/axmolengine/axmol.git
9143 lines
416 KiB
C++
9143 lines
416 KiB
C++
// dear imgui, v1.84
|
|
// (demo code)
|
|
|
|
// Help:
|
|
// - Read FAQ at http://dearimgui.org/faq
|
|
// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
|
|
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
|
|
// Read imgui.cpp for more details, documentation and comments.
|
|
// Get the latest version at https://github.com/ocornut/imgui
|
|
|
|
// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:
|
|
// Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other
|
|
// coders will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available
|
|
// debug menu of your game/app! Removing this file from your project is hindering access to documentation for everyone
|
|
// in your team, likely leading you to poorer usage of the library.
|
|
// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().
|
|
// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be
|
|
// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
|
|
// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.
|
|
// Thank you,
|
|
// -Your beloved friend, imgui_demo.cpp (which you won't delete)
|
|
|
|
// Message to beginner C/C++ programmers about the meaning of the 'static' keyword:
|
|
// In this demo code, we frequently use 'static' variables inside functions. A static variable persists across calls,
|
|
// so it is essentially like a global variable but declared inside the scope of the function. We do this as a way to
|
|
// gather code and data in the same place, to make the demo source code faster to read, faster to write, and smaller
|
|
// in size. It also happens to be a convenient way of storing simple UI related information as long as your function
|
|
// doesn't need to be reentrant or used in multiple threads. This might be a pattern you will want to use in your code,
|
|
// but most of the real data you would be editing is likely going to be stored outside your functions.
|
|
|
|
// The Demo code in this file is designed to be easy to copy-and-paste into your application!
|
|
// Because of this:
|
|
// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.
|
|
// - We try to declare static variables in the local scope, as close as possible to the code using them.
|
|
// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.
|
|
// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided
|
|
// by imgui_internal.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional
|
|
// and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
|
|
// Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
|
|
|
|
// Navigating this file:
|
|
// - In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12
|
|
// ("Edit.GoToImplementation") cannot.
|
|
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
|
|
|
|
/*
|
|
|
|
Index of this file:
|
|
|
|
// [SECTION] Forward Declarations, Helpers
|
|
// [SECTION] Demo Window / ShowDemoWindow()
|
|
// - sub section: ShowDemoWindowWidgets()
|
|
// - sub section: ShowDemoWindowLayout()
|
|
// - sub section: ShowDemoWindowPopups()
|
|
// - sub section: ShowDemoWindowTables()
|
|
// - sub section: ShowDemoWindowMisc()
|
|
// [SECTION] About Window / ShowAboutWindow()
|
|
// [SECTION] Style Editor / ShowStyleEditor()
|
|
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
|
|
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
|
|
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
|
|
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
|
|
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
|
|
// [SECTION] Example App: Long Text / ShowExampleAppLongText()
|
|
// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
|
|
// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
|
|
// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
|
|
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
|
|
// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()
|
|
// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
|
|
// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
|
|
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
|
|
|
|
*/
|
|
|
|
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
|
# define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
|
|
#include "imgui.h"
|
|
#ifndef IMGUI_DISABLE
|
|
|
|
// System includes
|
|
# include <ctype.h> // toupper
|
|
# include <limits.h> // INT_MIN, INT_MAX
|
|
# include <math.h> // sqrtf, powf, cosf, sinf, floorf, ceilf
|
|
# include <stdio.h> // vsnprintf, sscanf, printf
|
|
# include <stdlib.h> // NULL, malloc, free, atoi
|
|
# if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
|
# include <stddef.h> // intptr_t
|
|
# else
|
|
# include <stdint.h> // intptr_t
|
|
# endif
|
|
|
|
// Visual Studio warnings
|
|
# ifdef _MSC_VER
|
|
# pragma warning(disable : 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf,
|
|
// vsnprintf, sscanf, fopen
|
|
# pragma warning(disable : 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte
|
|
// value and then casting the result to a 8 byte value. Cast the value to the
|
|
// wider type before calling operator 'xxx' to avoid overflow(io.2).
|
|
# endif
|
|
|
|
// Clang/GCC warnings with -Weverything
|
|
# if defined(__clang__)
|
|
# if __has_warning("-Wunknown-warning-option")
|
|
# 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!
|
|
# endif
|
|
# pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
|
# pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more
|
|
// terse.
|
|
# 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)
|
|
# pragma clang diagnostic ignored \
|
|
"-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type
|
|
# pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal
|
|
# 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.
|
|
# 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.
|
|
# pragma clang diagnostic ignored \
|
|
"-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some
|
|
// standard header variations use #define NULL 0
|
|
# 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.
|
|
# pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
|
|
# pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx'
|
|
// to 'float' may lose precision
|
|
# elif defined(__GNUC__)
|
|
# pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
|
# pragma GCC diagnostic ignored \
|
|
"-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
|
|
# pragma GCC diagnostic ignored \
|
|
"-Wformat-security" // warning: format string is not a string literal (potentially insecure)
|
|
# pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double'
|
|
// when passing argument to function
|
|
# pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
|
|
# 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.
|
|
# endif
|
|
|
|
// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
|
|
# ifdef _WIN32
|
|
# define IM_NEWLINE "\r\n"
|
|
# else
|
|
# define IM_NEWLINE "\n"
|
|
# endif
|
|
|
|
// Helpers
|
|
# if defined(_MSC_VER) && !defined(snprintf)
|
|
# define snprintf _snprintf
|
|
# endif
|
|
# if defined(_MSC_VER) && !defined(vsnprintf)
|
|
# define vsnprintf _vsnprintf
|
|
# endif
|
|
|
|
// Format specifiers, printing 64-bit hasn't been decently standardized...
|
|
// In a real application you should be using PRId64 and PRIu64 from <inttypes.h> (non-windows) and on Windows define
|
|
// them yourself.
|
|
# ifdef _MSC_VER
|
|
# define IM_PRId64 "I64d"
|
|
# define IM_PRIu64 "I64u"
|
|
# else
|
|
# define IM_PRId64 "lld"
|
|
# define IM_PRIu64 "llu"
|
|
# endif
|
|
|
|
// Helpers macros
|
|
// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
|
|
// but making an exception here as those are largely simplifying code...
|
|
// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
|
|
# define IM_MIN(A, B) (((A) < (B)) ? (A) : (B))
|
|
# define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
|
|
# define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
|
|
|
|
// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed
|
|
// the default to e.g. __vectorcall
|
|
# ifndef IMGUI_CDECL
|
|
# ifdef _MSC_VER
|
|
# define IMGUI_CDECL __cdecl
|
|
# else
|
|
# define IMGUI_CDECL
|
|
# endif
|
|
# endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Forward Declarations, Helpers
|
|
//-----------------------------------------------------------------------------
|
|
|
|
# if !defined(IMGUI_DISABLE_DEMO_WINDOWS)
|
|
|
|
// Forward Declarations
|
|
static void ShowExampleAppDockSpace(bool* p_open);
|
|
static void ShowExampleAppDocuments(bool* p_open);
|
|
static void ShowExampleAppMainMenuBar();
|
|
static void ShowExampleAppConsole(bool* p_open);
|
|
static void ShowExampleAppLog(bool* p_open);
|
|
static void ShowExampleAppLayout(bool* p_open);
|
|
static void ShowExampleAppPropertyEditor(bool* p_open);
|
|
static void ShowExampleAppLongText(bool* p_open);
|
|
static void ShowExampleAppAutoResize(bool* p_open);
|
|
static void ShowExampleAppConstrainedResize(bool* p_open);
|
|
static void ShowExampleAppSimpleOverlay(bool* p_open);
|
|
static void ShowExampleAppFullscreen(bool* p_open);
|
|
static void ShowExampleAppWindowTitles(bool* p_open);
|
|
static void ShowExampleAppCustomRendering(bool* p_open);
|
|
static void ShowExampleMenuFile();
|
|
|
|
// Helper to display a little (?) mark which shows a tooltip when hovered.
|
|
// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
|
|
static void HelpMarker(const char* desc)
|
|
{
|
|
ImGui::TextDisabled("(?)");
|
|
if (ImGui::IsItemHovered())
|
|
{
|
|
ImGui::BeginTooltip();
|
|
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
|
ImGui::TextUnformatted(desc);
|
|
ImGui::PopTextWrapPos();
|
|
ImGui::EndTooltip();
|
|
}
|
|
}
|
|
|
|
static void ShowDockingDisabledMessage()
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui::Text("ERROR: Docking is not enabled! See Demo > Configuration.");
|
|
ImGui::Text("Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or ");
|
|
ImGui::SameLine(0.0f, 0.0f);
|
|
if (ImGui::SmallButton("click here"))
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
|
}
|
|
|
|
// Helper to display basic user controls.
|
|
void ImGui::ShowUserGuide()
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui::BulletText("Double-click on title bar to collapse window.");
|
|
ImGui::BulletText(
|
|
"Click and drag on lower corner to resize window\n"
|
|
"(double-click to auto fit window to its contents).");
|
|
ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text.");
|
|
ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
|
|
if (io.FontAllowUserScaling)
|
|
ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents.");
|
|
ImGui::BulletText("While inputing text:\n");
|
|
ImGui::Indent();
|
|
ImGui::BulletText("CTRL+Left/Right to word jump.");
|
|
ImGui::BulletText("CTRL+A or double-click to select all.");
|
|
ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
|
|
ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo.");
|
|
ImGui::BulletText("ESCAPE to revert.");
|
|
ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract.");
|
|
ImGui::Unindent();
|
|
ImGui::BulletText("With keyboard navigation enabled:");
|
|
ImGui::Indent();
|
|
ImGui::BulletText("Arrow keys to navigate.");
|
|
ImGui::BulletText("Space to activate a widget.");
|
|
ImGui::BulletText("Return to input text into a widget.");
|
|
ImGui::BulletText("Escape to deactivate a widget, close popup, exit child window.");
|
|
ImGui::BulletText("Alt to jump to the menu layer of a window.");
|
|
ImGui::BulletText("CTRL+Tab to select a window.");
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Demo Window / ShowDemoWindow()
|
|
//-----------------------------------------------------------------------------
|
|
// - ShowDemoWindowWidgets()
|
|
// - ShowDemoWindowLayout()
|
|
// - ShowDemoWindowPopups()
|
|
// - ShowDemoWindowTables()
|
|
// - ShowDemoWindowColumns()
|
|
// - ShowDemoWindowMisc()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// We split the contents of the big ShowDemoWindow() function into smaller functions
|
|
// (because the link time of very large functions grow non-linearly)
|
|
static void ShowDemoWindowWidgets();
|
|
static void ShowDemoWindowLayout();
|
|
static void ShowDemoWindowPopups();
|
|
static void ShowDemoWindowTables();
|
|
static void ShowDemoWindowColumns();
|
|
static void ShowDemoWindowMisc();
|
|
|
|
// Demonstrate most Dear ImGui features (this is big function!)
|
|
// You may execute this function to experiment with the UI and understand what it does.
|
|
// You may then search for keywords in the code when you are interested by a specific feature.
|
|
void ImGui::ShowDemoWindow(bool* p_open)
|
|
{
|
|
// Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
|
|
// Most ImGui functions would normally just crash if the context is missing.
|
|
IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!");
|
|
|
|
// Examples Apps (accessible from the "Examples" menu)
|
|
static bool show_app_main_menu_bar = false;
|
|
static bool show_app_dockspace = false;
|
|
static bool show_app_documents = false;
|
|
|
|
static bool show_app_console = false;
|
|
static bool show_app_log = false;
|
|
static bool show_app_layout = false;
|
|
static bool show_app_property_editor = false;
|
|
static bool show_app_long_text = false;
|
|
static bool show_app_auto_resize = false;
|
|
static bool show_app_constrained_resize = false;
|
|
static bool show_app_simple_overlay = false;
|
|
static bool show_app_fullscreen = false;
|
|
static bool show_app_window_titles = false;
|
|
static bool show_app_custom_rendering = false;
|
|
|
|
if (show_app_main_menu_bar)
|
|
ShowExampleAppMainMenuBar();
|
|
if (show_app_dockspace)
|
|
ShowExampleAppDockSpace(
|
|
&show_app_dockspace); // Process the Docking app first, as explicit DockSpace() nodes needs to be submitted
|
|
// early (read comments near the DockSpace function)
|
|
if (show_app_documents)
|
|
ShowExampleAppDocuments(
|
|
&show_app_documents); // Process the Document app next, as it may also use a DockSpace()
|
|
|
|
if (show_app_console)
|
|
ShowExampleAppConsole(&show_app_console);
|
|
if (show_app_log)
|
|
ShowExampleAppLog(&show_app_log);
|
|
if (show_app_layout)
|
|
ShowExampleAppLayout(&show_app_layout);
|
|
if (show_app_property_editor)
|
|
ShowExampleAppPropertyEditor(&show_app_property_editor);
|
|
if (show_app_long_text)
|
|
ShowExampleAppLongText(&show_app_long_text);
|
|
if (show_app_auto_resize)
|
|
ShowExampleAppAutoResize(&show_app_auto_resize);
|
|
if (show_app_constrained_resize)
|
|
ShowExampleAppConstrainedResize(&show_app_constrained_resize);
|
|
if (show_app_simple_overlay)
|
|
ShowExampleAppSimpleOverlay(&show_app_simple_overlay);
|
|
if (show_app_fullscreen)
|
|
ShowExampleAppFullscreen(&show_app_fullscreen);
|
|
if (show_app_window_titles)
|
|
ShowExampleAppWindowTitles(&show_app_window_titles);
|
|
if (show_app_custom_rendering)
|
|
ShowExampleAppCustomRendering(&show_app_custom_rendering);
|
|
|
|
// Dear ImGui Apps (accessible from the "Tools" menu)
|
|
static bool show_app_metrics = false;
|
|
static bool show_app_style_editor = false;
|
|
static bool show_app_about = false;
|
|
|
|
if (show_app_metrics)
|
|
{
|
|
ImGui::ShowMetricsWindow(&show_app_metrics);
|
|
}
|
|
if (show_app_about)
|
|
{
|
|
ImGui::ShowAboutWindow(&show_app_about);
|
|
}
|
|
if (show_app_style_editor)
|
|
{
|
|
ImGui::Begin("Dear ImGui Style Editor", &show_app_style_editor);
|
|
ImGui::ShowStyleEditor();
|
|
ImGui::End();
|
|
}
|
|
|
|
// Demonstrate the various window flags. Typically you would just use the default!
|
|
static bool no_titlebar = false;
|
|
static bool no_scrollbar = false;
|
|
static bool no_menu = false;
|
|
static bool no_move = false;
|
|
static bool no_resize = false;
|
|
static bool no_collapse = false;
|
|
static bool no_close = false;
|
|
static bool no_nav = false;
|
|
static bool no_background = false;
|
|
static bool no_bring_to_front = false;
|
|
static bool no_docking = false;
|
|
static bool unsaved_document = false;
|
|
|
|
ImGuiWindowFlags window_flags = 0;
|
|
if (no_titlebar)
|
|
window_flags |= ImGuiWindowFlags_NoTitleBar;
|
|
if (no_scrollbar)
|
|
window_flags |= ImGuiWindowFlags_NoScrollbar;
|
|
if (!no_menu)
|
|
window_flags |= ImGuiWindowFlags_MenuBar;
|
|
if (no_move)
|
|
window_flags |= ImGuiWindowFlags_NoMove;
|
|
if (no_resize)
|
|
window_flags |= ImGuiWindowFlags_NoResize;
|
|
if (no_collapse)
|
|
window_flags |= ImGuiWindowFlags_NoCollapse;
|
|
if (no_nav)
|
|
window_flags |= ImGuiWindowFlags_NoNav;
|
|
if (no_background)
|
|
window_flags |= ImGuiWindowFlags_NoBackground;
|
|
if (no_bring_to_front)
|
|
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
|
|
if (no_docking)
|
|
window_flags |= ImGuiWindowFlags_NoDocking;
|
|
if (unsaved_document)
|
|
window_flags |= ImGuiWindowFlags_UnsavedDocument;
|
|
if (no_close)
|
|
p_open = NULL; // Don't pass our bool* to Begin
|
|
|
|
// We specify a default position/size in case there's no data in the .ini file.
|
|
// We only do it to make the demo applications a little more welcoming, but typically this isn't required.
|
|
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
|
ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20),
|
|
ImGuiCond_FirstUseEver);
|
|
ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver);
|
|
|
|
// Main body of the Demo window starts here.
|
|
if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags))
|
|
{
|
|
// Early out if the window is collapsed, as an optimization.
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Most "big" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details.
|
|
|
|
// e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align)
|
|
// ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f);
|
|
|
|
// e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets.
|
|
ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
|
|
|
|
// Menu Bar
|
|
if (ImGui::BeginMenuBar())
|
|
{
|
|
if (ImGui::BeginMenu("Menu"))
|
|
{
|
|
ShowExampleMenuFile();
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Examples"))
|
|
{
|
|
ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar);
|
|
ImGui::MenuItem("Console", NULL, &show_app_console);
|
|
ImGui::MenuItem("Log", NULL, &show_app_log);
|
|
ImGui::MenuItem("Simple layout", NULL, &show_app_layout);
|
|
ImGui::MenuItem("Property editor", NULL, &show_app_property_editor);
|
|
ImGui::MenuItem("Long text display", NULL, &show_app_long_text);
|
|
ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize);
|
|
ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize);
|
|
ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay);
|
|
ImGui::MenuItem("Fullscreen window", NULL, &show_app_fullscreen);
|
|
ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles);
|
|
ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering);
|
|
ImGui::MenuItem("Dockspace", NULL, &show_app_dockspace);
|
|
ImGui::MenuItem("Documents", NULL, &show_app_documents);
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Tools"))
|
|
{
|
|
ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics);
|
|
ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor);
|
|
ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about);
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenuBar();
|
|
}
|
|
|
|
ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION);
|
|
ImGui::Spacing();
|
|
|
|
if (ImGui::CollapsingHeader("Help"))
|
|
{
|
|
ImGui::Text("ABOUT THIS DEMO:");
|
|
ImGui::BulletText("Sections below are demonstrating many aspects of the library.");
|
|
ImGui::BulletText("The \"Examples\" menu above leads to more demo contents.");
|
|
ImGui::BulletText(
|
|
"The \"Tools\" menu above gives access to: About Box, Style Editor,\n"
|
|
"and Metrics/Debugger (general purpose Dear ImGui debugging tool).");
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("PROGRAMMER GUIDE:");
|
|
ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!");
|
|
ImGui::BulletText("See comments in imgui.cpp.");
|
|
ImGui::BulletText("See example applications in the examples/ folder.");
|
|
ImGui::BulletText("Read the FAQ at http://www.dearimgui.org/faq/");
|
|
ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.");
|
|
ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.");
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("USER GUIDE:");
|
|
ImGui::ShowUserGuide();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Configuration"))
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
if (ImGui::TreeNode("Configuration##2"))
|
|
{
|
|
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags,
|
|
ImGuiConfigFlags_NavEnableKeyboard);
|
|
ImGui::SameLine();
|
|
HelpMarker("Enable keyboard controls.");
|
|
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags,
|
|
ImGuiConfigFlags_NavEnableGamepad);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Enable gamepad controls. Require backend to set io.BackendFlags |= "
|
|
"ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
|
|
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", &io.ConfigFlags,
|
|
ImGuiConfigFlags_NavEnableSetMousePos);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos.");
|
|
ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", &io.ConfigFlags, ImGuiConfigFlags_NoMouse);
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
|
|
{
|
|
// The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix
|
|
// it:
|
|
if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("<<PRESS SPACE TO DISABLE>>");
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space)))
|
|
io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
|
|
}
|
|
ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags,
|
|
ImGuiConfigFlags_NoMouseCursorChange);
|
|
ImGui::SameLine();
|
|
HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.");
|
|
|
|
ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from "
|
|
"window menu button (upper-left button) to undock an entire node (all windows).");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
|
{
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Simplified docking mode: disable window splitting, so docking is limited to merging multiple "
|
|
"windows together into tab-bars.");
|
|
ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar);
|
|
ImGui::SameLine();
|
|
HelpMarker("Create a docking node and tab-bar on single floating windows.");
|
|
ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Make window or viewport transparent when docking and only display docking boxes on the target "
|
|
"viewport. Useful if rendering of multiple viewport cannot be synced. Best used with "
|
|
"ConfigViewportsNoAutoMerge.");
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", &io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable);
|
|
ImGui::SameLine();
|
|
HelpMarker("[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details.");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
|
{
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("io.ConfigViewportsNoAutoMerge", &io.ConfigViewportsNoAutoMerge);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Set to make all floating imgui windows always create their own viewport. Otherwise, they are "
|
|
"merged into the main host viewports when overlapping it.");
|
|
ImGui::Checkbox("io.ConfigViewportsNoTaskBarIcon", &io.ConfigViewportsNoTaskBarIcon);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Toggling this at runtime is normally unsupported (most platform backends won't refresh the task "
|
|
"bar icon state right away).");
|
|
ImGui::Checkbox("io.ConfigViewportsNoDecoration", &io.ConfigViewportsNoDecoration);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Toggling this at runtime is normally unsupported (most platform backends won't refresh the "
|
|
"decoration right away).");
|
|
ImGui::Checkbox("io.ConfigViewportsNoDefaultParent", &io.ConfigViewportsNoDefaultParent);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Toggling this at runtime is normally unsupported (most platform backends won't refresh the "
|
|
"parenting right away).");
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
|
|
ImGui::SameLine();
|
|
HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)");
|
|
ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
|
|
ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
|
|
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.");
|
|
ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
|
|
ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
|
|
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).");
|
|
ImGui::Text("Also see Style->Rendering for rendering options.");
|
|
ImGui::TreePop();
|
|
ImGui::Separator();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Backend Flags"))
|
|
{
|
|
HelpMarker(
|
|
"Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n"
|
|
"Here we expose them as read-only fields to avoid breaking interactions with your backend.");
|
|
|
|
// Make a local copy to avoid modifying actual backend flags.
|
|
ImGuiBackendFlags backend_flags = io.BackendFlags;
|
|
ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &backend_flags, ImGuiBackendFlags_HasGamepad);
|
|
ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &backend_flags, ImGuiBackendFlags_HasMouseCursors);
|
|
ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &backend_flags, ImGuiBackendFlags_HasSetMousePos);
|
|
ImGui::CheckboxFlags("io.BackendFlags: PlatformHasViewports", &backend_flags,
|
|
ImGuiBackendFlags_PlatformHasViewports);
|
|
ImGui::CheckboxFlags("io.BackendFlags: HasMouseHoveredViewport", &backend_flags,
|
|
ImGuiBackendFlags_HasMouseHoveredViewport);
|
|
ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &backend_flags,
|
|
ImGuiBackendFlags_RendererHasVtxOffset);
|
|
ImGui::CheckboxFlags("io.BackendFlags: RendererHasViewports", &backend_flags,
|
|
ImGuiBackendFlags_RendererHasViewports);
|
|
ImGui::TreePop();
|
|
ImGui::Separator();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Style"))
|
|
{
|
|
HelpMarker(
|
|
"The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() "
|
|
"function.");
|
|
ImGui::ShowStyleEditor();
|
|
ImGui::TreePop();
|
|
ImGui::Separator();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Capture/Logging"))
|
|
{
|
|
HelpMarker(
|
|
"The logging API redirects all text output so you can easily capture the content of "
|
|
"a window or a block. Tree nodes can be automatically expanded.\n"
|
|
"Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
|
|
ImGui::LogButtons();
|
|
|
|
HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output.");
|
|
if (ImGui::Button("Copy \"Hello, world!\" to clipboard"))
|
|
{
|
|
ImGui::LogToClipboard();
|
|
ImGui::LogText("Hello, world!");
|
|
ImGui::LogFinish();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Window options"))
|
|
{
|
|
if (ImGui::BeginTable("split", 3))
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No titlebar", &no_titlebar);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No scrollbar", &no_scrollbar);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No menu", &no_menu);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No move", &no_move);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No resize", &no_resize);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No collapse", &no_collapse);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No close", &no_close);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No nav", &no_nav);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No background", &no_background);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No bring to front", &no_bring_to_front);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("No docking", &no_docking);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("Unsaved document", &unsaved_document);
|
|
ImGui::EndTable();
|
|
}
|
|
}
|
|
|
|
// All demo contents
|
|
ShowDemoWindowWidgets();
|
|
ShowDemoWindowLayout();
|
|
ShowDemoWindowPopups();
|
|
ShowDemoWindowTables();
|
|
ShowDemoWindowMisc();
|
|
|
|
// End of ShowDemoWindow()
|
|
ImGui::PopItemWidth();
|
|
ImGui::End();
|
|
}
|
|
|
|
static void ShowDemoWindowWidgets()
|
|
{
|
|
if (!ImGui::CollapsingHeader("Widgets"))
|
|
return;
|
|
|
|
static bool disable_all = false; // The Checkbox for that is inside the "Disabled" section at the bottom
|
|
if (disable_all)
|
|
ImGui::BeginDisabled();
|
|
|
|
if (ImGui::TreeNode("Basic"))
|
|
{
|
|
static int clicked = 0;
|
|
if (ImGui::Button("Button"))
|
|
clicked++;
|
|
if (clicked & 1)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("Thanks for clicking me!");
|
|
}
|
|
|
|
static bool check = true;
|
|
ImGui::Checkbox("checkbox", &check);
|
|
|
|
static int e = 0;
|
|
ImGui::RadioButton("radio a", &e, 0);
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton("radio b", &e, 1);
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton("radio c", &e, 2);
|
|
|
|
// Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
if (i > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(i);
|
|
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));
|
|
ImGui::Button("Click");
|
|
ImGui::PopStyleColor(3);
|
|
ImGui::PopID();
|
|
}
|
|
|
|
// Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
|
|
// (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
|
|
// See 'Demo->Layout->Text Baseline Alignment' for details.
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("Hold to repeat:");
|
|
ImGui::SameLine();
|
|
|
|
// Arrow buttons with Repeater
|
|
static int counter = 0;
|
|
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
|
|
ImGui::PushButtonRepeat(true);
|
|
if (ImGui::ArrowButton("##left", ImGuiDir_Left))
|
|
{
|
|
counter--;
|
|
}
|
|
ImGui::SameLine(0.0f, spacing);
|
|
if (ImGui::ArrowButton("##right", ImGuiDir_Right))
|
|
{
|
|
counter++;
|
|
}
|
|
ImGui::PopButtonRepeat();
|
|
ImGui::SameLine();
|
|
ImGui::Text("%d", counter);
|
|
|
|
ImGui::Text("Hover over me");
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("I am a tooltip");
|
|
|
|
ImGui::SameLine();
|
|
ImGui::Text("- or me");
|
|
if (ImGui::IsItemHovered())
|
|
{
|
|
ImGui::BeginTooltip();
|
|
ImGui::Text("I am a fancy tooltip");
|
|
static float arr[] = {0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f};
|
|
ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr));
|
|
ImGui::EndTooltip();
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::LabelText("label", "Value");
|
|
|
|
{
|
|
// Using the _simplified_ one-liner Combo() api here
|
|
// See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.
|
|
const char* items[] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF",
|
|
"GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK"};
|
|
static int item_current = 0;
|
|
ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items));
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Using the simplified one-liner Combo API here.\nRefer to the \"Combo\" section below for an "
|
|
"explanation of how to use the more flexible and general BeginCombo/EndCombo API.");
|
|
}
|
|
|
|
{
|
|
// To wire InputText() with std::string or any other custom string type,
|
|
// see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
|
|
static char str0[128] = "Hello, world!";
|
|
ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0));
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"USER:\n"
|
|
"Hold SHIFT or use mouse to select text.\n"
|
|
"CTRL+Left/Right to word jump.\n"
|
|
"CTRL+A or double-click to select all.\n"
|
|
"CTRL+X,CTRL+C,CTRL+V clipboard.\n"
|
|
"CTRL+Z,CTRL+Y undo/redo.\n"
|
|
"ESCAPE to revert.\n\n"
|
|
"PROGRAMMER:\n"
|
|
"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
|
|
"to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
|
|
"in imgui_demo.cpp).");
|
|
|
|
static char str1[128] = "";
|
|
ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1));
|
|
|
|
static int i0 = 123;
|
|
ImGui::InputInt("input int", &i0);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"You can apply arithmetic operators +,*,/ on numerical values.\n"
|
|
" e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\n"
|
|
"Use +- to subtract.");
|
|
|
|
static float f0 = 0.001f;
|
|
ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f");
|
|
|
|
static double d0 = 999999.00000001;
|
|
ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f");
|
|
|
|
static float f1 = 1.e10f;
|
|
ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"You can input value using the scientific notation,\n"
|
|
" e.g. \"1e+8\" becomes \"100000000\".");
|
|
|
|
static float vec4a[4] = {0.10f, 0.20f, 0.30f, 0.44f};
|
|
ImGui::InputFloat3("input float3", vec4a);
|
|
}
|
|
|
|
{
|
|
static int i1 = 50, i2 = 42;
|
|
ImGui::DragInt("drag int", &i1, 1);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Click and drag to edit value.\n"
|
|
"Hold SHIFT/ALT for faster/slower edit.\n"
|
|
"Double-click or CTRL+click to input value.");
|
|
|
|
ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp);
|
|
|
|
static float f1 = 1.00f, f2 = 0.0067f;
|
|
ImGui::DragFloat("drag float", &f1, 0.005f);
|
|
ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns");
|
|
}
|
|
|
|
{
|
|
static int i1 = 0;
|
|
ImGui::SliderInt("slider int", &i1, -1, 3);
|
|
ImGui::SameLine();
|
|
HelpMarker("CTRL+click to input value.");
|
|
|
|
static float f1 = 0.123f, f2 = 0.0f;
|
|
ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
|
|
ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic);
|
|
|
|
static float angle = 0.0f;
|
|
ImGui::SliderAngle("slider angle", &angle);
|
|
|
|
// Using the format string to display a name instead of an integer.
|
|
// Here we completely omit '%d' from the format string, so it'll only display a name.
|
|
// This technique can also be used with DragInt().
|
|
enum Element
|
|
{
|
|
Element_Fire,
|
|
Element_Earth,
|
|
Element_Air,
|
|
Element_Water,
|
|
Element_COUNT
|
|
};
|
|
static int elem = Element_Fire;
|
|
const char* elems_names[Element_COUNT] = {"Fire", "Earth", "Air", "Water"};
|
|
const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
|
|
ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name);
|
|
ImGui::SameLine();
|
|
HelpMarker("Using the format string parameter to display a name instead of the underlying integer.");
|
|
}
|
|
|
|
{
|
|
static float col1[3] = {1.0f, 0.0f, 0.2f};
|
|
static float col2[4] = {0.4f, 0.7f, 0.0f, 0.5f};
|
|
ImGui::ColorEdit3("color 1", col1);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Click on the color square to open a color picker.\n"
|
|
"Click and hold to use drag and drop.\n"
|
|
"Right-click on the color square to show options.\n"
|
|
"CTRL+click on individual component to input value.\n");
|
|
|
|
ImGui::ColorEdit4("color 2", col2);
|
|
}
|
|
|
|
{
|
|
// Using the _simplified_ one-liner ListBox() api here
|
|
// See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.
|
|
const char* items[] = {"Apple", "Banana", "Cherry", "Kiwi", "Mango",
|
|
"Orange", "Pineapple", "Strawberry", "Watermelon"};
|
|
static int item_current = 1;
|
|
ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Using the simplified one-liner ListBox API here.\nRefer to the \"List boxes\" section below for an "
|
|
"explanation of how to use the more flexible and general BeginListBox/EndListBox API.");
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Testing ImGuiOnceUponAFrame helper.
|
|
// static ImGuiOnceUponAFrame once;
|
|
// for (int i = 0; i < 5; i++)
|
|
// if (once)
|
|
// ImGui::Text("This will be displayed only once.");
|
|
|
|
if (ImGui::TreeNode("Trees"))
|
|
{
|
|
if (ImGui::TreeNode("Basic trees"))
|
|
{
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
// Use SetNextItemOpen() so set the default state of a node to be open. We could
|
|
// also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
|
|
if (i == 0)
|
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
|
|
|
if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i))
|
|
{
|
|
ImGui::Text("blah blah");
|
|
ImGui::SameLine();
|
|
if (ImGui::SmallButton("button"))
|
|
{}
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Advanced, with Selectable nodes"))
|
|
{
|
|
HelpMarker(
|
|
"This is a more typical looking tree with selectable nodes.\n"
|
|
"Click to select, CTRL+Click to toggle, click on arrows or double-click to open.");
|
|
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow |
|
|
ImGuiTreeNodeFlags_OpenOnDoubleClick |
|
|
ImGuiTreeNodeFlags_SpanAvailWidth;
|
|
static bool align_label_with_current_x_position = false;
|
|
static bool test_drag_and_drop = false;
|
|
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
|
|
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags,
|
|
ImGuiTreeNodeFlags_OpenOnDoubleClick);
|
|
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.");
|
|
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth);
|
|
ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
|
|
ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop);
|
|
ImGui::Text("Hello!");
|
|
if (align_label_with_current_x_position)
|
|
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
|
|
|
|
// 'selection_mask' is dumb representation of what may be user-side selection state.
|
|
// You may retain selection state inside or outside your objects in whatever format you see fit.
|
|
// 'node_clicked' is temporary storage of what node we have clicked to process selection at the end
|
|
/// of the loop. May be a pointer to your own node type, etc.
|
|
static int selection_mask = (1 << 2);
|
|
int node_clicked = -1;
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
// Disable the default "open on single-click behavior" + set Selected flag according to our selection.
|
|
ImGuiTreeNodeFlags node_flags = base_flags;
|
|
const bool is_selected = (selection_mask & (1 << i)) != 0;
|
|
if (is_selected)
|
|
node_flags |= ImGuiTreeNodeFlags_Selected;
|
|
if (i < 3)
|
|
{
|
|
// Items 0..2 are Tree Node
|
|
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i);
|
|
if (ImGui::IsItemClicked())
|
|
node_clicked = i;
|
|
if (test_drag_and_drop && ImGui::BeginDragDropSource())
|
|
{
|
|
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
|
|
ImGui::Text("This is a drag and drop source");
|
|
ImGui::EndDragDropSource();
|
|
}
|
|
if (node_open)
|
|
{
|
|
ImGui::BulletText("Blah blah\nBlah Blah");
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Items 3..5 are Tree Leaves
|
|
// The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
|
|
// use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
|
|
node_flags |=
|
|
ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
|
|
ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i);
|
|
if (ImGui::IsItemClicked())
|
|
node_clicked = i;
|
|
if (test_drag_and_drop && ImGui::BeginDragDropSource())
|
|
{
|
|
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
|
|
ImGui::Text("This is a drag and drop source");
|
|
ImGui::EndDragDropSource();
|
|
}
|
|
}
|
|
}
|
|
if (node_clicked != -1)
|
|
{
|
|
// Update selection state
|
|
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
|
|
if (ImGui::GetIO().KeyCtrl)
|
|
selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
|
|
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
|
|
selection_mask = (1 << node_clicked); // Click to single-select
|
|
}
|
|
if (align_label_with_current_x_position)
|
|
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Collapsing Headers"))
|
|
{
|
|
static bool closable_group = true;
|
|
ImGui::Checkbox("Show 2nd header", &closable_group);
|
|
if (ImGui::CollapsingHeader("Header", ImGuiTreeNodeFlags_None))
|
|
{
|
|
ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
|
|
for (int i = 0; i < 5; i++)
|
|
ImGui::Text("Some content %d", i);
|
|
}
|
|
if (ImGui::CollapsingHeader("Header with a close button", &closable_group))
|
|
{
|
|
ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
|
|
for (int i = 0; i < 5; i++)
|
|
ImGui::Text("More content %d", i);
|
|
}
|
|
/*
|
|
if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet))
|
|
ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
|
|
*/
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Bullets"))
|
|
{
|
|
ImGui::BulletText("Bullet point 1");
|
|
ImGui::BulletText("Bullet point 2\nOn multiple lines");
|
|
if (ImGui::TreeNode("Tree node"))
|
|
{
|
|
ImGui::BulletText("Another bullet point");
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::Bullet();
|
|
ImGui::Text("Bullet point 3 (two calls)");
|
|
ImGui::Bullet();
|
|
ImGui::SmallButton("Button");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Text"))
|
|
{
|
|
if (ImGui::TreeNode("Colorful Text"))
|
|
{
|
|
// Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
|
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Pink");
|
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Yellow");
|
|
ImGui::TextDisabled("Disabled");
|
|
ImGui::SameLine();
|
|
HelpMarker("The TextDisabled color is stored in ImGuiStyle.");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Word Wrapping"))
|
|
{
|
|
// Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
|
|
ImGui::TextWrapped(
|
|
"This text should automatically wrap on the edge of the window. The current implementation "
|
|
"for text wrapping follows simple rules suitable for English and possibly other languages.");
|
|
ImGui::Spacing();
|
|
|
|
static float wrap_width = 200.0f;
|
|
ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f");
|
|
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
for (int n = 0; n < 2; n++)
|
|
{
|
|
ImGui::Text("Test paragraph %d:", n);
|
|
ImVec2 pos = ImGui::GetCursorScreenPos();
|
|
ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);
|
|
ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());
|
|
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
|
|
if (n == 0)
|
|
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);
|
|
else
|
|
ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh");
|
|
|
|
// Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
|
|
draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));
|
|
draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255));
|
|
ImGui::PopTextWrapPos();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("UTF-8 Text"))
|
|
{
|
|
// UTF-8 test with Japanese characters
|
|
// (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
|
|
// - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
|
|
// - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
|
|
// can save your source files as 'UTF-8 without signature').
|
|
// - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
|
|
// CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
|
|
// Don't do this in your application! Please use u8"text in any language" in your application!
|
|
// Note that characters values are preserved even by InputText() if the font cannot be displayed,
|
|
// so you can safely copy & paste garbled characters into another application.
|
|
ImGui::TextWrapped(
|
|
"CJK text will only appears if the font was loaded with the appropriate CJK character ranges. "
|
|
"Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. "
|
|
"Read docs/FONTS.md for details.");
|
|
ImGui::Text(
|
|
"Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we
|
|
// would use
|
|
// u8"blah
|
|
// blah" with
|
|
// the proper
|
|
// characters
|
|
// directly in
|
|
// the string.
|
|
ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
|
|
static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
|
|
// static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis
|
|
ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf));
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Images"))
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui::TextWrapped(
|
|
"Below we are displaying the font texture (which is the only texture we have access to in this demo). "
|
|
"Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. "
|
|
"Hover the texture for a zoomed view!");
|
|
|
|
// Below we are displaying the font texture because it is the only texture we have access to inside the demo!
|
|
// Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that
|
|
// will be passed to the rendering backend via the ImDrawCmd structure.
|
|
// If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
|
|
// of their respective source file to specify what they expect to be stored in ImTextureID, for example:
|
|
// - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer
|
|
// - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
|
|
// More:
|
|
// - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
|
|
// to ImGui::Image(), and gather width/height through your own functions, etc.
|
|
// - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
|
|
// it will help you debug issues if you are confused about it.
|
|
// - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
|
|
// - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
|
|
// - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
|
|
ImTextureID my_tex_id = io.Fonts->TexID;
|
|
float my_tex_w = (float)io.Fonts->TexWidth;
|
|
float my_tex_h = (float)io.Fonts->TexHeight;
|
|
{
|
|
ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
|
|
ImVec2 pos = ImGui::GetCursorScreenPos();
|
|
ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
|
|
ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
|
|
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
|
|
ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white
|
|
ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col);
|
|
if (ImGui::IsItemHovered())
|
|
{
|
|
ImGui::BeginTooltip();
|
|
float region_sz = 32.0f;
|
|
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
|
|
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
|
|
float zoom = 4.0f;
|
|
if (region_x < 0.0f)
|
|
{
|
|
region_x = 0.0f;
|
|
}
|
|
else if (region_x > my_tex_w - region_sz)
|
|
{
|
|
region_x = my_tex_w - region_sz;
|
|
}
|
|
if (region_y < 0.0f)
|
|
{
|
|
region_y = 0.0f;
|
|
}
|
|
else if (region_y > my_tex_h - region_sz)
|
|
{
|
|
region_y = my_tex_h - region_sz;
|
|
}
|
|
ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y);
|
|
ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
|
|
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
|
|
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
|
|
ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, tint_col, border_col);
|
|
ImGui::EndTooltip();
|
|
}
|
|
}
|
|
ImGui::TextWrapped("And now some textured buttons..");
|
|
static int pressed_count = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
ImGui::PushID(i);
|
|
int frame_padding = -1 + i; // -1 == uses default padding (style.FramePadding)
|
|
ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible
|
|
ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left
|
|
ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h); // UV coordinates for (32,32) in our texture
|
|
ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background
|
|
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
|
|
if (ImGui::ImageButton(my_tex_id, size, uv0, uv1, frame_padding, bg_col, tint_col))
|
|
pressed_count += 1;
|
|
ImGui::PopID();
|
|
ImGui::SameLine();
|
|
}
|
|
ImGui::NewLine();
|
|
ImGui::Text("Pressed %d times.", pressed_count);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Combo"))
|
|
{
|
|
// Expose flags as checkbox for the demo
|
|
static ImGuiComboFlags flags = 0;
|
|
ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft);
|
|
ImGui::SameLine();
|
|
HelpMarker("Only makes a difference if the popup is larger than the combo");
|
|
if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton))
|
|
flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both
|
|
if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview))
|
|
flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both
|
|
|
|
// Using the generic BeginCombo() API, you have full control over how to display the combo contents.
|
|
// (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
|
|
// stored in the object itself, etc.)
|
|
const char* items[] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG",
|
|
"HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO"};
|
|
static int item_current_idx = 0; // Here we store our selection data as an index.
|
|
const char* combo_preview_value = items[item_current_idx]; // Pass in the preview value visible before opening
|
|
// the combo (it could be anything)
|
|
if (ImGui::BeginCombo("combo 1", combo_preview_value, flags))
|
|
{
|
|
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
|
|
{
|
|
const bool is_selected = (item_current_idx == n);
|
|
if (ImGui::Selectable(items[n], is_selected))
|
|
item_current_idx = n;
|
|
|
|
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
|
if (is_selected)
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
|
|
// Simplified one-liner Combo() API, using values packed in a single constant string
|
|
// This is a convenience for when the selection set is small and known at compile-time.
|
|
static int item_current_2 = 0;
|
|
ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
|
|
|
|
// Simplified one-liner Combo() using an array of const char*
|
|
// This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
|
|
static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview
|
|
ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items));
|
|
|
|
// Simplified one-liner Combo() using an accessor function
|
|
struct Funcs
|
|
{
|
|
static bool ItemGetter(void* data, int n, const char** out_str)
|
|
{
|
|
*out_str = ((const char**)data)[n];
|
|
return true;
|
|
}
|
|
};
|
|
static int item_current_4 = 0;
|
|
ImGui::Combo("combo 4 (function)", &item_current_4, &Funcs::ItemGetter, items, IM_ARRAYSIZE(items));
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("List boxes"))
|
|
{
|
|
// Using the generic BeginListBox() API, you have full control over how to display the combo contents.
|
|
// (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
|
|
// stored in the object itself, etc.)
|
|
const char* items[] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG",
|
|
"HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO"};
|
|
static int item_current_idx = 0; // Here we store our selection data as an index.
|
|
if (ImGui::BeginListBox("listbox 1"))
|
|
{
|
|
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
|
|
{
|
|
const bool is_selected = (item_current_idx == n);
|
|
if (ImGui::Selectable(items[n], is_selected))
|
|
item_current_idx = n;
|
|
|
|
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
|
if (is_selected)
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
ImGui::EndListBox();
|
|
}
|
|
|
|
// Custom size: use all width, 5 items tall
|
|
ImGui::Text("Full-width:");
|
|
if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
|
|
{
|
|
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
|
|
{
|
|
const bool is_selected = (item_current_idx == n);
|
|
if (ImGui::Selectable(items[n], is_selected))
|
|
item_current_idx = n;
|
|
|
|
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
|
if (is_selected)
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
ImGui::EndListBox();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Selectables"))
|
|
{
|
|
// Selectable() has 2 overloads:
|
|
// - The one taking "bool selected" as a read-only selection information.
|
|
// When Selectable() has been clicked it returns true and you can alter selection state accordingly.
|
|
// - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
|
|
// The earlier is more flexible, as in real application your selection may be stored in many different ways
|
|
// and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
|
|
if (ImGui::TreeNode("Basic"))
|
|
{
|
|
static bool selection[5] = {false, true, false, false, false};
|
|
ImGui::Selectable("1. I am selectable", &selection[0]);
|
|
ImGui::Selectable("2. I am selectable", &selection[1]);
|
|
ImGui::Text("(I am not selectable)");
|
|
ImGui::Selectable("4. I am selectable", &selection[3]);
|
|
if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick))
|
|
if (ImGui::IsMouseDoubleClicked(0))
|
|
selection[4] = !selection[4];
|
|
ImGui::TreePop();
|
|
}
|
|
if (ImGui::TreeNode("Selection State: Single Selection"))
|
|
{
|
|
static int selected = -1;
|
|
for (int n = 0; n < 5; n++)
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "Object %d", n);
|
|
if (ImGui::Selectable(buf, selected == n))
|
|
selected = n;
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
if (ImGui::TreeNode("Selection State: Multiple Selection"))
|
|
{
|
|
HelpMarker("Hold CTRL and click to select multiple items.");
|
|
static bool selection[5] = {false, false, false, false, false};
|
|
for (int n = 0; n < 5; n++)
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "Object %d", n);
|
|
if (ImGui::Selectable(buf, selection[n]))
|
|
{
|
|
if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held
|
|
memset(selection, 0, sizeof(selection));
|
|
selection[n] ^= 1;
|
|
}
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
if (ImGui::TreeNode("Rendering more text into the same line"))
|
|
{
|
|
// Using the Selectable() override that takes "bool* p_selected" parameter,
|
|
// this function toggle your bool value automatically.
|
|
static bool selected[3] = {false, false, false};
|
|
ImGui::Selectable("main.c", &selected[0]);
|
|
ImGui::SameLine(300);
|
|
ImGui::Text(" 2,345 bytes");
|
|
ImGui::Selectable("Hello.cpp", &selected[1]);
|
|
ImGui::SameLine(300);
|
|
ImGui::Text("12,345 bytes");
|
|
ImGui::Selectable("Hello.h", &selected[2]);
|
|
ImGui::SameLine(300);
|
|
ImGui::Text(" 2,345 bytes");
|
|
ImGui::TreePop();
|
|
}
|
|
if (ImGui::TreeNode("In columns"))
|
|
{
|
|
static bool selected[10] = {};
|
|
|
|
if (ImGui::BeginTable(
|
|
"split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
|
|
{
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
char label[32];
|
|
sprintf(label, "Item %d", i);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::Spacing();
|
|
if (ImGui::BeginTable(
|
|
"split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
|
|
{
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
char label[32];
|
|
sprintf(label, "Item %d", i);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Some other contents");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("123456");
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
if (ImGui::TreeNode("Grid"))
|
|
{
|
|
static char selected[4][4] = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};
|
|
|
|
// Add in a bit of silly fun...
|
|
const float time = (float)ImGui::GetTime();
|
|
const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected...
|
|
if (winning_state)
|
|
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign,
|
|
ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f)));
|
|
|
|
for (int y = 0; y < 4; y++)
|
|
for (int x = 0; x < 4; x++)
|
|
{
|
|
if (x > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(y * 4 + x);
|
|
if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(50, 50)))
|
|
{
|
|
// Toggle clicked cell + toggle neighbors
|
|
selected[y][x] ^= 1;
|
|
if (x > 0)
|
|
{
|
|
selected[y][x - 1] ^= 1;
|
|
}
|
|
if (x < 3)
|
|
{
|
|
selected[y][x + 1] ^= 1;
|
|
}
|
|
if (y > 0)
|
|
{
|
|
selected[y - 1][x] ^= 1;
|
|
}
|
|
if (y < 3)
|
|
{
|
|
selected[y + 1][x] ^= 1;
|
|
}
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
if (winning_state)
|
|
ImGui::PopStyleVar();
|
|
ImGui::TreePop();
|
|
}
|
|
if (ImGui::TreeNode("Alignment"))
|
|
{
|
|
HelpMarker(
|
|
"By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item "
|
|
"basis using PushStyleVar(). You'll probably want to always keep your default situation to "
|
|
"left-align otherwise it becomes difficult to layout multiple items on a same line");
|
|
static bool selected[3 * 3] = {true, false, true, false, true, false, true, false, true};
|
|
for (int y = 0; y < 3; y++)
|
|
{
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);
|
|
char name[32];
|
|
sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y);
|
|
if (x > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);
|
|
ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80));
|
|
ImGui::PopStyleVar();
|
|
}
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// To wire InputText() with std::string or any other custom string type,
|
|
// see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
|
|
if (ImGui::TreeNode("Text Input"))
|
|
{
|
|
if (ImGui::TreeNode("Multi-line Text Input"))
|
|
{
|
|
// Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize
|
|
// and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.
|
|
static char text[1024 * 16] =
|
|
"/*\n"
|
|
" The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"
|
|
" the hexadecimal encoding of one offending instruction,\n"
|
|
" more formally, the invalid operand with locked CMPXCHG8B\n"
|
|
" instruction bug, is a design flaw in the majority of\n"
|
|
" Intel Pentium, Pentium MMX, and Pentium OverDrive\n"
|
|
" processors (all in the P5 microarchitecture).\n"
|
|
"*/\n\n"
|
|
"label:\n"
|
|
"\tlock cmpxchg8b eax\n";
|
|
|
|
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
|
|
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)");
|
|
ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput);
|
|
ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags,
|
|
ImGuiInputTextFlags_CtrlEnterForNewLine);
|
|
ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text),
|
|
ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Filtered Text Input"))
|
|
{
|
|
struct TextFilters
|
|
{
|
|
// Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i'
|
|
static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)
|
|
{
|
|
if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
static char buf1[64] = "";
|
|
ImGui::InputText("default", buf1, 64);
|
|
static char buf2[64] = "";
|
|
ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal);
|
|
static char buf3[64] = "";
|
|
ImGui::InputText("hexadecimal", buf3, 64,
|
|
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
|
|
static char buf4[64] = "";
|
|
ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase);
|
|
static char buf5[64] = "";
|
|
ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank);
|
|
static char buf6[64] = "";
|
|
ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter,
|
|
TextFilters::FilterImGuiLetters);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Password Input"))
|
|
{
|
|
static char password[64] = "password123";
|
|
ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
|
|
ImGui::SameLine();
|
|
HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
|
|
ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_ARRAYSIZE(password),
|
|
ImGuiInputTextFlags_Password);
|
|
ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password));
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Completion, History, Edit Callbacks"))
|
|
{
|
|
struct Funcs
|
|
{
|
|
static int MyCallback(ImGuiInputTextCallbackData* data)
|
|
{
|
|
if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)
|
|
{
|
|
data->InsertChars(data->CursorPos, "..");
|
|
}
|
|
else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)
|
|
{
|
|
if (data->EventKey == ImGuiKey_UpArrow)
|
|
{
|
|
data->DeleteChars(0, data->BufTextLen);
|
|
data->InsertChars(0, "Pressed Up!");
|
|
data->SelectAll();
|
|
}
|
|
else if (data->EventKey == ImGuiKey_DownArrow)
|
|
{
|
|
data->DeleteChars(0, data->BufTextLen);
|
|
data->InsertChars(0, "Pressed Down!");
|
|
data->SelectAll();
|
|
}
|
|
}
|
|
else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)
|
|
{
|
|
// Toggle casing of first character
|
|
char c = data->Buf[0];
|
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
|
|
data->Buf[0] ^= 32;
|
|
data->BufDirty = true;
|
|
|
|
// Increment a counter
|
|
int* p_int = (int*)data->UserData;
|
|
*p_int = *p_int + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
static char buf1[64];
|
|
ImGui::InputText("Completion", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Here we append \"..\" each time Tab is pressed. See 'Examples>Console' for a more meaningful "
|
|
"demonstration of using this callback.");
|
|
|
|
static char buf2[64];
|
|
ImGui::InputText("History", buf2, 64, ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Here we replace and select text each time Up/Down are pressed. See 'Examples>Console' for a more "
|
|
"meaningful demonstration of using this callback.");
|
|
|
|
static char buf3[64];
|
|
static int edit_count = 0;
|
|
ImGui::InputText("Edit", buf3, 64, ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);
|
|
ImGui::SameLine();
|
|
HelpMarker("Here we toggle the casing of the first character on every edits + count edits.");
|
|
ImGui::SameLine();
|
|
ImGui::Text("(%d)", edit_count);
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Resize Callback"))
|
|
{
|
|
// To wire InputText() with std::string or any other custom string type,
|
|
// you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
|
|
// using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
|
|
HelpMarker(
|
|
"Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n"
|
|
"See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
|
|
struct Funcs
|
|
{
|
|
static int MyResizeCallback(ImGuiInputTextCallbackData* data)
|
|
{
|
|
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
|
|
{
|
|
ImVector<char>* my_str = (ImVector<char>*)data->UserData;
|
|
IM_ASSERT(my_str->begin() == data->Buf);
|
|
my_str->resize(
|
|
data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
|
|
data->Buf = my_str->begin();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
|
|
// For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
|
|
static bool MyInputTextMultiline(const char* label,
|
|
ImVector<char>* my_str,
|
|
const ImVec2& size = ImVec2(0, 0),
|
|
ImGuiInputTextFlags flags = 0)
|
|
{
|
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
|
return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size,
|
|
flags | ImGuiInputTextFlags_CallbackResize,
|
|
Funcs::MyResizeCallback, (void*)my_str);
|
|
}
|
|
};
|
|
|
|
// For this demo we are using ImVector as a string container.
|
|
// Note that because we need to store a terminating zero character, our size/capacity are 1 more
|
|
// than usually reported by a typical string class.
|
|
static ImVector<char> my_str;
|
|
if (my_str.empty())
|
|
my_str.push_back(0);
|
|
Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));
|
|
ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Tabs
|
|
if (ImGui::TreeNode("Tabs"))
|
|
{
|
|
if (ImGui::TreeNode("Basic"))
|
|
{
|
|
ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
|
|
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
|
|
{
|
|
if (ImGui::BeginTabItem("Avocado"))
|
|
{
|
|
ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah");
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Broccoli"))
|
|
{
|
|
ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah");
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Cucumber"))
|
|
{
|
|
ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah");
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
}
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Advanced & Close Button"))
|
|
{
|
|
// Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).
|
|
static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;
|
|
ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", &tab_bar_flags, ImGuiTabBarFlags_Reorderable);
|
|
ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", &tab_bar_flags,
|
|
ImGuiTabBarFlags_AutoSelectNewTabs);
|
|
ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags,
|
|
ImGuiTabBarFlags_TabListPopupButton);
|
|
ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags,
|
|
ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);
|
|
if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
|
|
tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
|
|
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags,
|
|
ImGuiTabBarFlags_FittingPolicyResizeDown))
|
|
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
|
|
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags,
|
|
ImGuiTabBarFlags_FittingPolicyScroll))
|
|
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
|
|
|
|
// Tab Bar
|
|
const char* names[4] = {"Artichoke", "Beetroot", "Celery", "Daikon"};
|
|
static bool opened[4] = {true, true, true, true}; // Persistent user state
|
|
for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
|
|
{
|
|
if (n > 0)
|
|
{
|
|
ImGui::SameLine();
|
|
}
|
|
ImGui::Checkbox(names[n], &opened[n]);
|
|
}
|
|
|
|
// Passing a bool* to BeginTabItem() is similar to passing one to Begin():
|
|
// the underlying bool will be set to false when the tab is closed.
|
|
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
|
|
{
|
|
for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
|
|
if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None))
|
|
{
|
|
ImGui::Text("This is the %s tab!", names[n]);
|
|
if (n & 1)
|
|
ImGui::Text("I am an odd tab.");
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
}
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags"))
|
|
{
|
|
static ImVector<int> active_tabs;
|
|
static int next_tab_id = 0;
|
|
if (next_tab_id == 0) // Initialize with some default tabs
|
|
for (int i = 0; i < 3; i++)
|
|
active_tabs.push_back(next_tab_id++);
|
|
|
|
// TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.
|
|
// (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without
|
|
// Leading/Trailing flags... but they tend to make more sense together)
|
|
static bool show_leading_button = true;
|
|
static bool show_trailing_button = true;
|
|
ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button);
|
|
ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button);
|
|
|
|
// Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
|
|
static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
|
|
ImGuiTabBarFlags_FittingPolicyResizeDown;
|
|
ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags,
|
|
ImGuiTabBarFlags_TabListPopupButton);
|
|
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags,
|
|
ImGuiTabBarFlags_FittingPolicyResizeDown))
|
|
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
|
|
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags,
|
|
ImGuiTabBarFlags_FittingPolicyScroll))
|
|
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
|
|
|
|
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
|
|
{
|
|
// Demo a Leading TabItemButton(): click the "?" button to open a menu
|
|
if (show_leading_button)
|
|
if (ImGui::TabItemButton("?", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))
|
|
ImGui::OpenPopup("MyHelpMenu");
|
|
if (ImGui::BeginPopup("MyHelpMenu"))
|
|
{
|
|
ImGui::Selectable("Hello!");
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Demo Trailing Tabs: click the "+" button to add a new tab (in your app you may want to use a font
|
|
// icon instead of the "+") Note that we submit it before the regular tabs, but because of the
|
|
// ImGuiTabItemFlags_Trailing flag it will always appear at the end.
|
|
if (show_trailing_button)
|
|
if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))
|
|
active_tabs.push_back(next_tab_id++); // Add new tab
|
|
|
|
// Submit our regular tabs
|
|
for (int n = 0; n < active_tabs.Size;)
|
|
{
|
|
bool open = true;
|
|
char name[16];
|
|
snprintf(name, IM_ARRAYSIZE(name), "%04d", active_tabs[n]);
|
|
if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None))
|
|
{
|
|
ImGui::Text("This is the %s tab!", name);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (!open)
|
|
active_tabs.erase(active_tabs.Data + n);
|
|
else
|
|
n++;
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Plot/Graph widgets are not very good.
|
|
// Consider writing your own, or using a third-party one, see:
|
|
// - ImPlot https://github.com/epezent/implot
|
|
// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions
|
|
if (ImGui::TreeNode("Plots Widgets"))
|
|
{
|
|
static bool animate = true;
|
|
ImGui::Checkbox("Animate", &animate);
|
|
|
|
static float arr[] = {0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f};
|
|
ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr));
|
|
|
|
// Fill an array of contiguous float values to plot
|
|
// Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
|
|
// and the sizeof() of your structure in the "stride" parameter.
|
|
static float values[90] = {};
|
|
static int values_offset = 0;
|
|
static double refresh_time = 0.0;
|
|
if (!animate || refresh_time == 0.0)
|
|
refresh_time = ImGui::GetTime();
|
|
while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
|
|
{
|
|
static float phase = 0.0f;
|
|
values[values_offset] = cosf(phase);
|
|
values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
|
|
phase += 0.10f * values_offset;
|
|
refresh_time += 1.0f / 60.0f;
|
|
}
|
|
|
|
// Plots can display overlay texts
|
|
// (in this example, we will display an average value)
|
|
{
|
|
float average = 0.0f;
|
|
for (int n = 0; n < IM_ARRAYSIZE(values); n++)
|
|
average += values[n];
|
|
average /= (float)IM_ARRAYSIZE(values);
|
|
char overlay[32];
|
|
sprintf(overlay, "avg %f", average);
|
|
ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f,
|
|
ImVec2(0, 80.0f));
|
|
}
|
|
ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));
|
|
|
|
// Use functions to generate output
|
|
// FIXME: This is rather awkward because current plot API only pass in indices.
|
|
// We probably want an API passing floats and user provide sample rate/count.
|
|
struct Funcs
|
|
{
|
|
static float Sin(void*, int i) { return sinf(i * 0.1f); }
|
|
static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }
|
|
};
|
|
static int func_type = 0, display_count = 70;
|
|
ImGui::Separator();
|
|
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
|
ImGui::Combo("func", &func_type, "Sin\0Saw\0");
|
|
ImGui::SameLine();
|
|
ImGui::SliderInt("Sample count", &display_count, 1, 400);
|
|
float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
|
|
ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
|
|
ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
|
|
ImGui::Separator();
|
|
|
|
// Animate a simple progress bar
|
|
static float progress = 0.0f, progress_dir = 1.0f;
|
|
if (animate)
|
|
{
|
|
progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
|
|
if (progress >= +1.1f)
|
|
{
|
|
progress = +1.1f;
|
|
progress_dir *= -1.0f;
|
|
}
|
|
if (progress <= -0.1f)
|
|
{
|
|
progress = -0.1f;
|
|
progress_dir *= -1.0f;
|
|
}
|
|
}
|
|
|
|
// Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
|
|
// or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
|
|
ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));
|
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
|
ImGui::Text("Progress Bar");
|
|
|
|
float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f);
|
|
char buf[32];
|
|
sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753);
|
|
ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Color/Picker Widgets"))
|
|
{
|
|
static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);
|
|
|
|
static bool alpha_preview = true;
|
|
static bool alpha_half_preview = false;
|
|
static bool drag_and_drop = true;
|
|
static bool options_menu = true;
|
|
static bool hdr = false;
|
|
ImGui::Checkbox("With Alpha Preview", &alpha_preview);
|
|
ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview);
|
|
ImGui::Checkbox("With Drag and Drop", &drag_and_drop);
|
|
ImGui::Checkbox("With Options Menu", &options_menu);
|
|
ImGui::SameLine();
|
|
HelpMarker("Right-click on the individual color widget to show options.");
|
|
ImGui::Checkbox("With HDR", &hdr);
|
|
ImGui::SameLine();
|
|
HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets.");
|
|
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);
|
|
|
|
ImGui::Text("Color widget:");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Click on the color square to open a color picker.\n"
|
|
"CTRL+click on individual component to input value.\n");
|
|
ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags);
|
|
|
|
ImGui::Text("Color widget HSV with Alpha:");
|
|
ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags);
|
|
|
|
ImGui::Text("Color widget with Float Display:");
|
|
ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags);
|
|
|
|
ImGui::Text("Color button with Picker:");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n"
|
|
"With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only "
|
|
"be used for the tooltip and picker popup.");
|
|
ImGui::ColorEdit4("MyColor##3", (float*)&color,
|
|
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags);
|
|
|
|
ImGui::Text("Color button with Custom Picker Popup:");
|
|
|
|
// Generate a default palette. The palette will persist and can be edited.
|
|
static bool saved_palette_init = true;
|
|
static ImVec4 saved_palette[32] = {};
|
|
if (saved_palette_init)
|
|
{
|
|
for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
|
|
{
|
|
ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y,
|
|
saved_palette[n].z);
|
|
saved_palette[n].w = 1.0f; // Alpha
|
|
}
|
|
saved_palette_init = false;
|
|
}
|
|
|
|
static ImVec4 backup_color;
|
|
bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags);
|
|
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
|
|
open_popup |= ImGui::Button("Palette");
|
|
if (open_popup)
|
|
{
|
|
ImGui::OpenPopup("mypicker");
|
|
backup_color = color;
|
|
}
|
|
if (ImGui::BeginPopup("mypicker"))
|
|
{
|
|
ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!");
|
|
ImGui::Separator();
|
|
ImGui::ColorPicker4("##picker", (float*)&color,
|
|
misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);
|
|
ImGui::SameLine();
|
|
|
|
ImGui::BeginGroup(); // Lock X position
|
|
ImGui::Text("Current");
|
|
ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
|
ImVec2(60, 40));
|
|
ImGui::Text("Previous");
|
|
if (ImGui::ColorButton("##previous", backup_color,
|
|
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)))
|
|
color = backup_color;
|
|
ImGui::Separator();
|
|
ImGui::Text("Palette");
|
|
for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
|
|
{
|
|
ImGui::PushID(n);
|
|
if ((n % 8) != 0)
|
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
|
|
|
|
ImGuiColorEditFlags palette_button_flags =
|
|
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
|
|
if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, ImVec2(20, 20)))
|
|
color =
|
|
ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
|
|
|
|
// Allow user to drop colors into each palette entry. Note that ColorButton() is already a
|
|
// drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
|
|
if (ImGui::BeginDragDropTarget())
|
|
{
|
|
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
|
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);
|
|
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
|
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);
|
|
ImGui::EndDragDropTarget();
|
|
}
|
|
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndGroup();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
ImGui::Text("Color button only:");
|
|
static bool no_border = false;
|
|
ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border);
|
|
ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0),
|
|
ImVec2(80, 80));
|
|
|
|
ImGui::Text("Color picker:");
|
|
static bool alpha = true;
|
|
static bool alpha_bar = true;
|
|
static bool side_preview = true;
|
|
static bool ref_color = false;
|
|
static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);
|
|
static int display_mode = 0;
|
|
static int picker_mode = 0;
|
|
ImGui::Checkbox("With Alpha", &alpha);
|
|
ImGui::Checkbox("With Alpha Bar", &alpha_bar);
|
|
ImGui::Checkbox("With Side Preview", &side_preview);
|
|
if (side_preview)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("With Ref Color", &ref_color);
|
|
if (ref_color)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags);
|
|
}
|
|
}
|
|
ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
|
|
"but the user can change it with a right-click.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
|
|
"if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
|
|
ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0");
|
|
ImGui::SameLine();
|
|
HelpMarker("User can right-click the picker to change mode.");
|
|
ImGuiColorEditFlags flags = misc_flags;
|
|
if (!alpha)
|
|
flags |=
|
|
ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4()
|
|
if (alpha_bar)
|
|
flags |= ImGuiColorEditFlags_AlphaBar;
|
|
if (!side_preview)
|
|
flags |= ImGuiColorEditFlags_NoSidePreview;
|
|
if (picker_mode == 1)
|
|
flags |= ImGuiColorEditFlags_PickerHueBar;
|
|
if (picker_mode == 2)
|
|
flags |= ImGuiColorEditFlags_PickerHueWheel;
|
|
if (display_mode == 1)
|
|
flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays
|
|
if (display_mode == 2)
|
|
flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode
|
|
if (display_mode == 3)
|
|
flags |= ImGuiColorEditFlags_DisplayHSV;
|
|
if (display_mode == 4)
|
|
flags |= ImGuiColorEditFlags_DisplayHex;
|
|
ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);
|
|
|
|
ImGui::Text("Set defaults in code:");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"SetColorEditOptions() is designed to allow you to set boot-time default.\n"
|
|
"We don't have Push/Pop functions because you can force options on a per-widget basis if needed,"
|
|
"and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid"
|
|
"encouraging you to persistently save values that aren't forward-compatible.");
|
|
if (ImGui::Button("Default: Uint8 + HSV + Hue Bar"))
|
|
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV |
|
|
ImGuiColorEditFlags_PickerHueBar);
|
|
if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
|
|
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR |
|
|
ImGuiColorEditFlags_PickerHueWheel);
|
|
|
|
// HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
|
|
static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
|
|
ImGui::Spacing();
|
|
ImGui::Text("HSV encoded colors");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV"
|
|
"allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the"
|
|
"added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
|
|
ImGui::Text("Color widget with InputHSV:");
|
|
ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_hsv,
|
|
ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
|
|
ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv,
|
|
ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
|
|
ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f);
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Drag/Slider Flags"))
|
|
{
|
|
// Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
|
|
static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
|
|
ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp);
|
|
ImGui::SameLine();
|
|
HelpMarker("Always clamp value to min/max bounds (if any) when input manually with CTRL+Click.");
|
|
ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic);
|
|
ImGui::SameLine();
|
|
HelpMarker("Enable logarithmic editing (more precision for small values).");
|
|
ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded "
|
|
"to those 3 digits).");
|
|
ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput);
|
|
ImGui::SameLine();
|
|
HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
|
|
|
|
// Drags
|
|
static float drag_f = 0.5f;
|
|
static int drag_i = 50;
|
|
ImGui::Text("Underlying float value: %f", drag_f);
|
|
ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags);
|
|
ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags);
|
|
ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags);
|
|
ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags);
|
|
ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags);
|
|
|
|
// Sliders
|
|
static float slider_f = 0.5f;
|
|
static int slider_i = 50;
|
|
ImGui::Text("Underlying float value: %f", slider_f);
|
|
ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags);
|
|
ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags);
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Range Widgets"))
|
|
{
|
|
static float begin = 10, end = 90;
|
|
static int begin_i = 100, end_i = 1000;
|
|
ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%",
|
|
ImGuiSliderFlags_AlwaysClamp);
|
|
ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units");
|
|
ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Data Types"))
|
|
{
|
|
// DragScalar/InputScalar/SliderScalar functions allow various data types
|
|
// - signed/unsigned
|
|
// - 8/16/32/64-bits
|
|
// - integer/float/double
|
|
// To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
|
|
// to pass the type, and passing all arguments by pointer.
|
|
// This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types.
|
|
// In practice, if you frequently use a given type that is not covered by the normal API entry points,
|
|
// you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
|
|
// and then pass their address to the generic function. For example:
|
|
// bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
|
|
// {
|
|
// return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
|
|
// }
|
|
|
|
// Setup limits (as helper variables so we can take their address, as explained above)
|
|
// Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
|
|
# ifndef LLONG_MIN
|
|
ImS64 LLONG_MIN = -9223372036854775807LL - 1;
|
|
ImS64 LLONG_MAX = 9223372036854775807LL;
|
|
ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);
|
|
# endif
|
|
const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127;
|
|
const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255;
|
|
const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767;
|
|
const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;
|
|
const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;
|
|
|
|
// State
|
|
static char s8_v = 127;
|
|
static ImU8 u8_v = 255;
|
|
static short s16_v = 32767;
|
|
static ImU16 u16_v = 65535;
|
|
static ImS32 s32_v = -1;
|
|
static ImU32 u32_v = (ImU32)-1;
|
|
static ImS64 s64_v = -1;
|
|
static ImU64 u64_v = (ImU64)-1;
|
|
static float f32_v = 0.123f;
|
|
static double f64_v = 90000.01234567890123456789;
|
|
|
|
const float drag_speed = 0.2f;
|
|
static bool drag_clamp = false;
|
|
ImGui::Text("Drags:");
|
|
ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"As with every widgets in dear imgui, we never modify values unless there is a user interaction.\n"
|
|
"You can override the clamping limits by using CTRL+Click to input a value.");
|
|
ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL,
|
|
drag_clamp ? &s8_fifty : NULL);
|
|
ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL,
|
|
drag_clamp ? &u8_fifty : NULL, "%u ms");
|
|
ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL,
|
|
drag_clamp ? &s16_fifty : NULL);
|
|
ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL,
|
|
drag_clamp ? &u16_fifty : NULL, "%u ms");
|
|
ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL,
|
|
drag_clamp ? &s32_fifty : NULL);
|
|
ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL,
|
|
drag_clamp ? &u32_fifty : NULL, "%u ms");
|
|
ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL,
|
|
drag_clamp ? &s64_fifty : NULL);
|
|
ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL,
|
|
drag_clamp ? &u64_fifty : NULL);
|
|
ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f");
|
|
ImGui::DragScalar("drag float log", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f",
|
|
ImGuiSliderFlags_Logarithmic);
|
|
ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams");
|
|
ImGui::DragScalar("drag double log", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one,
|
|
"0 < %.10f < 1", ImGuiSliderFlags_Logarithmic);
|
|
|
|
ImGui::Text("Sliders");
|
|
ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d");
|
|
ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u");
|
|
ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d");
|
|
ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u");
|
|
ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "%d");
|
|
ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d");
|
|
ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d");
|
|
ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty, "%u");
|
|
ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u");
|
|
ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u");
|
|
ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty, "%" IM_PRId64);
|
|
ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" IM_PRId64);
|
|
ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" IM_PRId64);
|
|
ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty, "%" IM_PRIu64 " ms");
|
|
ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" IM_PRIu64 " ms");
|
|
ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" IM_PRIu64 " ms");
|
|
ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one);
|
|
ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f",
|
|
ImGuiSliderFlags_Logarithmic);
|
|
ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e");
|
|
ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams");
|
|
ImGui::SliderScalar("slider double low log", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f",
|
|
ImGuiSliderFlags_Logarithmic);
|
|
ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams");
|
|
|
|
ImGui::Text("Sliders (reverse)");
|
|
ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d");
|
|
ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u");
|
|
ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d");
|
|
ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u");
|
|
ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" IM_PRId64);
|
|
ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero,
|
|
"%" IM_PRIu64 " ms");
|
|
|
|
static bool inputs_step = true;
|
|
ImGui::Text("Inputs");
|
|
ImGui::Checkbox("Show step buttons", &inputs_step);
|
|
ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d");
|
|
ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u");
|
|
ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d");
|
|
ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u");
|
|
ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d");
|
|
ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X",
|
|
ImGuiInputTextFlags_CharsHexadecimal);
|
|
ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u");
|
|
ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X",
|
|
ImGuiInputTextFlags_CharsHexadecimal);
|
|
ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL);
|
|
ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL);
|
|
ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL);
|
|
ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL);
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Multi-component Widgets"))
|
|
{
|
|
static float vec4f[4] = {0.10f, 0.20f, 0.30f, 0.44f};
|
|
static int vec4i[4] = {1, 5, 100, 255};
|
|
|
|
ImGui::InputFloat2("input float2", vec4f);
|
|
ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f);
|
|
ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f);
|
|
ImGui::InputInt2("input int2", vec4i);
|
|
ImGui::DragInt2("drag int2", vec4i, 1, 0, 255);
|
|
ImGui::SliderInt2("slider int2", vec4i, 0, 255);
|
|
ImGui::Spacing();
|
|
|
|
ImGui::InputFloat3("input float3", vec4f);
|
|
ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f);
|
|
ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f);
|
|
ImGui::InputInt3("input int3", vec4i);
|
|
ImGui::DragInt3("drag int3", vec4i, 1, 0, 255);
|
|
ImGui::SliderInt3("slider int3", vec4i, 0, 255);
|
|
ImGui::Spacing();
|
|
|
|
ImGui::InputFloat4("input float4", vec4f);
|
|
ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f);
|
|
ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f);
|
|
ImGui::InputInt4("input int4", vec4i);
|
|
ImGui::DragInt4("drag int4", vec4i, 1, 0, 255);
|
|
ImGui::SliderInt4("slider int4", vec4i, 0, 255);
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Vertical Sliders"))
|
|
{
|
|
const float spacing = 4;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
|
|
|
|
static int int_value = 0;
|
|
ImGui::VSliderInt("##int", ImVec2(18, 160), &int_value, 0, 5);
|
|
ImGui::SameLine();
|
|
|
|
static float values[7] = {0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f};
|
|
ImGui::PushID("set1");
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
if (i > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(i);
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f));
|
|
ImGui::VSliderFloat("##v", ImVec2(18, 160), &values[i], 0.0f, 1.0f, "");
|
|
if (ImGui::IsItemActive() || ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("%.3f", values[i]);
|
|
ImGui::PopStyleColor(4);
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::PopID();
|
|
|
|
ImGui::SameLine();
|
|
ImGui::PushID("set2");
|
|
static float values2[4] = {0.20f, 0.80f, 0.40f, 0.25f};
|
|
const int rows = 3;
|
|
const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));
|
|
for (int nx = 0; nx < 4; nx++)
|
|
{
|
|
if (nx > 0)
|
|
ImGui::SameLine();
|
|
ImGui::BeginGroup();
|
|
for (int ny = 0; ny < rows; ny++)
|
|
{
|
|
ImGui::PushID(nx * rows + ny);
|
|
ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, "");
|
|
if (ImGui::IsItemActive() || ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("%.3f", values2[nx]);
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndGroup();
|
|
}
|
|
ImGui::PopID();
|
|
|
|
ImGui::SameLine();
|
|
ImGui::PushID("set3");
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (i > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(i);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40);
|
|
ImGui::VSliderFloat("##v", ImVec2(40, 160), &values[i], 0.0f, 1.0f, "%.2f\nsec");
|
|
ImGui::PopStyleVar();
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::PopID();
|
|
ImGui::PopStyleVar();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Drag and Drop"))
|
|
{
|
|
if (ImGui::TreeNode("Drag and drop in standard widgets"))
|
|
{
|
|
// ColorEdit widgets automatically act as drag source and drag target.
|
|
// They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
|
|
// to allow your own widgets to use colors in their drag and drop interaction.
|
|
// Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
|
|
HelpMarker("You can drag from the color squares.");
|
|
static float col1[3] = {1.0f, 0.0f, 0.2f};
|
|
static float col2[4] = {0.4f, 0.7f, 0.0f, 0.5f};
|
|
ImGui::ColorEdit3("color 1", col1);
|
|
ImGui::ColorEdit4("color 2", col2);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Drag and drop to copy/swap items"))
|
|
{
|
|
enum Mode
|
|
{
|
|
Mode_Copy,
|
|
Mode_Move,
|
|
Mode_Swap
|
|
};
|
|
static int mode = 0;
|
|
if (ImGui::RadioButton("Copy", mode == Mode_Copy))
|
|
{
|
|
mode = Mode_Copy;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::RadioButton("Move", mode == Mode_Move))
|
|
{
|
|
mode = Mode_Move;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::RadioButton("Swap", mode == Mode_Swap))
|
|
{
|
|
mode = Mode_Swap;
|
|
}
|
|
static const char* names[9] = {"Bobby", "Beatrice", "Betty", "Brianna", "Barry",
|
|
"Bernard", "Bibi", "Blaine", "Bryn"};
|
|
for (int n = 0; n < IM_ARRAYSIZE(names); n++)
|
|
{
|
|
ImGui::PushID(n);
|
|
if ((n % 3) != 0)
|
|
ImGui::SameLine();
|
|
ImGui::Button(names[n], ImVec2(60, 60));
|
|
|
|
// Our buttons are both drag sources and drag targets here!
|
|
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
|
|
{
|
|
// Set payload to carry the index of our item (could be anything)
|
|
ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int));
|
|
|
|
// Display preview (could be anything, e.g. when dragging an image we could decide to display
|
|
// the filename and a small preview of the image, etc.)
|
|
if (mode == Mode_Copy)
|
|
{
|
|
ImGui::Text("Copy %s", names[n]);
|
|
}
|
|
if (mode == Mode_Move)
|
|
{
|
|
ImGui::Text("Move %s", names[n]);
|
|
}
|
|
if (mode == Mode_Swap)
|
|
{
|
|
ImGui::Text("Swap %s", names[n]);
|
|
}
|
|
ImGui::EndDragDropSource();
|
|
}
|
|
if (ImGui::BeginDragDropTarget())
|
|
{
|
|
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL"))
|
|
{
|
|
IM_ASSERT(payload->DataSize == sizeof(int));
|
|
int payload_n = *(const int*)payload->Data;
|
|
if (mode == Mode_Copy)
|
|
{
|
|
names[n] = names[payload_n];
|
|
}
|
|
if (mode == Mode_Move)
|
|
{
|
|
names[n] = names[payload_n];
|
|
names[payload_n] = "";
|
|
}
|
|
if (mode == Mode_Swap)
|
|
{
|
|
const char* tmp = names[n];
|
|
names[n] = names[payload_n];
|
|
names[payload_n] = tmp;
|
|
}
|
|
}
|
|
ImGui::EndDragDropTarget();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Drag to reorder items (simple)"))
|
|
{
|
|
// Simple reordering
|
|
HelpMarker(
|
|
"We don't use the drag and drop api at all here! "
|
|
"Instead we query when the item is held but not hovered, and order items accordingly.");
|
|
static const char* item_names[] = {"Item One", "Item Two", "Item Three", "Item Four", "Item Five"};
|
|
for (int n = 0; n < IM_ARRAYSIZE(item_names); n++)
|
|
{
|
|
const char* item = item_names[n];
|
|
ImGui::Selectable(item);
|
|
|
|
if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
|
|
{
|
|
int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1);
|
|
if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names))
|
|
{
|
|
item_names[n] = item_names[n_next];
|
|
item_names[n_next] = item;
|
|
ImGui::ResetMouseDragDelta();
|
|
}
|
|
}
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)"))
|
|
{
|
|
// Select an item type
|
|
const char* item_names[] = {"Text",
|
|
"Button",
|
|
"Button (w/ repeat)",
|
|
"Checkbox",
|
|
"SliderFloat",
|
|
"InputText",
|
|
"InputFloat",
|
|
"InputFloat3",
|
|
"ColorEdit4",
|
|
"Selectable",
|
|
"MenuItem",
|
|
"TreeNode",
|
|
"TreeNode (w/ double-click)",
|
|
"Combo",
|
|
"ListBox"};
|
|
static int item_type = 4;
|
|
static bool item_disabled = false;
|
|
ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names));
|
|
ImGui::SameLine();
|
|
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().");
|
|
ImGui::Checkbox("Item Disabled", &item_disabled);
|
|
|
|
// Submit selected item item so we can query their status in the code following it.
|
|
bool ret = false;
|
|
static bool b = false;
|
|
static float col4f[4] = {1.0f, 0.5, 0.0f, 1.0f};
|
|
static char str[16] = {};
|
|
if (item_disabled)
|
|
ImGui::BeginDisabled(true);
|
|
if (item_type == 0)
|
|
{
|
|
ImGui::Text("ITEM: Text");
|
|
} // Testing text items with no identifier/interaction
|
|
if (item_type == 1)
|
|
{
|
|
ret = ImGui::Button("ITEM: Button");
|
|
} // Testing button
|
|
if (item_type == 2)
|
|
{
|
|
ImGui::PushButtonRepeat(true);
|
|
ret = ImGui::Button("ITEM: Button");
|
|
ImGui::PopButtonRepeat();
|
|
} // Testing button (with repeater)
|
|
if (item_type == 3)
|
|
{
|
|
ret = ImGui::Checkbox("ITEM: Checkbox", &b);
|
|
} // Testing checkbox
|
|
if (item_type == 4)
|
|
{
|
|
ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f);
|
|
} // Testing basic item
|
|
if (item_type == 5)
|
|
{
|
|
ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str));
|
|
} // Testing input text (which handles tabbing)
|
|
if (item_type == 6)
|
|
{
|
|
ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f);
|
|
} // Testing +/- buttons on scalar input
|
|
if (item_type == 7)
|
|
{
|
|
ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f);
|
|
} // Testing multi-component items (IsItemXXX flags are reported merged)
|
|
if (item_type == 8)
|
|
{
|
|
ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f);
|
|
} // Testing multi-component items (IsItemXXX flags are reported merged)
|
|
if (item_type == 9)
|
|
{
|
|
ret = ImGui::Selectable("ITEM: Selectable");
|
|
} // Testing selectable item
|
|
if (item_type == 10)
|
|
{
|
|
ret = ImGui::MenuItem("ITEM: MenuItem");
|
|
} // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)
|
|
if (item_type == 11)
|
|
{
|
|
ret = ImGui::TreeNode("ITEM: TreeNode");
|
|
if (ret)
|
|
ImGui::TreePop();
|
|
} // Testing tree node
|
|
if (item_type == 12)
|
|
{
|
|
ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick",
|
|
ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen);
|
|
} // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.
|
|
if (item_type == 13)
|
|
{
|
|
const char* items[] = {"Apple", "Banana", "Cherry", "Kiwi"};
|
|
static int current = 1;
|
|
ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items));
|
|
}
|
|
if (item_type == 14)
|
|
{
|
|
const char* items[] = {"Apple", "Banana", "Cherry", "Kiwi"};
|
|
static int current = 1;
|
|
ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items));
|
|
}
|
|
|
|
// Display the values of IsItemHovered() and other common item state functions.
|
|
// Note that the ImGuiHoveredFlags_XXX flags can be combined.
|
|
// Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
|
|
// we query every state in a single call to avoid storing them and to simplify the code.
|
|
ImGui::BulletText(
|
|
"Return value = %d\n"
|
|
"IsItemFocused() = %d\n"
|
|
"IsItemHovered() = %d\n"
|
|
"IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
|
|
"IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
|
|
"IsItemHovered(_AllowWhenOverlapped) = %d\n"
|
|
"IsItemHovered(_AllowWhenDisabled) = %d\n"
|
|
"IsItemHovered(_RectOnly) = %d\n"
|
|
"IsItemActive() = %d\n"
|
|
"IsItemEdited() = %d\n"
|
|
"IsItemActivated() = %d\n"
|
|
"IsItemDeactivated() = %d\n"
|
|
"IsItemDeactivatedAfterEdit() = %d\n"
|
|
"IsItemVisible() = %d\n"
|
|
"IsItemClicked() = %d\n"
|
|
"IsItemToggledOpen() = %d\n"
|
|
"GetItemRectMin() = (%.1f, %.1f)\n"
|
|
"GetItemRectMax() = (%.1f, %.1f)\n"
|
|
"GetItemRectSize() = (%.1f, %.1f)",
|
|
ret, ImGui::IsItemFocused(), ImGui::IsItemHovered(),
|
|
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
|
|
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
|
|
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped),
|
|
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),
|
|
ImGui::IsItemActive(), ImGui::IsItemEdited(), ImGui::IsItemActivated(), ImGui::IsItemDeactivated(),
|
|
ImGui::IsItemDeactivatedAfterEdit(), ImGui::IsItemVisible(), ImGui::IsItemClicked(),
|
|
ImGui::IsItemToggledOpen(), ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y, ImGui::GetItemRectMax().x,
|
|
ImGui::GetItemRectMax().y, ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y);
|
|
|
|
if (item_disabled)
|
|
ImGui::EndDisabled();
|
|
|
|
static bool embed_all_inside_a_child_window = false;
|
|
ImGui::Checkbox("Embed everything inside a child window (for additional testing)",
|
|
&embed_all_inside_a_child_window);
|
|
if (embed_all_inside_a_child_window)
|
|
ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true);
|
|
|
|
// Testing IsWindowFocused() function with its various flags.
|
|
// Note that the ImGuiFocusedFlags_XXX flags can be combined.
|
|
ImGui::BulletText(
|
|
"IsWindowFocused() = %d\n"
|
|
"IsWindowFocused(_ChildWindows) = %d\n"
|
|
"IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
|
|
"IsWindowFocused(_RootWindow) = %d\n"
|
|
"IsWindowFocused(_AnyWindow) = %d\n",
|
|
ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),
|
|
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
|
|
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));
|
|
|
|
// Testing IsWindowHovered() function with its various flags.
|
|
// Note that the ImGuiHoveredFlags_XXX flags can be combined.
|
|
ImGui::BulletText(
|
|
"IsWindowHovered() = %d\n"
|
|
"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
|
|
"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
|
|
"IsWindowHovered(_ChildWindows) = %d\n"
|
|
"IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
|
|
"IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
|
|
"IsWindowHovered(_RootWindow) = %d\n"
|
|
"IsWindowHovered(_AnyWindow) = %d\n",
|
|
ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
|
|
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
|
|
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),
|
|
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
|
|
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
|
|
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow));
|
|
|
|
ImGui::BeginChild("child", ImVec2(0, 50), true);
|
|
ImGui::Text("This is another child window for testing the _ChildWindows flag.");
|
|
ImGui::EndChild();
|
|
if (embed_all_inside_a_child_window)
|
|
ImGui::EndChild();
|
|
|
|
static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above.";
|
|
ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly);
|
|
|
|
// Calling IsItemHovered() after begin returns the hovered status of the title bar.
|
|
// This is useful in particular if you want to create a context menu associated to the title bar of a window.
|
|
// This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).
|
|
static bool test_window = false;
|
|
ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window);
|
|
if (test_window)
|
|
{
|
|
// FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop
|
|
// and get them stuck. Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our
|
|
// parent as "don't skip items"?
|
|
ImGui::Begin("Title bar Hovered/Active tests", &test_window);
|
|
if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()
|
|
{
|
|
if (ImGui::MenuItem("Close"))
|
|
{
|
|
test_window = false;
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::Text(
|
|
"IsItemHovered() after begin = %d (== is title bar hovered)\n"
|
|
"IsItemActive() after begin = %d (== is window being clicked/moved)\n",
|
|
ImGui::IsItemHovered(), ImGui::IsItemActive());
|
|
ImGui::End();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Demonstrate BeginDisabled/EndDisabled using a checkbox located at the bottom of the section (which is a bit odd:
|
|
// logically we'd have this checkbox at the top of the section, but we don't want this feature to steal that space)
|
|
if (disable_all)
|
|
ImGui::EndDisabled();
|
|
|
|
if (ImGui::TreeNode("Disable block"))
|
|
{
|
|
ImGui::Checkbox("Disable entire section above", &disable_all);
|
|
ImGui::SameLine();
|
|
HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across this section.");
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
|
|
static void ShowDemoWindowLayout()
|
|
{
|
|
if (!ImGui::CollapsingHeader("Layout & Scrolling"))
|
|
return;
|
|
|
|
if (ImGui::TreeNode("Child windows"))
|
|
{
|
|
HelpMarker(
|
|
"Use child windows to begin into a self-contained independent scrolling/clipping regions within a host "
|
|
"window.");
|
|
static bool disable_mouse_wheel = false;
|
|
static bool disable_menu = false;
|
|
ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel);
|
|
ImGui::Checkbox("Disable Menu", &disable_menu);
|
|
|
|
// Child 1: no border, enable horizontal scrollbar
|
|
{
|
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
|
|
if (disable_mouse_wheel)
|
|
window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
|
|
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags);
|
|
for (int i = 0; i < 100; i++)
|
|
ImGui::Text("%04d: scrollable region", i);
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
// Child 2: rounded border
|
|
{
|
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
|
|
if (disable_mouse_wheel)
|
|
window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
|
|
if (!disable_menu)
|
|
window_flags |= ImGuiWindowFlags_MenuBar;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
|
|
ImGui::BeginChild("ChildR", ImVec2(0, 260), true, window_flags);
|
|
if (!disable_menu && ImGui::BeginMenuBar())
|
|
{
|
|
if (ImGui::BeginMenu("Menu"))
|
|
{
|
|
ShowExampleMenuFile();
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenuBar();
|
|
}
|
|
if (ImGui::BeginTable("split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))
|
|
{
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "%03d", i);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::PopStyleVar();
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// Demonstrate a few extra things
|
|
// - Changing ImGuiCol_ChildBg (which is transparent black in default styles)
|
|
// - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
|
|
// You can also call SetNextWindowPos() to position the child window. The parent window will effectively
|
|
// layout from this position.
|
|
// - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
|
|
// the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.
|
|
{
|
|
static int offset_x = 0;
|
|
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
|
ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000);
|
|
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
|
|
ImGui::BeginChild("Red", ImVec2(200, 100), true, ImGuiWindowFlags_None);
|
|
for (int n = 0; n < 50; n++)
|
|
ImGui::Text("Some test %d", n);
|
|
ImGui::EndChild();
|
|
bool child_is_hovered = ImGui::IsItemHovered();
|
|
ImVec2 child_rect_min = ImGui::GetItemRectMin();
|
|
ImVec2 child_rect_max = ImGui::GetItemRectMax();
|
|
ImGui::PopStyleColor();
|
|
ImGui::Text("Hovered: %d", child_is_hovered);
|
|
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);
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Widgets Width"))
|
|
{
|
|
static float f = 0.0f;
|
|
static bool show_indented_items = true;
|
|
ImGui::Checkbox("Show indented items", &show_indented_items);
|
|
|
|
// Use SetNextItemWidth() to set the width of a single upcoming item.
|
|
// Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
|
|
// In real code use you'll probably want to choose width values that are proportional to your font size
|
|
// e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
|
|
|
|
ImGui::Text("SetNextItemWidth/PushItemWidth(100)");
|
|
ImGui::SameLine();
|
|
HelpMarker("Fixed width.");
|
|
ImGui::PushItemWidth(100);
|
|
ImGui::DragFloat("float##1b", &f);
|
|
if (show_indented_items)
|
|
{
|
|
ImGui::Indent();
|
|
ImGui::DragFloat("float (indented)##1b", &f);
|
|
ImGui::Unindent();
|
|
}
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::Text("SetNextItemWidth/PushItemWidth(-100)");
|
|
ImGui::SameLine();
|
|
HelpMarker("Align to right edge minus 100");
|
|
ImGui::PushItemWidth(-100);
|
|
ImGui::DragFloat("float##2a", &f);
|
|
if (show_indented_items)
|
|
{
|
|
ImGui::Indent();
|
|
ImGui::DragFloat("float (indented)##2b", &f);
|
|
ImGui::Unindent();
|
|
}
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)");
|
|
ImGui::SameLine();
|
|
HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)");
|
|
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f);
|
|
ImGui::DragFloat("float##3a", &f);
|
|
if (show_indented_items)
|
|
{
|
|
ImGui::Indent();
|
|
ImGui::DragFloat("float (indented)##3b", &f);
|
|
ImGui::Unindent();
|
|
}
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)");
|
|
ImGui::SameLine();
|
|
HelpMarker("Align to right edge minus half");
|
|
ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
|
|
ImGui::DragFloat("float##4a", &f);
|
|
if (show_indented_items)
|
|
{
|
|
ImGui::Indent();
|
|
ImGui::DragFloat("float (indented)##4b", &f);
|
|
ImGui::Unindent();
|
|
}
|
|
ImGui::PopItemWidth();
|
|
|
|
// Demonstrate using PushItemWidth to surround three items.
|
|
// Calling SetNextItemWidth() before each of them would have the same effect.
|
|
ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)");
|
|
ImGui::SameLine();
|
|
HelpMarker("Align to right edge");
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
ImGui::DragFloat("##float5a", &f);
|
|
if (show_indented_items)
|
|
{
|
|
ImGui::Indent();
|
|
ImGui::DragFloat("float (indented)##5b", &f);
|
|
ImGui::Unindent();
|
|
}
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Basic Horizontal Layout"))
|
|
{
|
|
ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)");
|
|
|
|
// Text
|
|
ImGui::Text("Two items: Hello");
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
|
|
|
|
// Adjust spacing
|
|
ImGui::Text("More spacing: Hello");
|
|
ImGui::SameLine(0, 20);
|
|
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
|
|
|
|
// Button
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("Normal buttons");
|
|
ImGui::SameLine();
|
|
ImGui::Button("Banana");
|
|
ImGui::SameLine();
|
|
ImGui::Button("Apple");
|
|
ImGui::SameLine();
|
|
ImGui::Button("Corniflower");
|
|
|
|
// Button
|
|
ImGui::Text("Small buttons");
|
|
ImGui::SameLine();
|
|
ImGui::SmallButton("Like this one");
|
|
ImGui::SameLine();
|
|
ImGui::Text("can fit within a text block.");
|
|
|
|
// Aligned to arbitrary position. Easy/cheap column.
|
|
ImGui::Text("Aligned");
|
|
ImGui::SameLine(150);
|
|
ImGui::Text("x=150");
|
|
ImGui::SameLine(300);
|
|
ImGui::Text("x=300");
|
|
ImGui::Text("Aligned");
|
|
ImGui::SameLine(150);
|
|
ImGui::SmallButton("x=150");
|
|
ImGui::SameLine(300);
|
|
ImGui::SmallButton("x=300");
|
|
|
|
// Checkbox
|
|
static bool c1 = false, c2 = false, c3 = false, c4 = false;
|
|
ImGui::Checkbox("My", &c1);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Tailor", &c2);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Is", &c3);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Rich", &c4);
|
|
|
|
// Various
|
|
static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;
|
|
ImGui::PushItemWidth(80);
|
|
const char* items[] = {"AAAA", "BBBB", "CCCC", "DDDD"};
|
|
static int item = -1;
|
|
ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items));
|
|
ImGui::SameLine();
|
|
ImGui::SliderFloat("X", &f0, 0.0f, 5.0f);
|
|
ImGui::SameLine();
|
|
ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f);
|
|
ImGui::SameLine();
|
|
ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f);
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::PushItemWidth(80);
|
|
ImGui::Text("Lists:");
|
|
static int selection[4] = {0, 1, 2, 3};
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (i > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(i);
|
|
ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items));
|
|
ImGui::PopID();
|
|
// if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i);
|
|
}
|
|
ImGui::PopItemWidth();
|
|
|
|
// Dummy
|
|
ImVec2 button_sz(40, 40);
|
|
ImGui::Button("A", button_sz);
|
|
ImGui::SameLine();
|
|
ImGui::Dummy(button_sz);
|
|
ImGui::SameLine();
|
|
ImGui::Button("B", button_sz);
|
|
|
|
// Manually wrapping
|
|
// (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
|
|
ImGui::Text("Manually wrapping:");
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
int buttons_count = 20;
|
|
float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x;
|
|
for (int n = 0; n < buttons_count; n++)
|
|
{
|
|
ImGui::PushID(n);
|
|
ImGui::Button("Box", button_sz);
|
|
float last_button_x2 = ImGui::GetItemRectMax().x;
|
|
float next_button_x2 = last_button_x2 + style.ItemSpacing.x +
|
|
button_sz.x; // Expected position if next button was on same line
|
|
if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)
|
|
ImGui::SameLine();
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Groups"))
|
|
{
|
|
HelpMarker(
|
|
"BeginGroup() basically locks the horizontal position for new line. "
|
|
"EndGroup() bundles the whole group so that you can use \"item\" functions such as "
|
|
"IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
|
|
ImGui::BeginGroup();
|
|
{
|
|
ImGui::BeginGroup();
|
|
ImGui::Button("AAA");
|
|
ImGui::SameLine();
|
|
ImGui::Button("BBB");
|
|
ImGui::SameLine();
|
|
ImGui::BeginGroup();
|
|
ImGui::Button("CCC");
|
|
ImGui::Button("DDD");
|
|
ImGui::EndGroup();
|
|
ImGui::SameLine();
|
|
ImGui::Button("EEE");
|
|
ImGui::EndGroup();
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("First group hovered");
|
|
}
|
|
// Capture the group size and create widgets using the same size
|
|
ImVec2 size = ImGui::GetItemRectSize();
|
|
const float values[5] = {0.5f, 0.20f, 0.80f, 0.60f, 0.25f};
|
|
ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size);
|
|
|
|
ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
|
|
ImGui::SameLine();
|
|
ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
|
|
ImGui::EndGroup();
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Button("LEVERAGE\nBUZZWORD", size);
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::BeginListBox("List", size))
|
|
{
|
|
ImGui::Selectable("Selected", true);
|
|
ImGui::Selectable("Not Selected", false);
|
|
ImGui::EndListBox();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Text Baseline Alignment"))
|
|
{
|
|
{
|
|
ImGui::BulletText("Text baseline:");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. "
|
|
"Lines only composed of text or \"small\" widgets use less vertical space than lines with framed "
|
|
"widgets.");
|
|
ImGui::Indent();
|
|
|
|
ImGui::Text("KO Blahblah");
|
|
ImGui::SameLine();
|
|
ImGui::Button("Some framed item");
|
|
ImGui::SameLine();
|
|
HelpMarker("Baseline of button will look misaligned with text..");
|
|
|
|
// If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
|
|
// (because we don't know what's coming after the Text() statement, we need to move the text baseline
|
|
// down by FramePadding.y ahead of time)
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("OK Blahblah");
|
|
ImGui::SameLine();
|
|
ImGui::Button("Some framed item");
|
|
ImGui::SameLine();
|
|
HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y");
|
|
|
|
// SmallButton() uses the same vertical padding as Text
|
|
ImGui::Button("TEST##1");
|
|
ImGui::SameLine();
|
|
ImGui::Text("TEST");
|
|
ImGui::SameLine();
|
|
ImGui::SmallButton("TEST##2");
|
|
|
|
// If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("Text aligned to framed item");
|
|
ImGui::SameLine();
|
|
ImGui::Button("Item##1");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Item");
|
|
ImGui::SameLine();
|
|
ImGui::SmallButton("Item##2");
|
|
ImGui::SameLine();
|
|
ImGui::Button("Item##3");
|
|
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
|
|
{
|
|
ImGui::BulletText("Multi-line text:");
|
|
ImGui::Indent();
|
|
ImGui::Text("One\nTwo\nThree");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Hello\nWorld");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Banana");
|
|
|
|
ImGui::Text("Banana");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Hello\nWorld");
|
|
ImGui::SameLine();
|
|
ImGui::Text("One\nTwo\nThree");
|
|
|
|
ImGui::Button("HOP##1");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Banana");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Hello\nWorld");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Banana");
|
|
|
|
ImGui::Button("HOP##2");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Hello\nWorld");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Banana");
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
|
|
{
|
|
ImGui::BulletText("Misc items:");
|
|
ImGui::Indent();
|
|
|
|
// SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
|
|
ImGui::Button("80x80", ImVec2(80, 80));
|
|
ImGui::SameLine();
|
|
ImGui::Button("50x50", ImVec2(50, 50));
|
|
ImGui::SameLine();
|
|
ImGui::Button("Button()");
|
|
ImGui::SameLine();
|
|
ImGui::SmallButton("SmallButton()");
|
|
|
|
// Tree
|
|
const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
|
|
ImGui::Button("Button##1");
|
|
ImGui::SameLine(0.0f, spacing);
|
|
if (ImGui::TreeNode("Node##1"))
|
|
{
|
|
// Placeholder tree data
|
|
for (int i = 0; i < 6; i++)
|
|
ImGui::BulletText("Item %d..", i);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
|
|
// Otherwise you can use SmallButton() (smaller fit).
|
|
ImGui::AlignTextToFramePadding();
|
|
|
|
// Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
|
|
// other contents below the node.
|
|
bool node_open = ImGui::TreeNode("Node##2");
|
|
ImGui::SameLine(0.0f, spacing);
|
|
ImGui::Button("Button##2");
|
|
if (node_open)
|
|
{
|
|
// Placeholder tree data
|
|
for (int i = 0; i < 6; i++)
|
|
ImGui::BulletText("Item %d..", i);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Bullet
|
|
ImGui::Button("Button##3");
|
|
ImGui::SameLine(0.0f, spacing);
|
|
ImGui::BulletText("Bullet text");
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::BulletText("Node");
|
|
ImGui::SameLine(0.0f, spacing);
|
|
ImGui::Button("Button##4");
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Scrolling"))
|
|
{
|
|
// Vertical scroll functions
|
|
HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.");
|
|
|
|
static int track_item = 50;
|
|
static bool enable_track = true;
|
|
static bool enable_extra_decorations = false;
|
|
static float scroll_to_off_px = 0.0f;
|
|
static float scroll_to_pos_px = 200.0f;
|
|
|
|
ImGui::Checkbox("Decoration", &enable_extra_decorations);
|
|
|
|
ImGui::Checkbox("Track", &enable_track);
|
|
ImGui::PushItemWidth(100);
|
|
ImGui::SameLine(140);
|
|
enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d");
|
|
|
|
bool scroll_to_off = ImGui::Button("Scroll Offset");
|
|
ImGui::SameLine(140);
|
|
scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px");
|
|
|
|
bool scroll_to_pos = ImGui::Button("Scroll To Pos");
|
|
ImGui::SameLine(140);
|
|
scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");
|
|
ImGui::PopItemWidth();
|
|
|
|
if (scroll_to_off || scroll_to_pos)
|
|
enable_track = false;
|
|
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;
|
|
if (child_w < 1.0f)
|
|
child_w = 1.0f;
|
|
ImGui::PushID("##VerticalScrolling");
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
if (i > 0)
|
|
ImGui::SameLine();
|
|
ImGui::BeginGroup();
|
|
const char* names[] = {"Top", "25%", "Center", "75%", "Bottom"};
|
|
ImGui::TextUnformatted(names[i]);
|
|
|
|
const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
|
|
const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
|
|
const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), true, child_flags);
|
|
if (ImGui::BeginMenuBar())
|
|
{
|
|
ImGui::TextUnformatted("abc");
|
|
ImGui::EndMenuBar();
|
|
}
|
|
if (scroll_to_off)
|
|
ImGui::SetScrollY(scroll_to_off_px);
|
|
if (scroll_to_pos)
|
|
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);
|
|
if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
|
|
{
|
|
for (int item = 0; item < 100; item++)
|
|
{
|
|
if (enable_track && item == track_item)
|
|
{
|
|
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
|
|
ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom
|
|
}
|
|
else
|
|
{
|
|
ImGui::Text("Item %d", item);
|
|
}
|
|
}
|
|
}
|
|
float scroll_y = ImGui::GetScrollY();
|
|
float scroll_max_y = ImGui::GetScrollMaxY();
|
|
ImGui::EndChild();
|
|
ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y);
|
|
ImGui::EndGroup();
|
|
}
|
|
ImGui::PopID();
|
|
|
|
// Horizontal scroll functions
|
|
ImGui::Spacing();
|
|
HelpMarker(
|
|
"Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
|
|
"Because the clipping rectangle of most window hides half worth of WindowPadding on the "
|
|
"left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the "
|
|
"equivalent SetScrollFromPosY(+1) wouldn't.");
|
|
ImGui::PushID("##HorizontalScrolling");
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
|
|
ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar |
|
|
(enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
|
|
ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
|
|
bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), true, child_flags);
|
|
if (scroll_to_off)
|
|
ImGui::SetScrollX(scroll_to_off_px);
|
|
if (scroll_to_pos)
|
|
ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f);
|
|
if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
|
|
{
|
|
for (int item = 0; item < 100; item++)
|
|
{
|
|
if (item > 0)
|
|
ImGui::SameLine();
|
|
if (enable_track && item == track_item)
|
|
{
|
|
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
|
|
ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right
|
|
}
|
|
else
|
|
{
|
|
ImGui::Text("Item %d", item);
|
|
}
|
|
}
|
|
}
|
|
float scroll_x = ImGui::GetScrollX();
|
|
float scroll_max_x = ImGui::GetScrollMaxX();
|
|
ImGui::EndChild();
|
|
ImGui::SameLine();
|
|
const char* names[] = {"Left", "25%", "Center", "75%", "Right"};
|
|
ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x);
|
|
ImGui::Spacing();
|
|
}
|
|
ImGui::PopID();
|
|
|
|
// Miscellaneous Horizontal Scrolling Demo
|
|
HelpMarker(
|
|
"Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n"
|
|
"You may want to also explicitly specify content width by using SetNextWindowContentWidth() before "
|
|
"Begin().");
|
|
static int lines = 7;
|
|
ImGui::SliderInt("Lines", &lines, 1, 15);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
|
|
ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
|
|
ImGui::BeginChild("scrolling", scrolling_child_size, true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
for (int line = 0; line < lines; line++)
|
|
{
|
|
// Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
|
|
// If you want to create your own time line for a real application you may be better off manipulating
|
|
// the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
|
|
// yourself. You may also want to use the lower-level ImDrawList API.
|
|
int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);
|
|
for (int n = 0; n < num_buttons; n++)
|
|
{
|
|
if (n > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(n + line * 1000);
|
|
char num_buf[16];
|
|
sprintf(num_buf, "%d", n);
|
|
const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf;
|
|
float hue = n * 0.05f;
|
|
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f));
|
|
ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f));
|
|
ImGui::PopStyleColor(3);
|
|
ImGui::PopID();
|
|
}
|
|
}
|
|
float scroll_x = ImGui::GetScrollX();
|
|
float scroll_max_x = ImGui::GetScrollMaxX();
|
|
ImGui::EndChild();
|
|
ImGui::PopStyleVar(2);
|
|
float scroll_x_delta = 0.0f;
|
|
ImGui::SmallButton("<<");
|
|
if (ImGui::IsItemActive())
|
|
scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;
|
|
ImGui::SameLine();
|
|
ImGui::Text("Scroll from code");
|
|
ImGui::SameLine();
|
|
ImGui::SmallButton(">>");
|
|
if (ImGui::IsItemActive())
|
|
scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;
|
|
ImGui::SameLine();
|
|
ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x);
|
|
if (scroll_x_delta != 0.0f)
|
|
{
|
|
// Demonstrate a trick: you can use Begin to set yourself in the context of another window
|
|
// (here we are already out of your child window)
|
|
ImGui::BeginChild("scrolling");
|
|
ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::Spacing();
|
|
|
|
static bool show_horizontal_contents_size_demo_window = false;
|
|
ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window);
|
|
|
|
if (show_horizontal_contents_size_demo_window)
|
|
{
|
|
static bool show_h_scrollbar = true;
|
|
static bool show_button = true;
|
|
static bool show_tree_nodes = true;
|
|
static bool show_text_wrapped = false;
|
|
static bool show_columns = true;
|
|
static bool show_tab_bar = true;
|
|
static bool show_child = false;
|
|
static bool explicit_content_size = false;
|
|
static float contents_size_x = 300.0f;
|
|
if (explicit_content_size)
|
|
ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));
|
|
ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window,
|
|
show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0));
|
|
HelpMarker(
|
|
"Test of different widgets react and impact the work rectangle growing when horizontal scrolling is "
|
|
"enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles.");
|
|
ImGui::Checkbox("H-scrollbar", &show_h_scrollbar);
|
|
ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten)
|
|
ImGui::Checkbox("Tree nodes",
|
|
&show_tree_nodes); // Will grow contents size and display highlight over full width
|
|
ImGui::Checkbox("Text wrapped", &show_text_wrapped); // Will grow and use contents size
|
|
ImGui::Checkbox("Columns", &show_columns); // Will use contents size
|
|
ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size
|
|
ImGui::Checkbox("Child", &show_child); // Will grow and use contents size
|
|
ImGui::Checkbox("Explicit content size", &explicit_content_size);
|
|
ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(),
|
|
ImGui::GetScrollMaxY());
|
|
if (explicit_content_size)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::SetNextItemWidth(100);
|
|
ImGui::DragFloat("##csx", &contents_size_x);
|
|
ImVec2 p = ImGui::GetCursorScreenPos();
|
|
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);
|
|
ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y),
|
|
ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);
|
|
ImGui::Dummy(ImVec2(0, 10));
|
|
}
|
|
ImGui::PopStyleVar(2);
|
|
ImGui::Separator();
|
|
if (show_button)
|
|
{
|
|
ImGui::Button("this is a 300-wide button", ImVec2(300, 0));
|
|
}
|
|
if (show_tree_nodes)
|
|
{
|
|
bool open = true;
|
|
if (ImGui::TreeNode("this is a tree node"))
|
|
{
|
|
if (ImGui::TreeNode("another one of those tree node..."))
|
|
{
|
|
ImGui::Text("Some tree contents");
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::CollapsingHeader("CollapsingHeader", &open);
|
|
}
|
|
if (show_text_wrapped)
|
|
{
|
|
ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle.");
|
|
}
|
|
if (show_columns)
|
|
{
|
|
ImGui::Text("Tables:");
|
|
if (ImGui::BeginTable("table", 4, ImGuiTableFlags_Borders))
|
|
{
|
|
for (int n = 0; n < 4; n++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Width %.2f", ImGui::GetContentRegionAvail().x);
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::Text("Columns:");
|
|
ImGui::Columns(4);
|
|
for (int n = 0; n < 4; n++)
|
|
{
|
|
ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
|
|
ImGui::NextColumn();
|
|
}
|
|
ImGui::Columns(1);
|
|
}
|
|
if (show_tab_bar && ImGui::BeginTabBar("Hello"))
|
|
{
|
|
if (ImGui::BeginTabItem("OneOneOne"))
|
|
{
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("TwoTwoTwo"))
|
|
{
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("ThreeThreeThree"))
|
|
{
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("FourFourFour"))
|
|
{
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
}
|
|
if (show_child)
|
|
{
|
|
ImGui::BeginChild("child", ImVec2(0, 0), true);
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Clipping"))
|
|
{
|
|
static ImVec2 size(100.0f, 100.0f);
|
|
static ImVec2 offset(30.0f, 30.0f);
|
|
ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f");
|
|
ImGui::TextWrapped("(Click and drag to scroll)");
|
|
|
|
for (int n = 0; n < 3; n++)
|
|
{
|
|
if (n > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(n);
|
|
ImGui::BeginGroup(); // Lock X position
|
|
|
|
ImGui::InvisibleButton("##empty", size);
|
|
if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))
|
|
{
|
|
offset.x += ImGui::GetIO().MouseDelta.x;
|
|
offset.y += ImGui::GetIO().MouseDelta.y;
|
|
}
|
|
const ImVec2 p0 = ImGui::GetItemRectMin();
|
|
const ImVec2 p1 = ImGui::GetItemRectMax();
|
|
const char* text_str = "Line 1 hello\nLine 2 clip me!";
|
|
const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
|
|
switch (n)
|
|
{
|
|
case 0:
|
|
HelpMarker(
|
|
"Using ImGui::PushClipRect():\n"
|
|
"Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
|
|
"(use this if you want your clipping rectangle to affect interactions)");
|
|
ImGui::PushClipRect(p0, p1, true);
|
|
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
|
|
draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
|
|
ImGui::PopClipRect();
|
|
break;
|
|
case 1:
|
|
HelpMarker(
|
|
"Using ImDrawList::PushClipRect():\n"
|
|
"Will alter ImDrawList rendering only.\n"
|
|
"(use this as a shortcut if you are only using ImDrawList calls)");
|
|
draw_list->PushClipRect(p0, p1, true);
|
|
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
|
|
draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
|
|
draw_list->PopClipRect();
|
|
break;
|
|
case 2:
|
|
HelpMarker(
|
|
"Using ImDrawList::AddText() with a fine ClipRect:\n"
|
|
"Will alter only this specific ImDrawList::AddText() rendering.\n"
|
|
"(this is often used internally to avoid altering the clipping rectangle and minimize draw calls)");
|
|
ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
|
|
draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
|
|
draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL,
|
|
0.0f, &clip_rect);
|
|
break;
|
|
}
|
|
ImGui::EndGroup();
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
|
|
static void ShowDemoWindowPopups()
|
|
{
|
|
if (!ImGui::CollapsingHeader("Popups & Modal windows"))
|
|
return;
|
|
|
|
// The properties of popups windows are:
|
|
// - They block normal mouse hovering detection outside them. (*)
|
|
// - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
|
|
// - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
|
|
// we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
|
|
// (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even
|
|
// when normally blocked by a popup.
|
|
// Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
|
|
// popups at any time.
|
|
|
|
// Typical use for regular windows:
|
|
// 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();
|
|
// Typical use for popups:
|
|
// if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); }
|
|
|
|
// With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
|
|
// This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
|
|
|
|
if (ImGui::TreeNode("Popups"))
|
|
{
|
|
ImGui::TextWrapped(
|
|
"When a popup is active, it inhibits interacting with windows that are behind the popup. "
|
|
"Clicking outside the popup closes it.");
|
|
|
|
static int selected_fish = -1;
|
|
const char* names[] = {"Bream", "Haddock", "Mackerel", "Pollock", "Tilefish"};
|
|
static bool toggles[] = {true, false, false, false, false};
|
|
|
|
// Simple selection popup (if you want to show the current selection inside the Button itself,
|
|
// you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
|
|
if (ImGui::Button("Select.."))
|
|
ImGui::OpenPopup("my_select_popup");
|
|
ImGui::SameLine();
|
|
ImGui::TextUnformatted(selected_fish == -1 ? "<None>" : names[selected_fish]);
|
|
if (ImGui::BeginPopup("my_select_popup"))
|
|
{
|
|
ImGui::Text("Aquarium");
|
|
ImGui::Separator();
|
|
for (int i = 0; i < IM_ARRAYSIZE(names); i++)
|
|
if (ImGui::Selectable(names[i]))
|
|
selected_fish = i;
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Showing a menu with toggles
|
|
if (ImGui::Button("Toggle.."))
|
|
ImGui::OpenPopup("my_toggle_popup");
|
|
if (ImGui::BeginPopup("my_toggle_popup"))
|
|
{
|
|
for (int i = 0; i < IM_ARRAYSIZE(names); i++)
|
|
ImGui::MenuItem(names[i], "", &toggles[i]);
|
|
if (ImGui::BeginMenu("Sub-menu"))
|
|
{
|
|
ImGui::MenuItem("Click me");
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Tooltip here");
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("I am a tooltip over a popup");
|
|
|
|
if (ImGui::Button("Stacked Popup"))
|
|
ImGui::OpenPopup("another popup");
|
|
if (ImGui::BeginPopup("another popup"))
|
|
{
|
|
for (int i = 0; i < IM_ARRAYSIZE(names); i++)
|
|
ImGui::MenuItem(names[i], "", &toggles[i]);
|
|
if (ImGui::BeginMenu("Sub-menu"))
|
|
{
|
|
ImGui::MenuItem("Click me");
|
|
if (ImGui::Button("Stacked Popup"))
|
|
ImGui::OpenPopup("another popup");
|
|
if (ImGui::BeginPopup("another popup"))
|
|
{
|
|
ImGui::Text("I am the last one here.");
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Call the more complete ShowExampleMenuFile which we use in various places of this demo
|
|
if (ImGui::Button("File Menu.."))
|
|
ImGui::OpenPopup("my_file_popup");
|
|
if (ImGui::BeginPopup("my_file_popup"))
|
|
{
|
|
ShowExampleMenuFile();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Context menus"))
|
|
{
|
|
HelpMarker(
|
|
"\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.");
|
|
|
|
// BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
|
|
// if (id == 0)
|
|
// id = GetItemID(); // Use last item id
|
|
// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
|
|
// OpenPopup(id);
|
|
// return BeginPopup(id);
|
|
// For advanced advanced uses you may want to replicate and customize this code.
|
|
// See more details in BeginPopupContextItem().
|
|
|
|
// Example 1
|
|
// When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
|
|
// and BeginPopupContextItem() will use the last item ID as the popup ID.
|
|
{
|
|
const char* names[5] = {"Label1", "Label2", "Label3", "Label4", "Label5"};
|
|
for (int n = 0; n < 5; n++)
|
|
{
|
|
ImGui::Selectable(names[n]);
|
|
if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
|
|
{
|
|
ImGui::Text("This a popup for \"%s\"!", names[n]);
|
|
if (ImGui::Button("Close"))
|
|
ImGui::CloseCurrentPopup();
|
|
ImGui::EndPopup();
|
|
}
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("Right-click to open popup");
|
|
}
|
|
}
|
|
|
|
// Example 2
|
|
// Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to
|
|
// BeginPopupContextItem(). Using an explicit identifier is also convenient if you want to activate the popups
|
|
// from different locations.
|
|
{
|
|
HelpMarker("Text() elements don't have stable identifiers so we need to provide one.");
|
|
static float value = 0.5f;
|
|
ImGui::Text("Value = %.3f <-- (1) right-click this value", value);
|
|
if (ImGui::BeginPopupContextItem("my popup"))
|
|
{
|
|
if (ImGui::Selectable("Set to zero"))
|
|
value = 0.0f;
|
|
if (ImGui::Selectable("Set to PI"))
|
|
value = 3.1415f;
|
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
|
ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f);
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
|
|
// Here we make it that right-clicking this other text element opens the same popup as above.
|
|
// The popup itself will be submitted by the code above.
|
|
ImGui::Text("(2) Or right-click this text");
|
|
ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight);
|
|
|
|
// Back to square one: manually open the same popup.
|
|
if (ImGui::Button("(3) Or click this button"))
|
|
ImGui::OpenPopup("my popup");
|
|
}
|
|
|
|
// Example 3
|
|
// When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),
|
|
// we need to make sure your item identifier is stable.
|
|
// In this example we showcase altering the item label while preserving its identifier, using the ### operator
|
|
// (see FAQ).
|
|
{
|
|
HelpMarker(
|
|
"Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using "
|
|
"the ### operator.");
|
|
static char name[32] = "Label1";
|
|
char buf[64];
|
|
sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
|
|
ImGui::Button(buf);
|
|
if (ImGui::BeginPopupContextItem())
|
|
{
|
|
ImGui::Text("Edit name:");
|
|
ImGui::InputText("##edit", name, IM_ARRAYSIZE(name));
|
|
if (ImGui::Button("Close"))
|
|
ImGui::CloseCurrentPopup();
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Text("(<-- right-click here)");
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Modals"))
|
|
{
|
|
ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside.");
|
|
|
|
if (ImGui::Button("Delete.."))
|
|
ImGui::OpenPopup("Delete?");
|
|
|
|
// Always center this window when appearing
|
|
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
|
|
|
if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
|
{
|
|
ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n");
|
|
ImGui::Separator();
|
|
|
|
// static int unused_i = 0;
|
|
// ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0");
|
|
|
|
static bool dont_ask_me_next_time = false;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
|
ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time);
|
|
ImGui::PopStyleVar();
|
|
|
|
if (ImGui::Button("OK", ImVec2(120, 0)))
|
|
{
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::SetItemDefaultFocus();
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Cancel", ImVec2(120, 0)))
|
|
{
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
if (ImGui::Button("Stacked modals.."))
|
|
ImGui::OpenPopup("Stacked 1");
|
|
if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar))
|
|
{
|
|
if (ImGui::BeginMenuBar())
|
|
{
|
|
if (ImGui::BeginMenu("File"))
|
|
{
|
|
if (ImGui::MenuItem("Some menu item"))
|
|
{}
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenuBar();
|
|
}
|
|
ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.");
|
|
|
|
// Testing behavior of widgets stacking their own regular popups over the modal.
|
|
static int item = 1;
|
|
static float color[4] = {0.4f, 0.7f, 0.0f, 0.5f};
|
|
ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
|
|
ImGui::ColorEdit4("color", color);
|
|
|
|
if (ImGui::Button("Add another modal.."))
|
|
ImGui::OpenPopup("Stacked 2");
|
|
|
|
// Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
|
|
// will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
|
|
// of the bool actually doesn't matter here.
|
|
bool unused_open = true;
|
|
if (ImGui::BeginPopupModal("Stacked 2", &unused_open))
|
|
{
|
|
ImGui::Text("Hello from Stacked The Second!");
|
|
if (ImGui::Button("Close"))
|
|
ImGui::CloseCurrentPopup();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
if (ImGui::Button("Close"))
|
|
ImGui::CloseCurrentPopup();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Menus inside a regular window"))
|
|
{
|
|
ImGui::TextWrapped(
|
|
"Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
|
|
ImGui::Separator();
|
|
|
|
// Note: As a quirk in this very specific example, we want to differentiate the parent of this menu from the
|
|
// parent of the various popup menus above. To do so we are encloding the items in a PushID()/PopID() block
|
|
// to make them two different menusets. If we don't, opening any popup above and hovering our menu here would
|
|
// open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it,
|
|
// which is the desired behavior for regular menus.
|
|
ImGui::PushID("foo");
|
|
ImGui::MenuItem("Menu item", "CTRL+M");
|
|
if (ImGui::BeginMenu("Menu inside a regular window"))
|
|
{
|
|
ShowExampleMenuFile();
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::PopID();
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
|
|
// Dummy data structure that we use for the Table demo.
|
|
// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure if defined inside the demo
|
|
// function)
|
|
namespace
|
|
{
|
|
// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.
|
|
// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.
|
|
// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead!
|
|
// (ImGuiTableSortSpec::ColumnIndex) If you don't use sorting, you will generally never care about giving column an ID!
|
|
enum MyItemColumnID
|
|
{
|
|
MyItemColumnID_ID,
|
|
MyItemColumnID_Name,
|
|
MyItemColumnID_Action,
|
|
MyItemColumnID_Quantity,
|
|
MyItemColumnID_Description
|
|
};
|
|
|
|
struct MyItem
|
|
{
|
|
int ID;
|
|
const char* Name;
|
|
int Quantity;
|
|
|
|
// We have a problem which is affecting _only this demo_ and should not affect your code:
|
|
// As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to
|
|
// qsort(), however qsort doesn't allow passing user data to comparing function. As a workaround, we are storing the
|
|
// sort specs in a static/global for the comparing function to access. In your own use case you would probably pass
|
|
// the sort specs to your sorting/comparing functions directly and not use a global. We could technically call
|
|
// ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called very often by
|
|
// the sorting algorithm it would be a little wasteful.
|
|
static const ImGuiTableSortSpecs* s_current_sort_specs;
|
|
|
|
// Compare function to be used by qsort()
|
|
static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
|
|
{
|
|
const MyItem* a = (const MyItem*)lhs;
|
|
const MyItem* b = (const MyItem*)rhs;
|
|
for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
|
|
{
|
|
// Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()
|
|
// We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!
|
|
const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
|
|
int delta = 0;
|
|
switch (sort_spec->ColumnUserID)
|
|
{
|
|
case MyItemColumnID_ID:
|
|
delta = (a->ID - b->ID);
|
|
break;
|
|
case MyItemColumnID_Name:
|
|
delta = (strcmp(a->Name, b->Name));
|
|
break;
|
|
case MyItemColumnID_Quantity:
|
|
delta = (a->Quantity - b->Quantity);
|
|
break;
|
|
case MyItemColumnID_Description:
|
|
delta = (strcmp(a->Name, b->Name));
|
|
break;
|
|
default:
|
|
IM_ASSERT(0);
|
|
break;
|
|
}
|
|
if (delta > 0)
|
|
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
|
|
if (delta < 0)
|
|
return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
|
|
}
|
|
|
|
// qsort() is instable so always return a way to differenciate items.
|
|
// Your own compare function may want to avoid fallback on implicit sort specs e.g. a Name compare if it wasn't
|
|
// already part of the sort specs.
|
|
return (a->ID - b->ID);
|
|
}
|
|
};
|
|
const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;
|
|
} // namespace
|
|
|
|
// Make the UI compact because there are so many fields
|
|
static void PushStyleCompact()
|
|
{
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
|
|
ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f)));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,
|
|
ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f)));
|
|
}
|
|
|
|
static void PopStyleCompact()
|
|
{
|
|
ImGui::PopStyleVar(2);
|
|
}
|
|
|
|
// Show a combo box with a choice of sizing policies
|
|
static void EditTableSizingFlags(ImGuiTableFlags* p_flags)
|
|
{
|
|
struct EnumDesc
|
|
{
|
|
ImGuiTableFlags Value;
|
|
const char* Name;
|
|
const char* Tooltip;
|
|
};
|
|
static const EnumDesc policies[] = {
|
|
{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."},
|
|
{ImGuiTableFlags_SizingFixedFit, "ImGuiTableFlags_SizingFixedFit",
|
|
"Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width."},
|
|
{ImGuiTableFlags_SizingFixedSame, "ImGuiTableFlags_SizingFixedSame",
|
|
"Columns are all the same width, matching the maximum contents width.\nImplicitly disable "
|
|
"ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible."},
|
|
{ImGuiTableFlags_SizingStretchProp, "ImGuiTableFlags_SizingStretchProp",
|
|
"Columns default to _WidthStretch with weights proportional to their widths."},
|
|
{ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame",
|
|
"Columns default to _WidthStretch with same weights."}};
|
|
int idx;
|
|
for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++)
|
|
if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))
|
|
break;
|
|
const char* preview_text =
|
|
(idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : "";
|
|
if (ImGui::BeginCombo("Sizing Policy", preview_text))
|
|
{
|
|
for (int n = 0; n < IM_ARRAYSIZE(policies); n++)
|
|
if (ImGui::Selectable(policies[n].Name, idx == n))
|
|
*p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;
|
|
ImGui::EndCombo();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(?)");
|
|
if (ImGui::IsItemHovered())
|
|
{
|
|
ImGui::BeginTooltip();
|
|
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f);
|
|
for (int m = 0; m < IM_ARRAYSIZE(policies); m++)
|
|
{
|
|
ImGui::Separator();
|
|
ImGui::Text("%s:", policies[m].Name);
|
|
ImGui::Separator();
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);
|
|
ImGui::TextUnformatted(policies[m].Tooltip);
|
|
}
|
|
ImGui::PopTextWrapPos();
|
|
ImGui::EndTooltip();
|
|
}
|
|
}
|
|
|
|
static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)
|
|
{
|
|
ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled);
|
|
ImGui::SameLine();
|
|
HelpMarker("Master disable flag (also hide from context menu)");
|
|
ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide);
|
|
ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort);
|
|
if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch))
|
|
*p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);
|
|
if (ImGui::CheckboxFlags("_WidthFixed", p_flags, ImGuiTableColumnFlags_WidthFixed))
|
|
*p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::CheckboxFlags("_NoResize", p_flags, ImGuiTableColumnFlags_NoResize);
|
|
ImGui::CheckboxFlags("_NoReorder", p_flags, ImGuiTableColumnFlags_NoReorder);
|
|
ImGui::CheckboxFlags("_NoHide", p_flags, ImGuiTableColumnFlags_NoHide);
|
|
ImGui::CheckboxFlags("_NoClip", p_flags, ImGuiTableColumnFlags_NoClip);
|
|
ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort);
|
|
ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending);
|
|
ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending);
|
|
ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel);
|
|
ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth);
|
|
ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending);
|
|
ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending);
|
|
ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable);
|
|
ImGui::SameLine();
|
|
HelpMarker("Default for column 0");
|
|
ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable);
|
|
ImGui::SameLine();
|
|
HelpMarker("Default for column >0");
|
|
}
|
|
|
|
static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)
|
|
{
|
|
ImGui::CheckboxFlags("_IsEnabled", &flags, ImGuiTableColumnFlags_IsEnabled);
|
|
ImGui::CheckboxFlags("_IsVisible", &flags, ImGuiTableColumnFlags_IsVisible);
|
|
ImGui::CheckboxFlags("_IsSorted", &flags, ImGuiTableColumnFlags_IsSorted);
|
|
ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered);
|
|
}
|
|
|
|
static void ShowDemoWindowTables()
|
|
{
|
|
// ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
|
if (!ImGui::CollapsingHeader("Tables & Columns"))
|
|
return;
|
|
|
|
// Using those as a base value to create width/height that are factor of the size of our font
|
|
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
|
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
|
|
|
ImGui::PushID("Tables");
|
|
|
|
int open_action = -1;
|
|
if (ImGui::Button("Open all"))
|
|
open_action = 1;
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Close all"))
|
|
open_action = 0;
|
|
ImGui::SameLine();
|
|
|
|
// Options
|
|
static bool disable_indent = false;
|
|
ImGui::Checkbox("Disable tree indentation", &disable_indent);
|
|
ImGui::SameLine();
|
|
HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width.");
|
|
ImGui::Separator();
|
|
if (disable_indent)
|
|
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f);
|
|
|
|
// About Styling of tables
|
|
// Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns
|
|
// APIs. There are however a few settings that a shared and part of the ImGuiStyle structure:
|
|
// style.CellPadding // Padding within each cell
|
|
// style.Colors[ImGuiCol_TableHeaderBg] // Table header background
|
|
// style.Colors[ImGuiCol_TableBorderStrong] // Table outer and header borders
|
|
// style.Colors[ImGuiCol_TableBorderLight] // Table inner borders
|
|
// style.Colors[ImGuiCol_TableRowBg] // Table row background when ImGuiTableFlags_RowBg is enabled (even
|
|
// rows) style.Colors[ImGuiCol_TableRowBgAlt] // Table row background when ImGuiTableFlags_RowBg is enabled
|
|
// (odds rows)
|
|
|
|
// Demos
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Basic"))
|
|
{
|
|
// Here we will showcase three different ways to output a table.
|
|
// They are very simple variations of a same thing!
|
|
|
|
// [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
|
|
// In many situations, this is the most flexible and easy to use pattern.
|
|
HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
|
|
if (ImGui::BeginTable("table1", 3))
|
|
{
|
|
for (int row = 0; row < 4; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Row %d Column %d", row, column);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
// [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop +
|
|
// TableSetColumnIndex(). This is generally more convenient when you have code manually submitting the contents
|
|
// of each columns.
|
|
HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.");
|
|
if (ImGui::BeginTable("table2", 3))
|
|
{
|
|
for (int row = 0; row < 4; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Row %d", row);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Some contents");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("123.456");
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
// [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),
|
|
// as TableNextColumn() will automatically wrap around and create new roes as needed.
|
|
// This is generally more convenient when your cells all contains the same type of data.
|
|
HelpMarker(
|
|
"Only using TableNextColumn(), which tends to be convenient for tables where every cells contains the same "
|
|
"type of contents.\n"
|
|
"This is also more similar to the old NextColumn() function of the Columns API, and provided to facilitate "
|
|
"the Columns->Tables API transition.");
|
|
if (ImGui::BeginTable("table3", 3))
|
|
{
|
|
for (int item = 0; item < 14; item++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Item %d", item);
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Borders, background"))
|
|
{
|
|
// Expose a few Borders related flags interactively
|
|
enum ContentsType
|
|
{
|
|
CT_Text,
|
|
CT_FillButton
|
|
};
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
|
|
static bool display_headers = false;
|
|
static int contents_type = CT_Text;
|
|
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | "
|
|
"ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterH");
|
|
ImGui::Indent();
|
|
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
|
|
ImGui::Indent();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
|
|
ImGui::Unindent();
|
|
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
|
|
ImGui::Indent();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
|
|
ImGui::Unindent();
|
|
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner);
|
|
ImGui::Unindent();
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("Cell contents:");
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton("Text", &contents_type, CT_Text);
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton("FillButton", &contents_type, CT_FillButton);
|
|
ImGui::Checkbox("Display headers", &display_headers);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody);
|
|
ImGui::SameLine();
|
|
HelpMarker("Disable vertical borders in columns Body (borders will always appears in Headers");
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table1", 3, flags))
|
|
{
|
|
// Display headers so we can inspect their interaction with borders.
|
|
// (Headers are not the main purpose of this section of the demo, so we are not elaborating on them too
|
|
// much. See other sections for details)
|
|
if (display_headers)
|
|
{
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
ImGui::TableHeadersRow();
|
|
}
|
|
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
char buf[32];
|
|
sprintf(buf, "Hello %d,%d", column, row);
|
|
if (contents_type == CT_Text)
|
|
ImGui::TextUnformatted(buf);
|
|
else if (contents_type)
|
|
ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Resizable, stretch"))
|
|
{
|
|
// By default, if we don't enable ScrollX the sizing policy for each columns is "Stretch"
|
|
// Each columns maintain a sizing weight, and they will occupy all available width.
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable |
|
|
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
|
ImGuiTableFlags_ContextMenuInBody;
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Using the _Resizable flag automatically enables the _BordersInnerV flag as well, this is why the resize "
|
|
"borders are still showing when unchecking this.");
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table1", 3, flags))
|
|
{
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Hello %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Resizable, fixed"))
|
|
{
|
|
// Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)
|
|
// So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available
|
|
// width (unless table is small) If there is not enough available width to fit all columns, they will however be
|
|
// resized down.
|
|
// FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved
|
|
// settings
|
|
HelpMarker(
|
|
"Using _Resizable + _SizingFixedFit flags.\n"
|
|
"Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n"
|
|
"Double-click a column border to auto-fit the column to its contents.");
|
|
PushStyleCompact();
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable |
|
|
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
|
ImGuiTableFlags_ContextMenuInBody;
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table1", 3, flags))
|
|
{
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Hello %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Resizable, mixed"))
|
|
{
|
|
HelpMarker(
|
|
"Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n"
|
|
"When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use "
|
|
"_WidthStretch.");
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg |
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
|
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
|
|
|
|
if (ImGui::BeginTable("table1", 3, flags))
|
|
{
|
|
ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableHeadersRow();
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
if (ImGui::BeginTable("table2", 6, flags))
|
|
{
|
|
ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);
|
|
ImGui::TableSetupColumn("DDD", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupColumn("EEE", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupColumn("FFF", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);
|
|
ImGui::TableHeadersRow();
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 6; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Reorderable, hideable, with headers"))
|
|
{
|
|
HelpMarker(
|
|
"Click and drag column headers to reorder columns.\n\n"
|
|
"Right-click on a header to open a context menu.");
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
|
ImGuiTableFlags_BordersV;
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags,
|
|
ImGuiTableFlags_NoBordersInBodyUntilResize);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Disable vertical borders in columns Body until hovered for resize (borders will always appears in "
|
|
"Headers)");
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table1", 3, flags))
|
|
{
|
|
// Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in
|
|
// each column. (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight
|
|
// etc.)
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
ImGui::TableHeadersRow();
|
|
for (int row = 0; row < 6; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Hello %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
// Use outer_size.x == 0.0f instead of default to make the table as tight as possible (only valid when no
|
|
// scrolling and no stretch column)
|
|
if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f)))
|
|
{
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
ImGui::TableHeadersRow();
|
|
for (int row = 0; row < 6; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Fixed %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Padding"))
|
|
{
|
|
// First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.
|
|
// We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.
|
|
HelpMarker(
|
|
"We often want outer padding activated when any using features which makes the edges of a column visible:\n"
|
|
"e.g.:\n"
|
|
"- BorderOuterV\n"
|
|
"- any form of row selection\n"
|
|
"Because of this, activating BorderOuterV sets the default to PadOuterX. Using PadOuterX or NoPadOuterX "
|
|
"you can override the default.\n\n"
|
|
"Actual padding values are using style.CellPadding.\n\n"
|
|
"In this demo we don't show horizontal borders to emphasis how they don't affect default horizontal "
|
|
"padding.");
|
|
|
|
static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX);
|
|
ImGui::SameLine();
|
|
HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX);
|
|
ImGui::SameLine();
|
|
HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding "
|
|
"if BordersOuterV is off)");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV);
|
|
static bool show_headers = false;
|
|
ImGui::Checkbox("show_headers", &show_headers);
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table_padding", 3, flags1))
|
|
{
|
|
if (show_headers)
|
|
{
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
ImGui::TableHeadersRow();
|
|
}
|
|
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
if (row == 0)
|
|
{
|
|
ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
|
|
}
|
|
else
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "Hello %d,%d", column, row);
|
|
ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
|
|
}
|
|
// if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)
|
|
// ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
// Second example: set style.CellPadding to (0.0) or a custom value.
|
|
// FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...
|
|
HelpMarker("Setting style.CellPadding to (0,0) or a custom value.");
|
|
static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
|
|
static ImVec2 cell_padding(0.0f, 0.0f);
|
|
static bool show_widget_frame_bg = true;
|
|
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable);
|
|
ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg);
|
|
ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f");
|
|
PopStyleCompact();
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);
|
|
if (ImGui::BeginTable("table_padding_2", 3, flags2))
|
|
{
|
|
static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells
|
|
static bool init = true;
|
|
if (!show_widget_frame_bg)
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
|
|
for (int cell = 0; cell < 3 * 5; cell++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
if (init)
|
|
strcpy(text_bufs[cell], "edit me");
|
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
|
ImGui::PushID(cell);
|
|
ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell]));
|
|
ImGui::PopID();
|
|
}
|
|
if (!show_widget_frame_bg)
|
|
ImGui::PopStyleColor();
|
|
init = false;
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar();
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Sizing policies"))
|
|
{
|
|
static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH |
|
|
ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags1, ImGuiTableFlags_NoHostExtendX);
|
|
PopStyleCompact();
|
|
|
|
static ImGuiTableFlags sizing_policy_flags[4] = {
|
|
ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp,
|
|
ImGuiTableFlags_SizingStretchSame};
|
|
for (int table_n = 0; table_n < 4; table_n++)
|
|
{
|
|
ImGui::PushID(table_n);
|
|
ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);
|
|
EditTableSizingFlags(&sizing_policy_flags[table_n]);
|
|
|
|
// To make it easier to understand the different sizing policy,
|
|
// For each policy: we display one table where the columns have equal contents width, and one where the
|
|
// columns have different contents width.
|
|
if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1))
|
|
{
|
|
for (int row = 0; row < 3; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Oh dear");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Oh dear");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Oh dear");
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
if (ImGui::BeginTable("table2", 3, sizing_policy_flags[table_n] | flags1))
|
|
{
|
|
for (int row = 0; row < 3; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("AAAA");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("BBBBBBBB");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("CCCCCCCCCCCC");
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::TextUnformatted("Advanced");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"This section allows you to interact and see the effect of various sizing policies depending on whether "
|
|
"Scroll is enabled and the contents of your columns.");
|
|
|
|
enum ContentsType
|
|
{
|
|
CT_ShowWidth,
|
|
CT_ShortText,
|
|
CT_LongText,
|
|
CT_Button,
|
|
CT_FillButton,
|
|
CT_InputText
|
|
};
|
|
static ImGuiTableFlags flags =
|
|
ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
|
|
static int contents_type = CT_ShowWidth;
|
|
static int column_count = 3;
|
|
|
|
PushStyleCompact();
|
|
ImGui::PushID("Advanced");
|
|
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
|
|
EditTableSizingFlags(&flags);
|
|
ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0");
|
|
if (contents_type == CT_FillButton)
|
|
{
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop where contents "
|
|
"width can feed into auto-column width can feed into contents width.");
|
|
}
|
|
ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
|
|
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.");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
|
|
ImGui::PopItemWidth();
|
|
ImGui::PopID();
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table2", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))
|
|
{
|
|
for (int cell = 0; cell < 10 * column_count; cell++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
int column = ImGui::TableGetColumnIndex();
|
|
int row = ImGui::TableGetRowIndex();
|
|
|
|
ImGui::PushID(cell);
|
|
char label[32];
|
|
static char text_buf[32] = "";
|
|
sprintf(label, "Hello %d,%d", column, row);
|
|
switch (contents_type)
|
|
{
|
|
case CT_ShortText:
|
|
ImGui::TextUnformatted(label);
|
|
break;
|
|
case CT_LongText:
|
|
ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row);
|
|
break;
|
|
case CT_ShowWidth:
|
|
ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x);
|
|
break;
|
|
case CT_Button:
|
|
ImGui::Button(label);
|
|
break;
|
|
case CT_FillButton:
|
|
ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));
|
|
break;
|
|
case CT_InputText:
|
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
|
ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf));
|
|
break;
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Vertical scrolling, with clipping"))
|
|
{
|
|
HelpMarker(
|
|
"Here we activate ScrollY, which will create a child window container to allow hosting scrollable "
|
|
"contents.\n\nWe also demonstrate using ImGuiListClipper to virtualize the submission of many items.");
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter |
|
|
ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable |
|
|
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
|
|
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
|
|
PopStyleCompact();
|
|
|
|
// When using ScrollX or ScrollY we need to specify a size for our table container!
|
|
// Otherwise by default the table will fit all available space, like a BeginChild() call.
|
|
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
|
|
if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size))
|
|
{
|
|
ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
|
|
ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None);
|
|
ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None);
|
|
ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None);
|
|
ImGui::TableHeadersRow();
|
|
|
|
// Demonstrate using clipper for large vertical lists
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin(1000);
|
|
while (clipper.Step())
|
|
{
|
|
for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Hello %d,%d", column, row);
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Horizontal scrolling"))
|
|
{
|
|
HelpMarker(
|
|
"When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, "
|
|
"as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n"
|
|
"Also note that as of the current version, you will almost always want to enable ScrollY along with "
|
|
"ScrollX,"
|
|
"because the container window won't automatically extend vertically to fix contents (this may be improved "
|
|
"in future versions).");
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
|
|
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
ImGuiTableFlags_Hideable;
|
|
static int freeze_cols = 1;
|
|
static int freeze_rows = 1;
|
|
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
|
|
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
|
|
ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
|
|
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
|
|
ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
|
|
PopStyleCompact();
|
|
|
|
// When using ScrollX or ScrollY we need to specify a size for our table container!
|
|
// Otherwise by default the table will fit all available space, like a BeginChild() call.
|
|
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
|
|
if (ImGui::BeginTable("table_scrollx", 7, flags, outer_size))
|
|
{
|
|
ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
|
|
ImGui::TableSetupColumn("Line #",
|
|
ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our
|
|
// use of TableSetupScrollFreeze()
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
ImGui::TableSetupColumn("Four");
|
|
ImGui::TableSetupColumn("Five");
|
|
ImGui::TableSetupColumn("Six");
|
|
ImGui::TableHeadersRow();
|
|
for (int row = 0; row < 20; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 7; column++)
|
|
{
|
|
// Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or
|
|
// performing width measurement. Because here we know that:
|
|
// - A) all our columns are contributing the same to row height
|
|
// - B) column 0 is always visible,
|
|
// We only always submit this one column and can skip others.
|
|
// More advanced per-column clipping behaviors may benefit from polling the status flags via
|
|
// TableGetColumnFlags().
|
|
if (!ImGui::TableSetColumnIndex(column) && column > 0)
|
|
continue;
|
|
if (column == 0)
|
|
ImGui::Text("Line %d", row);
|
|
else
|
|
ImGui::Text("Hello world %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::TextUnformatted("Stretch + ScrollX");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Showcase using Stretch columns + ScrollX together: "
|
|
"this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n"
|
|
"Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns + ScrollX "
|
|
"together doesn't make sense.");
|
|
static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX |
|
|
ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg |
|
|
ImGuiTableFlags_ContextMenuInBody;
|
|
static float inner_width = 1000.0f;
|
|
PushStyleCompact();
|
|
ImGui::PushID("flags3");
|
|
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX);
|
|
ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f");
|
|
ImGui::PopItemWidth();
|
|
ImGui::PopID();
|
|
PopStyleCompact();
|
|
if (ImGui::BeginTable("table2", 7, flags2, outer_size, inner_width))
|
|
{
|
|
for (int cell = 0; cell < 20 * 7; cell++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Columns flags"))
|
|
{
|
|
// Create a first table just to show all the options/flags we want to make visible in our example!
|
|
const int column_count = 3;
|
|
const char* column_names[column_count] = {"One", "Two", "Three"};
|
|
static ImGuiTableColumnFlags column_flags[column_count] = {
|
|
ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide};
|
|
static ImGuiTableColumnFlags column_flags_out[column_count] = {0, 0, 0}; // Output from TableGetColumnFlags()
|
|
|
|
if (ImGui::BeginTable("table_columns_flags_checkboxes", column_count, ImGuiTableFlags_None))
|
|
{
|
|
PushStyleCompact();
|
|
for (int column = 0; column < column_count; column++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(column);
|
|
ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation
|
|
ImGui::Text("'%s'", column_names[column]);
|
|
ImGui::Spacing();
|
|
ImGui::Text("Input flags:");
|
|
EditTableColumnsFlags(&column_flags[column]);
|
|
ImGui::Spacing();
|
|
ImGui::Text("Output flags:");
|
|
ShowTableColumnsStatusFlags(column_flags_out[column]);
|
|
ImGui::PopID();
|
|
}
|
|
PopStyleCompact();
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
// Create the real table we care about for the example!
|
|
// We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags
|
|
// above, otherwise in a non-scrolling table columns are always visible (unless using
|
|
// ImGuiTableFlags_NoKeepColumnsVisible + resizing the parent window down)
|
|
const ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX |
|
|
ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter |
|
|
ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable |
|
|
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;
|
|
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);
|
|
if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size))
|
|
{
|
|
for (int column = 0; column < column_count; column++)
|
|
ImGui::TableSetupColumn(column_names[column], column_flags[column]);
|
|
ImGui::TableHeadersRow();
|
|
for (int column = 0; column < column_count; column++)
|
|
column_flags_out[column] = ImGui::TableGetColumnFlags(column);
|
|
float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);
|
|
for (int row = 0; row < 8; row++)
|
|
{
|
|
ImGui::Indent(indent_step); // Add some indentation to demonstrate usage of per-column
|
|
// IndentEnable/IndentDisable flags.
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < column_count; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column));
|
|
}
|
|
}
|
|
ImGui::Unindent(indent_step * 8.0f);
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Columns widths"))
|
|
{
|
|
HelpMarker("Using TableSetupColumn() to setup default width.");
|
|
|
|
static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags1,
|
|
ImGuiTableFlags_NoBordersInBodyUntilResize);
|
|
PopStyleCompact();
|
|
if (ImGui::BeginTable("table1", 3, flags1))
|
|
{
|
|
// We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to
|
|
// ImGuiTableColumnFlags_WidthFixed.
|
|
ImGui::TableSetupColumn("one", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f
|
|
ImGui::TableSetupColumn("two", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f
|
|
ImGui::TableSetupColumn("three", ImGuiTableColumnFlags_WidthFixed); // Default to auto
|
|
ImGui::TableHeadersRow();
|
|
for (int row = 0; row < 4; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
if (row == 0)
|
|
ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
|
|
else
|
|
ImGui::Text("Hello %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
HelpMarker(
|
|
"Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, fixed columns "
|
|
"with set width may still be shrunk down if there's not enough space in the host.");
|
|
|
|
static ImGuiTableFlags flags2 = ImGuiTableFlags_None;
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags2, ImGuiTableFlags_NoKeepColumnsVisible);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags2, ImGuiTableFlags_BordersInnerV);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags2, ImGuiTableFlags_BordersOuterV);
|
|
PopStyleCompact();
|
|
if (ImGui::BeginTable("table2", 4, flags2))
|
|
{
|
|
// We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to
|
|
// ImGuiTableColumnFlags_WidthFixed.
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f);
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 4; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
if (row == 0)
|
|
ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
|
|
else
|
|
ImGui::Text("Hello %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Nested tables"))
|
|
{
|
|
HelpMarker("This demonstrate embedding a table into another table cell.");
|
|
|
|
if (ImGui::BeginTable("table_nested1", 2,
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
ImGuiTableFlags_Hideable))
|
|
{
|
|
ImGui::TableSetupColumn("A0");
|
|
ImGui::TableSetupColumn("A1");
|
|
ImGui::TableHeadersRow();
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("A0 Row 0");
|
|
{
|
|
float rows_height = TEXT_BASE_HEIGHT * 2;
|
|
if (ImGui::BeginTable("table_nested2", 2,
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
|
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
|
|
{
|
|
ImGui::TableSetupColumn("B0");
|
|
ImGui::TableSetupColumn("B1");
|
|
ImGui::TableHeadersRow();
|
|
|
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("B0 Row 0");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("B1 Row 0");
|
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("B0 Row 1");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("B1 Row 1");
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("A1 Row 0");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("A0 Row 1");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("A1 Row 1");
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Row height"))
|
|
{
|
|
HelpMarker(
|
|
"You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top "
|
|
"and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\nWe "
|
|
"cannot honor a _maximum_ row height as that would requires a unique clipping rectangle per row.");
|
|
if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerV))
|
|
{
|
|
for (int row = 0; row < 10; row++)
|
|
{
|
|
float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row);
|
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("min_row_height = %.2f", min_row_height);
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Outer size"))
|
|
{
|
|
// Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY
|
|
// Important to that note how the two flags have slightly different behaviors!
|
|
ImGui::Text("Using NoHostExtendX and NoHostExtendY:");
|
|
PushStyleCompact();
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
|
ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg |
|
|
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
|
|
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.");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
|
|
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.");
|
|
PopStyleCompact();
|
|
|
|
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
|
|
if (ImGui::BeginTable("table1", 3, flags, outer_size))
|
|
{
|
|
for (int row = 0; row < 10; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Cell %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Text("Hello!");
|
|
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("Using explicit size:");
|
|
if (ImGui::BeginTable("table2", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg,
|
|
ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
|
|
{
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Cell %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg,
|
|
ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
|
|
{
|
|
for (int row = 0; row < 3; row++)
|
|
{
|
|
ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f);
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Cell %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Background color"))
|
|
{
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;
|
|
static int row_bg_type = 1;
|
|
static int row_bg_target = 1;
|
|
static int cell_bg_type = 1;
|
|
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
|
|
ImGui::SameLine();
|
|
HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.");
|
|
ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0");
|
|
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.");
|
|
ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0");
|
|
ImGui::SameLine();
|
|
HelpMarker("We are colorizing cells to B1->C2 here.");
|
|
IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);
|
|
IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);
|
|
IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table1", 5, flags))
|
|
{
|
|
for (int row = 0; row < 6; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
|
|
// Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX,
|
|
// ...)' 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.
|
|
if (row_bg_type != 0)
|
|
{
|
|
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?
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color);
|
|
}
|
|
|
|
// Fill cells
|
|
for (int column = 0; column < 5; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("%c%c", 'A' + row, '0' + column);
|
|
|
|
// Change background of Cells B1->C2
|
|
// Demonstrate setting a cell background color with
|
|
// 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)' (the CellBg color will be blended over
|
|
// the RowBg and ColumnBg colors) We can also pass a column number as a third parameter to
|
|
// TableSetBgColor() and do this outside the column loop.
|
|
if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)
|
|
{
|
|
ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f));
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Tree view"))
|
|
{
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH |
|
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg |
|
|
ImGuiTableFlags_NoBordersInBody;
|
|
|
|
if (ImGui::BeginTable("3ways", 3, flags))
|
|
{
|
|
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is
|
|
// On
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide);
|
|
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f);
|
|
ImGui::TableHeadersRow();
|
|
|
|
// Simple storage to output a dummy file-system.
|
|
struct MyTreeNode
|
|
{
|
|
const char* Name;
|
|
const char* Type;
|
|
int Size;
|
|
int ChildIdx;
|
|
int ChildCount;
|
|
static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
|
|
{
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
const bool is_folder = (node->ChildCount > 0);
|
|
if (is_folder)
|
|
{
|
|
bool open = ImGui::TreeNodeEx(node->Name, ImGuiTreeNodeFlags_SpanFullWidth);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextDisabled("--");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(node->Type);
|
|
if (open)
|
|
{
|
|
for (int child_n = 0; child_n < node->ChildCount; child_n++)
|
|
DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes);
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui::TreeNodeEx(node->Name, ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet |
|
|
ImGuiTreeNodeFlags_NoTreePushOnOpen |
|
|
ImGuiTreeNodeFlags_SpanFullWidth);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", node->Size);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(node->Type);
|
|
}
|
|
}
|
|
};
|
|
static const MyTreeNode nodes[] = {
|
|
{"Root", "Folder", -1, 1, 3}, // 0
|
|
{"Music", "Folder", -1, 4, 2}, // 1
|
|
{"Textures", "Folder", -1, 6, 3}, // 2
|
|
{"desktop.ini", "System file", 1024, -1, -1}, // 3
|
|
{"File1_a.wav", "Audio file", 123000, -1, -1}, // 4
|
|
{"File1_b.wav", "Audio file", 456000, -1, -1}, // 5
|
|
{"Image001.png", "Image file", 203128, -1, -1}, // 6
|
|
{"Copy of Image001.png", "Image file", 203256, -1, -1}, // 7
|
|
{"Copy of Image001 (Final2).png", "Image file", 203512, -1, -1}, // 8
|
|
};
|
|
|
|
MyTreeNode::DisplayNode(&nodes[0], nodes);
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Item width"))
|
|
{
|
|
HelpMarker(
|
|
"Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n"
|
|
"Note that on auto-resizing non-resizable fixed columns, querying the content width for e.g. "
|
|
"right-alignment doesn't make sense.");
|
|
if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders))
|
|
{
|
|
ImGui::TableSetupColumn("small");
|
|
ImGui::TableSetupColumn("half");
|
|
ImGui::TableSetupColumn("right-align");
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int row = 0; row < 3; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
if (row == 0)
|
|
{
|
|
// Setup ItemWidth once (instead of setting up every time, which is also possible but less
|
|
// efficient)
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
|
|
ImGui::TableSetColumnIndex(2);
|
|
ImGui::PushItemWidth(-FLT_MIN); // Right-aligned
|
|
}
|
|
|
|
// Draw our contents
|
|
static float dummy_f = 0.0f;
|
|
ImGui::PushID(row);
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f);
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f);
|
|
ImGui::TableSetColumnIndex(2);
|
|
ImGui::SliderFloat("float2", &dummy_f, 0.0f, 1.0f);
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Demonstrate using TableHeader() calls instead of TableHeadersRow()
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Custom headers"))
|
|
{
|
|
const int COLUMNS_COUNT = 3;
|
|
if (ImGui::BeginTable("table_custom_headers", COLUMNS_COUNT,
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
|
|
{
|
|
ImGui::TableSetupColumn("Apricot");
|
|
ImGui::TableSetupColumn("Banana");
|
|
ImGui::TableSetupColumn("Cherry");
|
|
|
|
// Dummy entire-column selection storage
|
|
// FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
|
|
static bool column_selected[3] = {};
|
|
|
|
// Instead of calling TableHeadersRow() we'll submit custom headers ourselves
|
|
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
|
for (int column = 0; column < COLUMNS_COUNT; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
const char* column_name =
|
|
ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
|
|
ImGui::PushID(column);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
|
ImGui::Checkbox("##checkall", &column_selected[column]);
|
|
ImGui::PopStyleVar();
|
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
|
ImGui::TableHeader(column_name);
|
|
ImGui::PopID();
|
|
}
|
|
|
|
for (int row = 0; row < 5; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < 3; column++)
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "Cell %d,%d", column, row);
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Selectable(buf, column_selected[column]);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by
|
|
// TableHeadersRow()/TableHeader()
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Context menus"))
|
|
{
|
|
HelpMarker(
|
|
"By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default "
|
|
"context-menu.\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.");
|
|
static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders |
|
|
ImGuiTableFlags_ContextMenuInBody;
|
|
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags1, ImGuiTableFlags_ContextMenuInBody);
|
|
PopStyleCompact();
|
|
|
|
// Context Menus: first example
|
|
// [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.
|
|
// [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody
|
|
// is set)
|
|
const int COLUMNS_COUNT = 3;
|
|
if (ImGui::BeginTable("table_context_menu", COLUMNS_COUNT, flags1))
|
|
{
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
|
|
// [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.
|
|
ImGui::TableHeadersRow();
|
|
|
|
// Submit dummy contents
|
|
for (int row = 0; row < 4; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < COLUMNS_COUNT; column++)
|
|
{
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Cell %d,%d", column, row);
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
// Context Menus: second example
|
|
// [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
|
|
// [2.2] Right-click on the ".." to open a custom popup
|
|
// [2.3] Right-click in columns to open another custom popup
|
|
HelpMarker(
|
|
"Demonstrate mixing table context menu (over header), item context button (over button) and custom "
|
|
"per-colum context menu (over column body).");
|
|
ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit |
|
|
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
|
|
if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2))
|
|
{
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
|
|
// [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
|
|
ImGui::TableHeadersRow();
|
|
for (int row = 0; row < 4; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
for (int column = 0; column < COLUMNS_COUNT; column++)
|
|
{
|
|
// Submit dummy contents
|
|
ImGui::TableSetColumnIndex(column);
|
|
ImGui::Text("Cell %d,%d", column, row);
|
|
ImGui::SameLine();
|
|
|
|
// [2.2] Right-click on the ".." to open a custom popup
|
|
ImGui::PushID(row * COLUMNS_COUNT + column);
|
|
ImGui::SmallButton("..");
|
|
if (ImGui::BeginPopupContextItem())
|
|
{
|
|
ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row);
|
|
if (ImGui::Button("Close"))
|
|
ImGui::CloseCurrentPopup();
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
}
|
|
|
|
// [2.3] Right-click anywhere in columns to open another custom popup
|
|
// (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with
|
|
// ImGuiPopupFlags_NoOpenOverExistingPopup to manage popup priority as the popups triggers, here "are we
|
|
// hovering a column" are overlapping)
|
|
int hovered_column = -1;
|
|
for (int column = 0; column < COLUMNS_COUNT + 1; column++)
|
|
{
|
|
ImGui::PushID(column);
|
|
if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered)
|
|
hovered_column = column;
|
|
if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1))
|
|
ImGui::OpenPopup("MyPopup");
|
|
if (ImGui::BeginPopup("MyPopup"))
|
|
{
|
|
if (column == COLUMNS_COUNT)
|
|
ImGui::Text("This is a custom popup for unused space after the last column.");
|
|
else
|
|
ImGui::Text("This is a custom popup for Column %d", column);
|
|
if (ImGui::Button("Close"))
|
|
ImGui::CloseCurrentPopup();
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
ImGui::Text("Hovered column: %d", hovered_column);
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Demonstrate creating multiple tables with the same ID
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Synced instances"))
|
|
{
|
|
HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc.");
|
|
for (int n = 0; n < 3; n++)
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "Synced Table %d", n);
|
|
bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen);
|
|
if (open && ImGui::BeginTable("Table", 3,
|
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders |
|
|
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings))
|
|
{
|
|
ImGui::TableSetupColumn("One");
|
|
ImGui::TableSetupColumn("Two");
|
|
ImGui::TableSetupColumn("Three");
|
|
ImGui::TableHeadersRow();
|
|
for (int cell = 0; cell < 9; cell++)
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("this cell %d", cell);
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Demonstrate using Sorting facilities
|
|
// This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle
|
|
// sorting. Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have
|
|
// been modified)
|
|
static const char* template_items_names[] = {"Banana", "Apple", "Cherry", "Watermelon", "Grapefruit",
|
|
"Strawberry", "Mango", "Kiwi", "Orange", "Pineapple",
|
|
"Blueberry", "Plum", "Coconut", "Pear", "Apricot"};
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Sorting"))
|
|
{
|
|
// Create item list
|
|
static ImVector<MyItem> items;
|
|
if (items.Size == 0)
|
|
{
|
|
items.resize(50, MyItem());
|
|
for (int n = 0; n < items.Size; n++)
|
|
{
|
|
const int template_n = n % IM_ARRAYSIZE(template_items_names);
|
|
MyItem& item = items[n];
|
|
item.ID = n;
|
|
item.Name = template_items_names[template_n];
|
|
item.Quantity = (n * n - n) % 20; // Assign default quantities
|
|
}
|
|
}
|
|
|
|
// Options
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti |
|
|
ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
|
ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY;
|
|
PushStyleCompact();
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() "
|
|
"may return specs where (SpecsCount > 1).");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs "
|
|
"where (SpecsCount == 0).");
|
|
PopStyleCompact();
|
|
|
|
if (ImGui::BeginTable("table_sorting", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f))
|
|
{
|
|
// Declare columns
|
|
// We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort
|
|
// specifications. This is so our sort function can identify a column given our own identifier. We could
|
|
// also identify them based on their index! Demonstrate using a mixture of flags among available
|
|
// sort-related flags:
|
|
// - ImGuiTableColumnFlags_DefaultSort
|
|
// - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending /
|
|
// ImGuiTableColumnFlags_NoSortDescending
|
|
// - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending
|
|
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f,
|
|
MyItemColumnID_ID);
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
|
|
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f,
|
|
MyItemColumnID_Action);
|
|
ImGui::TableSetupColumn("Quantity",
|
|
ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch,
|
|
0.0f, MyItemColumnID_Quantity);
|
|
ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible
|
|
ImGui::TableHeadersRow();
|
|
|
|
// Sort our data if sort specs have been changed!
|
|
if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs())
|
|
if (sorts_specs->SpecsDirty)
|
|
{
|
|
MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function.
|
|
if (items.Size > 1)
|
|
qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs);
|
|
MyItem::s_current_sort_specs = NULL;
|
|
sorts_specs->SpecsDirty = false;
|
|
}
|
|
|
|
// Demonstrate using clipper for large vertical lists
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin(items.Size);
|
|
while (clipper.Step())
|
|
for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
|
|
{
|
|
// Display a data item
|
|
MyItem* item = &items[row_n];
|
|
ImGui::PushID(item->ID);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%04d", item->ID);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(item->Name);
|
|
ImGui::TableNextColumn();
|
|
ImGui::SmallButton("None");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", item->Quantity);
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// In this example we'll expose most table flags and settings.
|
|
// For specific flags and settings refer to the corresponding section for more detailed explanation.
|
|
// This section is mostly useful to experiment with combining certain flags or settings with each others.
|
|
// ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]
|
|
if (open_action != -1)
|
|
ImGui::SetNextItemOpen(open_action != 0);
|
|
if (ImGui::TreeNode("Advanced"))
|
|
{
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
|
ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti |
|
|
ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders |
|
|
ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollX |
|
|
ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit;
|
|
|
|
enum ContentsType
|
|
{
|
|
CT_Text,
|
|
CT_Button,
|
|
CT_SmallButton,
|
|
CT_FillButton,
|
|
CT_Selectable,
|
|
CT_SelectableSpanRow
|
|
};
|
|
static int contents_type = CT_SelectableSpanRow;
|
|
const char* contents_type_names[] = {"Text", "Button", "SmallButton",
|
|
"FillButton", "Selectable", "Selectable (span row)"};
|
|
static int freeze_cols = 1;
|
|
static int freeze_rows = 1;
|
|
static int items_count = IM_ARRAYSIZE(template_items_names) * 2;
|
|
static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);
|
|
static float row_min_height = 0.0f; // Auto
|
|
static float inner_width_with_scroll = 0.0f; // Auto-extend
|
|
static bool outer_size_enabled = true;
|
|
static bool show_headers = true;
|
|
static bool show_wrapped_text = false;
|
|
// static ImGuiTextFilter filter;
|
|
// ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first
|
|
// pass on table which tend to affects column sizing
|
|
if (ImGui::TreeNode("Options"))
|
|
{
|
|
// Make the UI compact because there are so many fields
|
|
PushStyleCompact();
|
|
ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f);
|
|
|
|
if (ImGui::TreeNodeEx("Features:", ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", &flags, ImGuiTableFlags_Sortable);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", &flags, ImGuiTableFlags_NoSavedSettings);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags, ImGuiTableFlags_ContextMenuInBody);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Decorations:", ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody);
|
|
ImGui::SameLine();
|
|
HelpMarker("Disable vertical borders in columns Body (borders will always appears in Headers");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags,
|
|
ImGuiTableFlags_NoBordersInBodyUntilResize);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Disable vertical borders in columns Body until hovered for resize (borders will always appears in "
|
|
"Headers)");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
EditTableSizingFlags(&flags);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"In the Advanced demo we override the policy of each column so those table-wide settings have less "
|
|
"effect that typical.");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
|
|
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.");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
|
|
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.");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags,
|
|
ImGuiTableFlags_NoKeepColumnsVisible);
|
|
ImGui::SameLine();
|
|
HelpMarker("Only available if ScrollX is disabled.");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
|
|
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.");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
|
|
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.");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Padding:", ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Scrolling:", ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
|
|
ImGui::SameLine();
|
|
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
|
|
ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
|
|
ImGui::SameLine();
|
|
ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
|
|
ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Sorting:", ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"When sorting is enabled: hold shift when clicking headers to sort on multiple column. "
|
|
"TableGetSortSpecs() may return specs where (SpecsCount > 1).");
|
|
ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may "
|
|
"return specs where (SpecsCount == 0).");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
ImGui::Checkbox("show_headers", &show_headers);
|
|
ImGui::Checkbox("show_wrapped_text", &show_wrapped_text);
|
|
|
|
ImGui::DragFloat2("##OuterSize", &outer_size_value.x);
|
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
|
ImGui::Checkbox("outer_size", &outer_size_enabled);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"If scrolling is disabled (ScrollX and ScrollY not set):\n"
|
|
"- The table is output directly in the parent window.\n"
|
|
"- OuterSize.x < 0.0f will right-align the table.\n"
|
|
"- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch column.\n"
|
|
"- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there "
|
|
"are more rows (unless NoHostExtendY is set).");
|
|
|
|
// From a user point of view we will tend to use 'inner_width' differently depending on whether our
|
|
// table is embedding scrolling. To facilitate toying with this demo we will actually pass 0.0f to the
|
|
// BeginTable() when ScrollX is disabled.
|
|
ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);
|
|
|
|
ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX);
|
|
ImGui::SameLine();
|
|
HelpMarker("Specify height of the Selectable item.");
|
|
|
|
ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999);
|
|
ImGui::Combo("items_type (first column)", &contents_type, contents_type_names,
|
|
IM_ARRAYSIZE(contents_type_names));
|
|
// filter.Draw("filter");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
ImGui::PopItemWidth();
|
|
PopStyleCompact();
|
|
ImGui::Spacing();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Update item list if we changed the number of items
|
|
static ImVector<MyItem> items;
|
|
static ImVector<int> selection;
|
|
static bool items_need_sort = false;
|
|
if (items.Size != items_count)
|
|
{
|
|
items.resize(items_count, MyItem());
|
|
for (int n = 0; n < items_count; n++)
|
|
{
|
|
const int template_n = n % IM_ARRAYSIZE(template_items_names);
|
|
MyItem& item = items[n];
|
|
item.ID = n;
|
|
item.Name = template_items_names[template_n];
|
|
item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities
|
|
}
|
|
}
|
|
|
|
const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();
|
|
const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;
|
|
ImVec2 table_scroll_cur, table_scroll_max; // For debug display
|
|
const ImDrawList* table_draw_list = NULL; // "
|
|
|
|
// Submit table
|
|
const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;
|
|
if (ImGui::BeginTable("table_advanced", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0),
|
|
inner_width_to_use))
|
|
{
|
|
// Declare columns
|
|
// We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort
|
|
// specifications. This is so our sort function can identify a column given our own identifier. We could
|
|
// also identify them based on their index!
|
|
ImGui::TableSetupColumn(
|
|
"ID",
|
|
ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide,
|
|
0.0f, MyItemColumnID_ID);
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
|
|
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f,
|
|
MyItemColumnID_Action);
|
|
ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending, 0.0f,
|
|
MyItemColumnID_Quantity);
|
|
ImGui::TableSetupColumn("Description",
|
|
(flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch,
|
|
0.0f, MyItemColumnID_Description);
|
|
ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);
|
|
ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
|
|
|
|
// Sort our data if sort specs have been changed!
|
|
ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs();
|
|
if (sorts_specs && sorts_specs->SpecsDirty)
|
|
items_need_sort = true;
|
|
if (sorts_specs && items_need_sort && items.Size > 1)
|
|
{
|
|
MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function.
|
|
qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs);
|
|
MyItem::s_current_sort_specs = NULL;
|
|
sorts_specs->SpecsDirty = false;
|
|
}
|
|
items_need_sort = false;
|
|
|
|
// Take note of whether we are currently sorting based on the Quantity field,
|
|
// we will use this to trigger sorting when we know the data of this column has been modified.
|
|
const bool sorts_specs_using_quantity =
|
|
(ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0;
|
|
|
|
// Show headers
|
|
if (show_headers)
|
|
ImGui::TableHeadersRow();
|
|
|
|
// Show data
|
|
// FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?
|
|
ImGui::PushButtonRepeat(true);
|
|
# if 1
|
|
// Demonstrate using clipper for large vertical lists
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin(items.Size);
|
|
while (clipper.Step())
|
|
{
|
|
for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
|
|
# else
|
|
// Without clipper
|
|
{
|
|
for (int row_n = 0; row_n < items.Size; row_n++)
|
|
# endif
|
|
{
|
|
MyItem* item = &items[row_n];
|
|
// if (!filter.PassFilter(item->Name))
|
|
// continue;
|
|
|
|
const bool item_is_selected = selection.contains(item->ID);
|
|
ImGui::PushID(item->ID);
|
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height);
|
|
|
|
// For the demo purpose we can select among different type of items submitted in the first column
|
|
ImGui::TableSetColumnIndex(0);
|
|
char label[32];
|
|
sprintf(label, "%04d", item->ID);
|
|
if (contents_type == CT_Text)
|
|
ImGui::TextUnformatted(label);
|
|
else if (contents_type == CT_Button)
|
|
ImGui::Button(label);
|
|
else if (contents_type == CT_SmallButton)
|
|
ImGui::SmallButton(label);
|
|
else if (contents_type == CT_FillButton)
|
|
ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));
|
|
else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)
|
|
{
|
|
ImGuiSelectableFlags selectable_flags =
|
|
(contents_type == CT_SelectableSpanRow)
|
|
? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap
|
|
: ImGuiSelectableFlags_None;
|
|
if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height)))
|
|
{
|
|
if (ImGui::GetIO().KeyCtrl)
|
|
{
|
|
if (item_is_selected)
|
|
selection.find_erase_unsorted(item->ID);
|
|
else
|
|
selection.push_back(item->ID);
|
|
}
|
|
else
|
|
{
|
|
selection.clear();
|
|
selection.push_back(item->ID);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImGui::TableSetColumnIndex(1))
|
|
ImGui::TextUnformatted(item->Name);
|
|
|
|
// Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
|
|
// and we are currently sorting on the column showing the Quantity.
|
|
// To avoid triggering a sort while holding the button, we only trigger it when the button has been
|
|
// released. You will probably need a more advanced system in your code if you want to automatically
|
|
// sort when a specific entry changes.
|
|
if (ImGui::TableSetColumnIndex(2))
|
|
{
|
|
if (ImGui::SmallButton("Chop"))
|
|
{
|
|
item->Quantity += 1;
|
|
}
|
|
if (sorts_specs_using_quantity && ImGui::IsItemDeactivated())
|
|
{
|
|
items_need_sort = true;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::SmallButton("Eat"))
|
|
{
|
|
item->Quantity -= 1;
|
|
}
|
|
if (sorts_specs_using_quantity && ImGui::IsItemDeactivated())
|
|
{
|
|
items_need_sort = true;
|
|
}
|
|
}
|
|
|
|
if (ImGui::TableSetColumnIndex(3))
|
|
ImGui::Text("%d", item->Quantity);
|
|
|
|
ImGui::TableSetColumnIndex(4);
|
|
if (show_wrapped_text)
|
|
ImGui::TextWrapped("Lorem ipsum dolor sit amet");
|
|
else
|
|
ImGui::Text("Lorem ipsum dolor sit amet");
|
|
|
|
if (ImGui::TableSetColumnIndex(5))
|
|
ImGui::Text("1234");
|
|
|
|
ImGui::PopID();
|
|
}
|
|
}
|
|
ImGui::PopButtonRepeat();
|
|
|
|
// Store some info to display debug details below
|
|
table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());
|
|
table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());
|
|
table_draw_list = ImGui::GetWindowDrawList();
|
|
ImGui::EndTable();
|
|
}
|
|
static bool show_debug_details = false;
|
|
ImGui::Checkbox("Debug details", &show_debug_details);
|
|
if (show_debug_details && table_draw_list)
|
|
{
|
|
ImGui::SameLine(0.0f, 0.0f);
|
|
const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;
|
|
if (table_draw_list == parent_draw_list)
|
|
ImGui::Text(": DrawCmd: +%d (in same window)",
|
|
table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);
|
|
else
|
|
ImGui::Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)",
|
|
table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x,
|
|
table_scroll_cur.y, table_scroll_max.y);
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
ImGui::PopID();
|
|
|
|
ShowDemoWindowColumns();
|
|
|
|
if (disable_indent)
|
|
ImGui::PopStyleVar();
|
|
}
|
|
|
|
// Demonstrate old/legacy Columns API!
|
|
// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]
|
|
static void ShowDemoWindowColumns()
|
|
{
|
|
bool open = ImGui::TreeNode("Legacy Columns API");
|
|
ImGui::SameLine();
|
|
HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!");
|
|
if (!open)
|
|
return;
|
|
|
|
// Basic columns
|
|
if (ImGui::TreeNode("Basic"))
|
|
{
|
|
ImGui::Text("Without border:");
|
|
ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border
|
|
ImGui::Separator();
|
|
for (int n = 0; n < 14; n++)
|
|
{
|
|
char label[32];
|
|
sprintf(label, "Item %d", n);
|
|
if (ImGui::Selectable(label))
|
|
{}
|
|
// if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}
|
|
ImGui::NextColumn();
|
|
}
|
|
ImGui::Columns(1);
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("With border:");
|
|
ImGui::Columns(4, "mycolumns"); // 4-ways, with border
|
|
ImGui::Separator();
|
|
ImGui::Text("ID");
|
|
ImGui::NextColumn();
|
|
ImGui::Text("Name");
|
|
ImGui::NextColumn();
|
|
ImGui::Text("Path");
|
|
ImGui::NextColumn();
|
|
ImGui::Text("Hovered");
|
|
ImGui::NextColumn();
|
|
ImGui::Separator();
|
|
const char* names[3] = {"One", "Two", "Three"};
|
|
const char* paths[3] = {"/path/one", "/path/two", "/path/three"};
|
|
static int selected = -1;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
char label[32];
|
|
sprintf(label, "%04d", i);
|
|
if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))
|
|
selected = i;
|
|
bool hovered = ImGui::IsItemHovered();
|
|
ImGui::NextColumn();
|
|
ImGui::Text(names[i]);
|
|
ImGui::NextColumn();
|
|
ImGui::Text(paths[i]);
|
|
ImGui::NextColumn();
|
|
ImGui::Text("%d", hovered);
|
|
ImGui::NextColumn();
|
|
}
|
|
ImGui::Columns(1);
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Borders"))
|
|
{
|
|
// NB: Future columns API should allow automatic horizontal borders.
|
|
static bool h_borders = true;
|
|
static bool v_borders = true;
|
|
static int columns_count = 4;
|
|
const int lines_count = 3;
|
|
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
|
ImGui::DragInt("##columns_count", &columns_count, 0.1f, 2, 10, "%d columns");
|
|
if (columns_count < 2)
|
|
columns_count = 2;
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("horizontal", &h_borders);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("vertical", &v_borders);
|
|
ImGui::Columns(columns_count, NULL, v_borders);
|
|
for (int i = 0; i < columns_count * lines_count; i++)
|
|
{
|
|
if (h_borders && ImGui::GetColumnIndex() == 0)
|
|
ImGui::Separator();
|
|
ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i);
|
|
ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
|
|
ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
|
|
ImGui::Text("Offset %.2f", ImGui::GetColumnOffset());
|
|
ImGui::Text("Long text that is likely to clip");
|
|
ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f));
|
|
ImGui::NextColumn();
|
|
}
|
|
ImGui::Columns(1);
|
|
if (h_borders)
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Create multiple items in a same cell before switching to next column
|
|
if (ImGui::TreeNode("Mixed items"))
|
|
{
|
|
ImGui::Columns(3, "mixed");
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Hello");
|
|
ImGui::Button("Banana");
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("ImGui");
|
|
ImGui::Button("Apple");
|
|
static float foo = 1.0f;
|
|
ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f");
|
|
ImGui::Text("An extra line here.");
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("Sailor");
|
|
ImGui::Button("Corniflower");
|
|
static float bar = 1.0f;
|
|
ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f");
|
|
ImGui::NextColumn();
|
|
|
|
if (ImGui::CollapsingHeader("Category A"))
|
|
{
|
|
ImGui::Text("Blah blah blah");
|
|
}
|
|
ImGui::NextColumn();
|
|
if (ImGui::CollapsingHeader("Category B"))
|
|
{
|
|
ImGui::Text("Blah blah blah");
|
|
}
|
|
ImGui::NextColumn();
|
|
if (ImGui::CollapsingHeader("Category C"))
|
|
{
|
|
ImGui::Text("Blah blah blah");
|
|
}
|
|
ImGui::NextColumn();
|
|
ImGui::Columns(1);
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Word wrapping
|
|
if (ImGui::TreeNode("Word-wrapping"))
|
|
{
|
|
ImGui::Columns(2, "word-wrapping");
|
|
ImGui::Separator();
|
|
ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
|
|
ImGui::TextWrapped("Hello Left");
|
|
ImGui::NextColumn();
|
|
ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
|
|
ImGui::TextWrapped("Hello Right");
|
|
ImGui::Columns(1);
|
|
ImGui::Separator();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Horizontal Scrolling"))
|
|
{
|
|
ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));
|
|
ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);
|
|
ImGui::BeginChild("##ScrollingRegion", child_size, false, ImGuiWindowFlags_HorizontalScrollbar);
|
|
ImGui::Columns(10);
|
|
|
|
// Also demonstrate using clipper for large vertical lists
|
|
int ITEMS_COUNT = 2000;
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin(ITEMS_COUNT);
|
|
while (clipper.Step())
|
|
{
|
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
|
for (int j = 0; j < 10; j++)
|
|
{
|
|
ImGui::Text("Line %d Column %d...", i, j);
|
|
ImGui::NextColumn();
|
|
}
|
|
}
|
|
ImGui::Columns(1);
|
|
ImGui::EndChild();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Tree"))
|
|
{
|
|
ImGui::Columns(2, "tree", true);
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x);
|
|
ImGui::NextColumn();
|
|
ImGui::Text("Node contents");
|
|
ImGui::NextColumn();
|
|
if (open1)
|
|
{
|
|
for (int y = 0; y < 3; y++)
|
|
{
|
|
bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y);
|
|
ImGui::NextColumn();
|
|
ImGui::Text("Node contents");
|
|
if (open2)
|
|
{
|
|
ImGui::Text("Even more contents");
|
|
if (ImGui::TreeNode("Tree in column"))
|
|
{
|
|
ImGui::Text("The quick brown fox jumps over the lazy dog");
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
ImGui::NextColumn();
|
|
if (open2)
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
ImGui::Columns(1);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
static void ShowDemoWindowMisc()
|
|
{
|
|
if (ImGui::CollapsingHeader("Filtering"))
|
|
{
|
|
// Helper class to easy setup a text filter.
|
|
// You may want to implement a more feature-full filtering scheme in your own application.
|
|
static ImGuiTextFilter filter;
|
|
ImGui::Text(
|
|
"Filter usage:\n"
|
|
" \"\" display all lines\n"
|
|
" \"xxx\" display lines containing \"xxx\"\n"
|
|
" \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n"
|
|
" \"-xxx\" hide lines containing \"xxx\"");
|
|
filter.Draw();
|
|
const char* lines[] = {"aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp",
|
|
"bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world"};
|
|
for (int i = 0; i < IM_ARRAYSIZE(lines); i++)
|
|
if (filter.PassFilter(lines[i]))
|
|
ImGui::BulletText("%s", lines[i]);
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Inputs, Navigation & Focus"))
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
// Display ImGuiIO output flags
|
|
ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse);
|
|
ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
|
|
ImGui::Text("WantTextInput: %d", io.WantTextInput);
|
|
ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos);
|
|
ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible);
|
|
|
|
// Display Mouse state
|
|
if (ImGui::TreeNode("Mouse State"))
|
|
{
|
|
if (ImGui::IsMousePosValid())
|
|
ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
|
|
else
|
|
ImGui::Text("Mouse pos: <INVALID>");
|
|
ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
|
|
ImGui::Text("Mouse down:");
|
|
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]);
|
|
}
|
|
ImGui::Text("Mouse clicked:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
|
|
if (ImGui::IsMouseClicked(i))
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("b%d", i);
|
|
}
|
|
ImGui::Text("Mouse dblclick:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
|
|
if (ImGui::IsMouseDoubleClicked(i))
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("b%d", i);
|
|
}
|
|
ImGui::Text("Mouse released:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
|
|
if (ImGui::IsMouseReleased(i))
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("b%d", i);
|
|
}
|
|
ImGui::Text("Mouse wheel: %.1f", io.MouseWheel);
|
|
ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Display Keyboard/Mouse state
|
|
if (ImGui::TreeNode("Keyboard & Navigation State"))
|
|
{
|
|
ImGui::Text("Keys down:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++)
|
|
if (ImGui::IsKeyDown(i))
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]);
|
|
}
|
|
ImGui::Text("Keys pressed:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++)
|
|
if (ImGui::IsKeyPressed(i))
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("%d (0x%X)", i, i);
|
|
}
|
|
ImGui::Text("Keys release:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++)
|
|
if (ImGui::IsKeyReleased(i))
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("%d (0x%X)", i, i);
|
|
}
|
|
ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "",
|
|
io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
|
|
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.
|
|
|
|
ImGui::Text("NavInputs down:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
|
|
if (io.NavInputs[i] > 0.0f)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]);
|
|
}
|
|
ImGui::Text("NavInputs pressed:");
|
|
for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
|
|
if (io.NavInputsDownDuration[i] == 0.0f)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("[%d]", i);
|
|
}
|
|
|
|
ImGui::Button("Hovering me sets the\nkeyboard capture flag");
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::CaptureKeyboardFromApp(true);
|
|
ImGui::SameLine();
|
|
ImGui::Button("Holding me clears the\nthe keyboard capture flag");
|
|
if (ImGui::IsItemActive())
|
|
ImGui::CaptureKeyboardFromApp(false);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Tabbing"))
|
|
{
|
|
ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields.");
|
|
static char buf[32] = "hello";
|
|
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
|
|
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
|
|
ImGui::InputText("3", buf, IM_ARRAYSIZE(buf));
|
|
ImGui::PushAllowKeyboardFocus(false);
|
|
ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf));
|
|
ImGui::SameLine();
|
|
HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
|
|
ImGui::PopAllowKeyboardFocus();
|
|
ImGui::InputText("5", buf, IM_ARRAYSIZE(buf));
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Focus from code"))
|
|
{
|
|
bool focus_1 = ImGui::Button("Focus on 1");
|
|
ImGui::SameLine();
|
|
bool focus_2 = ImGui::Button("Focus on 2");
|
|
ImGui::SameLine();
|
|
bool focus_3 = ImGui::Button("Focus on 3");
|
|
int has_focus = 0;
|
|
static char buf[128] = "click on a button to set focus";
|
|
|
|
if (focus_1)
|
|
ImGui::SetKeyboardFocusHere();
|
|
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
|
|
if (ImGui::IsItemActive())
|
|
has_focus = 1;
|
|
|
|
if (focus_2)
|
|
ImGui::SetKeyboardFocusHere();
|
|
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
|
|
if (ImGui::IsItemActive())
|
|
has_focus = 2;
|
|
|
|
ImGui::PushAllowKeyboardFocus(false);
|
|
if (focus_3)
|
|
ImGui::SetKeyboardFocusHere();
|
|
ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf));
|
|
if (ImGui::IsItemActive())
|
|
has_focus = 3;
|
|
ImGui::SameLine();
|
|
HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
|
|
ImGui::PopAllowKeyboardFocus();
|
|
|
|
if (has_focus)
|
|
ImGui::Text("Item with focus: %d", has_focus);
|
|
else
|
|
ImGui::Text("Item with focus: <none>");
|
|
|
|
// Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item
|
|
static float f3[3] = {0.0f, 0.0f, 0.0f};
|
|
int focus_ahead = -1;
|
|
if (ImGui::Button("Focus on X"))
|
|
{
|
|
focus_ahead = 0;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Focus on Y"))
|
|
{
|
|
focus_ahead = 1;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Focus on Z"))
|
|
{
|
|
focus_ahead = 2;
|
|
}
|
|
if (focus_ahead != -1)
|
|
ImGui::SetKeyboardFocusHere(focus_ahead);
|
|
ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f);
|
|
|
|
ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code.");
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Dragging"))
|
|
{
|
|
ImGui::TextWrapped(
|
|
"You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");
|
|
for (int button = 0; button < 3; button++)
|
|
{
|
|
ImGui::Text("IsMouseDragging(%d):", button);
|
|
ImGui::Text(" w/ default threshold: %d,", ImGui::IsMouseDragging(button));
|
|
ImGui::Text(" w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f));
|
|
ImGui::Text(" w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f));
|
|
}
|
|
|
|
ImGui::Button("Drag Me");
|
|
if (ImGui::IsItemActive())
|
|
ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos,
|
|
ImGui::GetColorU32(ImGuiCol_Button),
|
|
4.0f); // Draw a line between the button and the mouse cursor
|
|
|
|
// Drag operations gets "unlocked" when the mouse has moved past a certain threshold
|
|
// (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
|
|
// threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
|
|
ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);
|
|
ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0);
|
|
ImVec2 mouse_delta = io.MouseDelta;
|
|
ImGui::Text("GetMouseDragDelta(0):");
|
|
ImGui::Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x,
|
|
value_with_lock_threshold.y);
|
|
ImGui::Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y);
|
|
ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y);
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
if (ImGui::TreeNode("Mouse cursors"))
|
|
{
|
|
const char* mouse_cursors_names[] = {"Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW",
|
|
"ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed"};
|
|
IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
|
|
|
|
ImGuiMouseCursor current = ImGui::GetMouseCursor();
|
|
ImGui::Text("Current mouse cursor = %d: %s", current, mouse_cursors_names[current]);
|
|
ImGui::Text("Hover to see mouse cursors:");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. "
|
|
"If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, "
|
|
"otherwise your backend needs to handle it.");
|
|
for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)
|
|
{
|
|
char label[32];
|
|
sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]);
|
|
ImGui::Bullet();
|
|
ImGui::Selectable(label, false);
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetMouseCursor(i);
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] About Window / ShowAboutWindow()
|
|
// Access from Dear ImGui Demo -> Tools -> About
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void ImGui::ShowAboutWindow(bool* p_open)
|
|
{
|
|
if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
|
|
ImGui::Separator();
|
|
ImGui::Text("By Omar Cornut and all Dear ImGui contributors.");
|
|
ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information.");
|
|
|
|
static bool show_config_info = false;
|
|
ImGui::Checkbox("Config/Build Information", &show_config_info);
|
|
if (show_config_info)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
|
|
bool copy_to_clipboard = ImGui::Button("Copy to clipboard");
|
|
ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);
|
|
ImGui::BeginChildFrame(ImGui::GetID("cfg_infos"), child_size, ImGuiWindowFlags_NoMove);
|
|
if (copy_to_clipboard)
|
|
{
|
|
ImGui::LogToClipboard();
|
|
ImGui::LogText("```\n"); // Back quotes will make text appears without formatting when pasting on GitHub
|
|
}
|
|
|
|
ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
|
|
ImGui::Separator();
|
|
ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t),
|
|
(int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));
|
|
ImGui::Text("define: __cplusplus=%d", (int)__cplusplus);
|
|
# ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_WIN32_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_FILE_FUNCTIONS
|
|
ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS");
|
|
# endif
|
|
# ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS
|
|
ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS");
|
|
# endif
|
|
# ifdef IMGUI_USE_BGRA_PACKED_COLOR
|
|
ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR");
|
|
# endif
|
|
# ifdef _WIN32
|
|
ImGui::Text("define: _WIN32");
|
|
# endif
|
|
# ifdef _WIN64
|
|
ImGui::Text("define: _WIN64");
|
|
# endif
|
|
# ifdef __linux__
|
|
ImGui::Text("define: __linux__");
|
|
# endif
|
|
# ifdef __APPLE__
|
|
ImGui::Text("define: __APPLE__");
|
|
# endif
|
|
# ifdef _MSC_VER
|
|
ImGui::Text("define: _MSC_VER=%d", _MSC_VER);
|
|
# endif
|
|
# ifdef _MSVC_LANG
|
|
ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG);
|
|
# endif
|
|
# ifdef __MINGW32__
|
|
ImGui::Text("define: __MINGW32__");
|
|
# endif
|
|
# ifdef __MINGW64__
|
|
ImGui::Text("define: __MINGW64__");
|
|
# endif
|
|
# ifdef __GNUC__
|
|
ImGui::Text("define: __GNUC__=%d", (int)__GNUC__);
|
|
# endif
|
|
# ifdef __clang_version__
|
|
ImGui::Text("define: __clang_version__=%s", __clang_version__);
|
|
# endif
|
|
# ifdef IMGUI_HAS_VIEWPORT
|
|
ImGui::Text("define: IMGUI_HAS_VIEWPORT");
|
|
# endif
|
|
# ifdef IMGUI_HAS_DOCK
|
|
ImGui::Text("define: IMGUI_HAS_DOCK");
|
|
# endif
|
|
ImGui::Separator();
|
|
ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
|
|
ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
|
|
ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags);
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
|
|
ImGui::Text(" NavEnableKeyboard");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)
|
|
ImGui::Text(" NavEnableGamepad");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
|
|
ImGui::Text(" NavEnableSetMousePos");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
|
|
ImGui::Text(" NavNoCaptureKeyboard");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
|
|
ImGui::Text(" NoMouse");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
|
ImGui::Text(" NoMouseCursorChange");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
|
ImGui::Text(" DockingEnable");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
|
ImGui::Text(" ViewportsEnable");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
|
|
ImGui::Text(" DpiEnableScaleViewports");
|
|
if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts)
|
|
ImGui::Text(" DpiEnableScaleFonts");
|
|
if (io.MouseDrawCursor)
|
|
ImGui::Text("io.MouseDrawCursor");
|
|
if (io.ConfigViewportsNoAutoMerge)
|
|
ImGui::Text("io.ConfigViewportsNoAutoMerge");
|
|
if (io.ConfigViewportsNoTaskBarIcon)
|
|
ImGui::Text("io.ConfigViewportsNoTaskBarIcon");
|
|
if (io.ConfigViewportsNoDecoration)
|
|
ImGui::Text("io.ConfigViewportsNoDecoration");
|
|
if (io.ConfigViewportsNoDefaultParent)
|
|
ImGui::Text("io.ConfigViewportsNoDefaultParent");
|
|
if (io.ConfigDockingNoSplit)
|
|
ImGui::Text("io.ConfigDockingNoSplit");
|
|
if (io.ConfigDockingAlwaysTabBar)
|
|
ImGui::Text("io.ConfigDockingAlwaysTabBar");
|
|
if (io.ConfigDockingTransparentPayload)
|
|
ImGui::Text("io.ConfigDockingTransparentPayload");
|
|
if (io.ConfigMacOSXBehaviors)
|
|
ImGui::Text("io.ConfigMacOSXBehaviors");
|
|
if (io.ConfigInputTextCursorBlink)
|
|
ImGui::Text("io.ConfigInputTextCursorBlink");
|
|
if (io.ConfigWindowsResizeFromEdges)
|
|
ImGui::Text("io.ConfigWindowsResizeFromEdges");
|
|
if (io.ConfigWindowsMoveFromTitleBarOnly)
|
|
ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly");
|
|
if (io.ConfigMemoryCompactTimer >= 0.0f)
|
|
ImGui::Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer);
|
|
ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags);
|
|
if (io.BackendFlags & ImGuiBackendFlags_HasGamepad)
|
|
ImGui::Text(" HasGamepad");
|
|
if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors)
|
|
ImGui::Text(" HasMouseCursors");
|
|
if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)
|
|
ImGui::Text(" HasSetMousePos");
|
|
if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)
|
|
ImGui::Text(" PlatformHasViewports");
|
|
if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
|
|
ImGui::Text(" HasMouseHoveredViewport");
|
|
if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
|
|
ImGui::Text(" RendererHasVtxOffset");
|
|
if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports)
|
|
ImGui::Text(" RendererHasViewports");
|
|
ImGui::Separator();
|
|
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);
|
|
ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
|
|
ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x,
|
|
io.DisplayFramebufferScale.y);
|
|
ImGui::Separator();
|
|
ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y);
|
|
ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize);
|
|
ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y);
|
|
ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding);
|
|
ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize);
|
|
ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y);
|
|
ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);
|
|
|
|
if (copy_to_clipboard)
|
|
{
|
|
ImGui::LogText("\n```\n");
|
|
ImGui::LogFinish();
|
|
}
|
|
ImGui::EndChildFrame();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Style Editor / ShowStyleEditor()
|
|
//-----------------------------------------------------------------------------
|
|
// - ShowFontSelector()
|
|
// - ShowStyleSelector()
|
|
// - ShowStyleEditor()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Forward declare ShowFontAtlas() which isn't worth putting in public API yet
|
|
namespace ImGui
|
|
{
|
|
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
|
|
}
|
|
|
|
// Demo helper function to select among loaded fonts.
|
|
// Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one.
|
|
void ImGui::ShowFontSelector(const char* label)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImFont* font_current = ImGui::GetFont();
|
|
if (ImGui::BeginCombo(label, font_current->GetDebugName()))
|
|
{
|
|
for (int n = 0; n < io.Fonts->Fonts.Size; n++)
|
|
{
|
|
ImFont* font = io.Fonts->Fonts[n];
|
|
ImGui::PushID((void*)font);
|
|
if (ImGui::Selectable(font->GetDebugName(), font == font_current))
|
|
io.FontDefault = font;
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n"
|
|
"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
|
|
"- Read FAQ and docs/FONTS.md for more details.\n"
|
|
"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
|
|
}
|
|
|
|
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
|
|
// Here we use the simplified Combo() api that packs items into a single literal string.
|
|
// Useful for quick combo boxes where the choices are known locally.
|
|
bool ImGui::ShowStyleSelector(const char* label)
|
|
{
|
|
static int style_idx = -1;
|
|
if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0"))
|
|
{
|
|
switch (style_idx)
|
|
{
|
|
case 0:
|
|
ImGui::StyleColorsDark();
|
|
break;
|
|
case 1:
|
|
ImGui::StyleColorsLight();
|
|
break;
|
|
case 2:
|
|
ImGui::StyleColorsClassic();
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ImGui::ShowStyleEditor(ImGuiStyle* ref)
|
|
{
|
|
// You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
|
|
// (without a reference style pointer, we will use one compared locally as a reference)
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
static ImGuiStyle ref_saved_style;
|
|
|
|
// Default to using internal storage as reference
|
|
static bool init = true;
|
|
if (init && ref == NULL)
|
|
ref_saved_style = style;
|
|
init = false;
|
|
if (ref == NULL)
|
|
ref = &ref_saved_style;
|
|
|
|
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
|
|
|
|
if (ImGui::ShowStyleSelector("Colors##Selector"))
|
|
ref_saved_style = style;
|
|
ImGui::ShowFontSelector("Fonts##Selector");
|
|
|
|
// Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
|
|
if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
|
|
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
|
|
{
|
|
bool border = (style.WindowBorderSize > 0.0f);
|
|
if (ImGui::Checkbox("WindowBorder", &border))
|
|
{
|
|
style.WindowBorderSize = border ? 1.0f : 0.0f;
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
{
|
|
bool border = (style.FrameBorderSize > 0.0f);
|
|
if (ImGui::Checkbox("FrameBorder", &border))
|
|
{
|
|
style.FrameBorderSize = border ? 1.0f : 0.0f;
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
{
|
|
bool border = (style.PopupBorderSize > 0.0f);
|
|
if (ImGui::Checkbox("PopupBorder", &border))
|
|
{
|
|
style.PopupBorderSize = border ? 1.0f : 0.0f;
|
|
}
|
|
}
|
|
|
|
// Save/Revert button
|
|
if (ImGui::Button("Save Ref"))
|
|
*ref = ref_saved_style = style;
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Revert Ref"))
|
|
style = *ref;
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
|
|
"Use \"Export\" below to save them somewhere.");
|
|
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None))
|
|
{
|
|
if (ImGui::BeginTabItem("Sizes"))
|
|
{
|
|
ImGui::Text("Main");
|
|
ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
|
|
ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
|
|
ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
|
|
ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
|
|
ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
|
|
ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
|
|
ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
|
|
ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
|
|
ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
|
|
ImGui::Text("Borders");
|
|
ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
|
|
ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
|
|
ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
|
|
ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
|
|
ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
|
|
ImGui::Text("Rounding");
|
|
ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
|
|
ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
|
|
ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
|
|
ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
|
|
ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
|
|
ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
|
|
ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
|
|
ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
|
|
ImGui::Text("Alignment");
|
|
ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
|
|
int window_menu_button_position = style.WindowMenuButtonPosition + 1;
|
|
if (ImGui::Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0"))
|
|
style.WindowMenuButtonPosition = window_menu_button_position - 1;
|
|
ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
|
|
ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
|
|
ImGui::SameLine();
|
|
HelpMarker("Alignment applies when a button is larger than its text content.");
|
|
ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
|
|
ImGui::SameLine();
|
|
HelpMarker("Alignment applies when a selectable is larger than its text content.");
|
|
ImGui::Text("Safe Area Padding");
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been "
|
|
"configured).");
|
|
ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f");
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Colors"))
|
|
{
|
|
static int output_dest = 0;
|
|
static bool output_only_modified = true;
|
|
if (ImGui::Button("Export"))
|
|
{
|
|
if (output_dest == 0)
|
|
ImGui::LogToClipboard();
|
|
else
|
|
ImGui::LogToTTY();
|
|
ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE);
|
|
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
|
{
|
|
const ImVec4& col = style.Colors[i];
|
|
const char* name = ImGui::GetStyleColorName(i);
|
|
if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
|
|
ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name,
|
|
23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
|
|
}
|
|
ImGui::LogFinish();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::SetNextItemWidth(120);
|
|
ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Only Modified Colors", &output_only_modified);
|
|
|
|
static ImGuiTextFilter filter;
|
|
filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
|
|
|
|
static ImGuiColorEditFlags alpha_flags = 0;
|
|
if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None))
|
|
{
|
|
alpha_flags = ImGuiColorEditFlags_None;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview))
|
|
{
|
|
alpha_flags = ImGuiColorEditFlags_AlphaPreview;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf))
|
|
{
|
|
alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;
|
|
}
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"In the color list:\n"
|
|
"Left-click on color square to open color picker,\n"
|
|
"Right-click to open edit options menu.");
|
|
|
|
ImGui::BeginChild("##colors", ImVec2(0, 0), true,
|
|
ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar |
|
|
ImGuiWindowFlags_NavFlattened);
|
|
ImGui::PushItemWidth(-160);
|
|
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
|
{
|
|
const char* name = ImGui::GetStyleColorName(i);
|
|
if (!filter.PassFilter(name))
|
|
continue;
|
|
ImGui::PushID(i);
|
|
ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);
|
|
if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)
|
|
{
|
|
// Tips: in a real user application, you may want to merge and use an icon font into the main font,
|
|
// so instead of "Save"/"Revert" you'd use icons!
|
|
// Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
|
|
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
|
if (ImGui::Button("Save"))
|
|
{
|
|
ref->Colors[i] = style.Colors[i];
|
|
}
|
|
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
|
if (ImGui::Button("Revert"))
|
|
{
|
|
style.Colors[i] = ref->Colors[i];
|
|
}
|
|
}
|
|
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
|
ImGui::TextUnformatted(name);
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndChild();
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Fonts"))
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImFontAtlas* atlas = io.Fonts;
|
|
HelpMarker("Read FAQ and docs/FONTS.md for details on font loading.");
|
|
ImGui::ShowFontAtlas(atlas);
|
|
|
|
// Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
|
|
// (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out
|
|
// of bounds).
|
|
const float MIN_SCALE = 0.3f;
|
|
const float MAX_SCALE = 2.0f;
|
|
HelpMarker(
|
|
"Those are old settings provided for convenience.\n"
|
|
"However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
|
|
"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
|
|
"Using those settings here will give you poor quality results.");
|
|
static float window_scale = 1.0f;
|
|
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
|
|
if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f",
|
|
ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
|
|
ImGui::SetWindowFontScale(window_scale);
|
|
ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f",
|
|
ImGuiSliderFlags_AlwaysClamp); // Scale everything
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Rendering"))
|
|
{
|
|
ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
|
|
|
|
ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest "
|
|
"filtering).");
|
|
|
|
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
|
|
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
|
|
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
|
|
if (style.CurveTessellationTol < 0.10f)
|
|
style.CurveTessellationTol = 0.10f;
|
|
|
|
// When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated
|
|
// circles.
|
|
ImGui::DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError, 0.005f, 0.10f, 5.0f,
|
|
"%.2f", ImGuiSliderFlags_AlwaysClamp);
|
|
if (ImGui::IsItemActive())
|
|
{
|
|
ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
|
|
ImGui::BeginTooltip();
|
|
ImGui::TextUnformatted("(R = radius, N = number of segments)");
|
|
ImGui::Spacing();
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
const float min_widget_width = ImGui::CalcTextSize("N: MMM\nR: MMM").x;
|
|
for (int n = 0; n < 8; n++)
|
|
{
|
|
const float RAD_MIN = 5.0f;
|
|
const float RAD_MAX = 70.0f;
|
|
const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
|
|
|
|
ImGui::BeginGroup();
|
|
|
|
ImGui::Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad));
|
|
|
|
const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
|
|
const float offset_x = floorf(canvas_width * 0.5f);
|
|
const float offset_y = floorf(RAD_MAX);
|
|
|
|
const ImVec2 p1 = ImGui::GetCursorScreenPos();
|
|
draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad,
|
|
ImGui::GetColorU32(ImGuiCol_Text));
|
|
ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
|
|
|
|
/*
|
|
const ImVec2 p2 = ImGui::GetCursorScreenPos();
|
|
draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad,
|
|
ImGui::GetColorU32(ImGuiCol_Text)); ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
|
|
*/
|
|
|
|
ImGui::EndGroup();
|
|
ImGui::SameLine();
|
|
}
|
|
ImGui::EndTooltip();
|
|
}
|
|
ImGui::SameLine();
|
|
HelpMarker(
|
|
"When drawing circle primitives with \"num_segments == 0\" tesselation will be calculated "
|
|
"automatically.");
|
|
|
|
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.
|
|
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).");
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
|
|
ImGui::PopItemWidth();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
|
|
//-----------------------------------------------------------------------------
|
|
// - ShowExampleAppMainMenuBar()
|
|
// - ShowExampleMenuFile()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate creating a "main" fullscreen menu bar and populating it.
|
|
// Note the difference between BeginMainMenuBar() and BeginMenuBar():
|
|
// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)
|
|
// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar()
|
|
// into it.
|
|
static void ShowExampleAppMainMenuBar()
|
|
{
|
|
if (ImGui::BeginMainMenuBar())
|
|
{
|
|
if (ImGui::BeginMenu("File"))
|
|
{
|
|
ShowExampleMenuFile();
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Edit"))
|
|
{
|
|
if (ImGui::MenuItem("Undo", "CTRL+Z"))
|
|
{}
|
|
if (ImGui::MenuItem("Redo", "CTRL+Y", false, false))
|
|
{} // Disabled item
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Cut", "CTRL+X"))
|
|
{}
|
|
if (ImGui::MenuItem("Copy", "CTRL+C"))
|
|
{}
|
|
if (ImGui::MenuItem("Paste", "CTRL+V"))
|
|
{}
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMainMenuBar();
|
|
}
|
|
}
|
|
|
|
// Note that shortcuts are currently provided for display only
|
|
// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
|
|
static void ShowExampleMenuFile()
|
|
{
|
|
ImGui::MenuItem("(demo menu)", NULL, false, false);
|
|
if (ImGui::MenuItem("New"))
|
|
{}
|
|
if (ImGui::MenuItem("Open", "Ctrl+O"))
|
|
{}
|
|
if (ImGui::BeginMenu("Open Recent"))
|
|
{
|
|
ImGui::MenuItem("fish_hat.c");
|
|
ImGui::MenuItem("fish_hat.inl");
|
|
ImGui::MenuItem("fish_hat.h");
|
|
if (ImGui::BeginMenu("More.."))
|
|
{
|
|
ImGui::MenuItem("Hello");
|
|
ImGui::MenuItem("Sailor");
|
|
if (ImGui::BeginMenu("Recurse.."))
|
|
{
|
|
ShowExampleMenuFile();
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::MenuItem("Save", "Ctrl+S"))
|
|
{}
|
|
if (ImGui::MenuItem("Save As.."))
|
|
{}
|
|
|
|
ImGui::Separator();
|
|
if (ImGui::BeginMenu("Options"))
|
|
{
|
|
static bool enabled = true;
|
|
ImGui::MenuItem("Enabled", "", &enabled);
|
|
ImGui::BeginChild("child", ImVec2(0, 60), true);
|
|
for (int i = 0; i < 10; i++)
|
|
ImGui::Text("Scrolling Text %d", i);
|
|
ImGui::EndChild();
|
|
static float f = 0.5f;
|
|
static int n = 0;
|
|
ImGui::SliderFloat("Value", &f, 0.0f, 1.0f);
|
|
ImGui::InputFloat("Input", &f, 0.1f);
|
|
ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0");
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Colors"))
|
|
{
|
|
float sz = ImGui::GetTextLineHeight();
|
|
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
|
{
|
|
const char* name = ImGui::GetStyleColorName((ImGuiCol)i);
|
|
ImVec2 p = ImGui::GetCursorScreenPos();
|
|
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i));
|
|
ImGui::Dummy(ImVec2(sz, sz));
|
|
ImGui::SameLine();
|
|
ImGui::MenuItem(name);
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
// Here we demonstrate appending again to the "Options" menu (which we already created above)
|
|
// Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice.
|
|
// In a real code-base using it would make senses to use this feature from very different code locations.
|
|
if (ImGui::BeginMenu("Options")) // <-- Append!
|
|
{
|
|
static bool b = true;
|
|
ImGui::Checkbox("SomeOption", &b);
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Disabled", false)) // Disabled
|
|
{
|
|
IM_ASSERT(0);
|
|
}
|
|
if (ImGui::MenuItem("Checked", NULL, true))
|
|
{}
|
|
if (ImGui::MenuItem("Quit", "Alt+F4"))
|
|
{}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
|
|
// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
|
|
struct ExampleAppConsole
|
|
{
|
|
char InputBuf[256];
|
|
ImVector<char*> Items;
|
|
ImVector<const char*> Commands;
|
|
ImVector<char*> History;
|
|
int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
|
|
ImGuiTextFilter Filter;
|
|
bool AutoScroll;
|
|
bool ScrollToBottom;
|
|
|
|
ExampleAppConsole()
|
|
{
|
|
ClearLog();
|
|
memset(InputBuf, 0, sizeof(InputBuf));
|
|
HistoryPos = -1;
|
|
|
|
// "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
|
|
Commands.push_back("HELP");
|
|
Commands.push_back("HISTORY");
|
|
Commands.push_back("CLEAR");
|
|
Commands.push_back("CLASSIFY");
|
|
AutoScroll = true;
|
|
ScrollToBottom = false;
|
|
AddLog("Welcome to Dear ImGui!");
|
|
}
|
|
~ExampleAppConsole()
|
|
{
|
|
ClearLog();
|
|
for (int i = 0; i < History.Size; i++)
|
|
free(History[i]);
|
|
}
|
|
|
|
// Portable helpers
|
|
static int Stricmp(const char* s1, const char* s2)
|
|
{
|
|
int d;
|
|
while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1)
|
|
{
|
|
s1++;
|
|
s2++;
|
|
}
|
|
return d;
|
|
}
|
|
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;
|
|
}
|
|
static char* Strdup(const char* s)
|
|
{
|
|
IM_ASSERT(s);
|
|
size_t len = strlen(s) + 1;
|
|
void* buf = malloc(len);
|
|
IM_ASSERT(buf);
|
|
return (char*)memcpy(buf, (const void*)s, len);
|
|
}
|
|
static void Strtrim(char* s)
|
|
{
|
|
char* str_end = s + strlen(s);
|
|
while (str_end > s && str_end[-1] == ' ')
|
|
str_end--;
|
|
*str_end = 0;
|
|
}
|
|
|
|
void ClearLog()
|
|
{
|
|
for (int i = 0; i < Items.Size; i++)
|
|
free(Items[i]);
|
|
Items.clear();
|
|
}
|
|
|
|
void AddLog(const char* fmt, ...) IM_FMTARGS(2)
|
|
{
|
|
// FIXME-OPT
|
|
char buf[1024];
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
|
|
buf[IM_ARRAYSIZE(buf) - 1] = 0;
|
|
va_end(args);
|
|
Items.push_back(Strdup(buf));
|
|
}
|
|
|
|
void Draw(const char* title, bool* p_open)
|
|
{
|
|
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin(title, p_open))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
|
|
// So e.g. IsItemHovered() will return true when hovering the title bar.
|
|
// Here we create a context menu only available from the title bar.
|
|
if (ImGui::BeginPopupContextItem())
|
|
{
|
|
if (ImGui::MenuItem("Close Console"))
|
|
*p_open = false;
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
ImGui::TextWrapped(
|
|
"This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A "
|
|
"more elaborate "
|
|
"implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
|
|
ImGui::TextWrapped("Enter 'HELP' for help.");
|
|
|
|
// TODO: display items starting from the bottom
|
|
|
|
if (ImGui::SmallButton("Add Debug Text"))
|
|
{
|
|
AddLog("%d some text", Items.Size);
|
|
AddLog("some more text");
|
|
AddLog("display very important message here!");
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::SmallButton("Add Debug Error"))
|
|
{
|
|
AddLog("[error] something went wrong");
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::SmallButton("Clear"))
|
|
{
|
|
ClearLog();
|
|
}
|
|
ImGui::SameLine();
|
|
bool copy_to_clipboard = ImGui::SmallButton("Copy");
|
|
// static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
|
|
|
|
ImGui::Separator();
|
|
|
|
// Options menu
|
|
if (ImGui::BeginPopup("Options"))
|
|
{
|
|
ImGui::Checkbox("Auto-scroll", &AutoScroll);
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Options, Filter
|
|
if (ImGui::Button("Options"))
|
|
ImGui::OpenPopup("Options");
|
|
ImGui::SameLine();
|
|
Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
|
|
ImGui::Separator();
|
|
|
|
// Reserve enough left-over height for 1 separator + 1 input text
|
|
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
|
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false,
|
|
ImGuiWindowFlags_HorizontalScrollbar);
|
|
if (ImGui::BeginPopupContextWindow())
|
|
{
|
|
if (ImGui::Selectable("Clear"))
|
|
ClearLog();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Display every line as a separate entry so we can change their color or add custom widgets.
|
|
// If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
|
|
// NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
|
|
// to only process visible items. The clipper will automatically measure the height of your first item and then
|
|
// "seek" to display only items in the visible area.
|
|
// To use the clipper we can replace your standard loop:
|
|
// for (int i = 0; i < Items.Size; i++)
|
|
// With:
|
|
// ImGuiListClipper clipper;
|
|
// clipper.Begin(Items.Size);
|
|
// while (clipper.Step())
|
|
// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
|
// - That your items are evenly spaced (same height)
|
|
// - That you have cheap random access to your elements (you can access them given their index,
|
|
// without processing all the ones before)
|
|
// You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
|
|
// We would need random-access on the post-filtered list.
|
|
// A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
|
|
// or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
|
|
// and appending newly elements as they are inserted. This is left as a task to the user until we can manage
|
|
// to improve this example code!
|
|
// If your items are of variable height:
|
|
// - Split them into same height items would be simpler and facilitate random-seeking into your list.
|
|
// - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing
|
|
if (copy_to_clipboard)
|
|
ImGui::LogToClipboard();
|
|
for (int i = 0; i < Items.Size; i++)
|
|
{
|
|
const char* item = Items[i];
|
|
if (!Filter.PassFilter(item))
|
|
continue;
|
|
|
|
// Normally you would store more information in your item than just a string.
|
|
// (e.g. make Items[] an array of structure, store color/type etc.)
|
|
ImVec4 color;
|
|
bool has_color = false;
|
|
if (strstr(item, "[error]"))
|
|
{
|
|
color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
|
|
has_color = true;
|
|
}
|
|
else if (strncmp(item, "# ", 2) == 0)
|
|
{
|
|
color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f);
|
|
has_color = true;
|
|
}
|
|
if (has_color)
|
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
|
ImGui::TextUnformatted(item);
|
|
if (has_color)
|
|
ImGui::PopStyleColor();
|
|
}
|
|
if (copy_to_clipboard)
|
|
ImGui::LogFinish();
|
|
|
|
if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
|
|
ImGui::SetScrollHereY(1.0f);
|
|
ScrollToBottom = false;
|
|
|
|
ImGui::PopStyleVar();
|
|
ImGui::EndChild();
|
|
ImGui::Separator();
|
|
|
|
// Command-line
|
|
bool reclaim_focus = false;
|
|
ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue |
|
|
ImGuiInputTextFlags_CallbackCompletion |
|
|
ImGuiInputTextFlags_CallbackHistory;
|
|
if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub,
|
|
(void*)this))
|
|
{
|
|
char* s = InputBuf;
|
|
Strtrim(s);
|
|
if (s[0])
|
|
ExecCommand(s);
|
|
strcpy(s, "");
|
|
reclaim_focus = true;
|
|
}
|
|
|
|
// Auto-focus on window apparition
|
|
ImGui::SetItemDefaultFocus();
|
|
if (reclaim_focus)
|
|
ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
void ExecCommand(const char* command_line)
|
|
{
|
|
AddLog("# %s\n", command_line);
|
|
|
|
// Insert into history. First find match and delete it so it can be pushed to the back.
|
|
// This isn't trying to be smart or optimal.
|
|
HistoryPos = -1;
|
|
for (int i = History.Size - 1; i >= 0; i--)
|
|
if (Stricmp(History[i], command_line) == 0)
|
|
{
|
|
free(History[i]);
|
|
History.erase(History.begin() + i);
|
|
break;
|
|
}
|
|
History.push_back(Strdup(command_line));
|
|
|
|
// Process command
|
|
if (Stricmp(command_line, "CLEAR") == 0)
|
|
{
|
|
ClearLog();
|
|
}
|
|
else if (Stricmp(command_line, "HELP") == 0)
|
|
{
|
|
AddLog("Commands:");
|
|
for (int i = 0; i < Commands.Size; i++)
|
|
AddLog("- %s", Commands[i]);
|
|
}
|
|
else if (Stricmp(command_line, "HISTORY") == 0)
|
|
{
|
|
int first = History.Size - 10;
|
|
for (int i = first > 0 ? first : 0; i < History.Size; i++)
|
|
AddLog("%3d: %s\n", i, History[i]);
|
|
}
|
|
else
|
|
{
|
|
AddLog("Unknown command: '%s'\n", command_line);
|
|
}
|
|
|
|
// On command input, we scroll to bottom even if AutoScroll==false
|
|
ScrollToBottom = true;
|
|
}
|
|
|
|
// In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
|
|
static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)
|
|
{
|
|
ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;
|
|
return console->TextEditCallback(data);
|
|
}
|
|
|
|
int TextEditCallback(ImGuiInputTextCallbackData* data)
|
|
{
|
|
// AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
|
|
switch (data->EventFlag)
|
|
{
|
|
case ImGuiInputTextFlags_CallbackCompletion:
|
|
{
|
|
// Example of TEXT COMPLETION
|
|
|
|
// Locate beginning of current word
|
|
const char* word_end = data->Buf + data->CursorPos;
|
|
const char* word_start = word_end;
|
|
while (word_start > data->Buf)
|
|
{
|
|
const char c = word_start[-1];
|
|
if (c == ' ' || c == '\t' || c == ',' || c == ';')
|
|
break;
|
|
word_start--;
|
|
}
|
|
|
|
// Build a list of candidates
|
|
ImVector<const char*> candidates;
|
|
for (int i = 0; i < Commands.Size; i++)
|
|
if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
|
|
candidates.push_back(Commands[i]);
|
|
|
|
if (candidates.Size == 0)
|
|
{
|
|
// No match
|
|
AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
|
|
}
|
|
else if (candidates.Size == 1)
|
|
{
|
|
// Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
|
|
data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
|
|
data->InsertChars(data->CursorPos, candidates[0]);
|
|
data->InsertChars(data->CursorPos, " ");
|
|
}
|
|
else
|
|
{
|
|
// Multiple matches. Complete as much as we can..
|
|
// So inputing "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
|
|
int match_len = (int)(word_end - word_start);
|
|
for (;;)
|
|
{
|
|
int c = 0;
|
|
bool all_candidates_matches = true;
|
|
for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
|
|
if (i == 0)
|
|
c = toupper(candidates[i][match_len]);
|
|
else if (c == 0 || c != toupper(candidates[i][match_len]))
|
|
all_candidates_matches = false;
|
|
if (!all_candidates_matches)
|
|
break;
|
|
match_len++;
|
|
}
|
|
|
|
if (match_len > 0)
|
|
{
|
|
data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
|
|
data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
|
|
}
|
|
|
|
// List matches
|
|
AddLog("Possible matches:\n");
|
|
for (int i = 0; i < candidates.Size; i++)
|
|
AddLog("- %s\n", candidates[i]);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ImGuiInputTextFlags_CallbackHistory:
|
|
{
|
|
// Example of HISTORY
|
|
const int prev_history_pos = HistoryPos;
|
|
if (data->EventKey == ImGuiKey_UpArrow)
|
|
{
|
|
if (HistoryPos == -1)
|
|
HistoryPos = History.Size - 1;
|
|
else if (HistoryPos > 0)
|
|
HistoryPos--;
|
|
}
|
|
else if (data->EventKey == ImGuiKey_DownArrow)
|
|
{
|
|
if (HistoryPos != -1)
|
|
if (++HistoryPos >= History.Size)
|
|
HistoryPos = -1;
|
|
}
|
|
|
|
// A better implementation would preserve the data on the current input line along with cursor position.
|
|
if (prev_history_pos != HistoryPos)
|
|
{
|
|
const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
|
|
data->DeleteChars(0, data->BufTextLen);
|
|
data->InsertChars(0, history_str);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
static void ShowExampleAppConsole(bool* p_open)
|
|
{
|
|
static ExampleAppConsole console;
|
|
console.Draw("Example: Console", p_open);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Usage:
|
|
// static ExampleAppLog my_log;
|
|
// my_log.AddLog("Hello %d world\n", 123);
|
|
// my_log.Draw("title");
|
|
struct ExampleAppLog
|
|
{
|
|
ImGuiTextBuffer Buf;
|
|
ImGuiTextFilter Filter;
|
|
ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
|
|
bool AutoScroll; // Keep scrolling if already at the bottom.
|
|
|
|
ExampleAppLog()
|
|
{
|
|
AutoScroll = true;
|
|
Clear();
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
Buf.clear();
|
|
LineOffsets.clear();
|
|
LineOffsets.push_back(0);
|
|
}
|
|
|
|
void AddLog(const char* fmt, ...) IM_FMTARGS(2)
|
|
{
|
|
int old_size = Buf.size();
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
Buf.appendfv(fmt, args);
|
|
va_end(args);
|
|
for (int new_size = Buf.size(); old_size < new_size; old_size++)
|
|
if (Buf[old_size] == '\n')
|
|
LineOffsets.push_back(old_size + 1);
|
|
}
|
|
|
|
void Draw(const char* title, bool* p_open = NULL)
|
|
{
|
|
if (!ImGui::Begin(title, p_open))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Options menu
|
|
if (ImGui::BeginPopup("Options"))
|
|
{
|
|
ImGui::Checkbox("Auto-scroll", &AutoScroll);
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Main window
|
|
if (ImGui::Button("Options"))
|
|
ImGui::OpenPopup("Options");
|
|
ImGui::SameLine();
|
|
bool clear = ImGui::Button("Clear");
|
|
ImGui::SameLine();
|
|
bool copy = ImGui::Button("Copy");
|
|
ImGui::SameLine();
|
|
Filter.Draw("Filter", -100.0f);
|
|
|
|
ImGui::Separator();
|
|
ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
|
|
|
|
if (clear)
|
|
Clear();
|
|
if (copy)
|
|
ImGui::LogToClipboard();
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
const char* buf = Buf.begin();
|
|
const char* buf_end = Buf.end();
|
|
if (Filter.IsActive())
|
|
{
|
|
// In this example we don't use the clipper when Filter is enabled.
|
|
// This is because we don't have a random access on the result on our filter.
|
|
// A real application processing logs with ten of thousands of entries may want to store the result of
|
|
// search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
|
|
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
|
|
{
|
|
const char* line_start = buf + LineOffsets[line_no];
|
|
const char* line_end =
|
|
(line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
|
if (Filter.PassFilter(line_start, line_end))
|
|
ImGui::TextUnformatted(line_start, line_end);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The simplest and easy way to display the entire buffer:
|
|
// ImGui::TextUnformatted(buf_begin, buf_end);
|
|
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
|
|
// to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
|
|
// within the visible area.
|
|
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
|
|
// on your side is recommended. Using ImGuiListClipper requires
|
|
// - A) random access into your data
|
|
// - B) items all being the same height,
|
|
// both of which we can handle since we an array pointing to the beginning of each line of text.
|
|
// When using the filter (in the block of code above) we don't have random access into the data to display
|
|
// anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
|
|
// it possible (and would be recommended if you want to search through tens of thousands of entries).
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin(LineOffsets.Size);
|
|
while (clipper.Step())
|
|
{
|
|
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
|
{
|
|
const char* line_start = buf + LineOffsets[line_no];
|
|
const char* line_end =
|
|
(line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
|
ImGui::TextUnformatted(line_start, line_end);
|
|
}
|
|
}
|
|
clipper.End();
|
|
}
|
|
ImGui::PopStyleVar();
|
|
|
|
if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
|
ImGui::SetScrollHereY(1.0f);
|
|
|
|
ImGui::EndChild();
|
|
ImGui::End();
|
|
}
|
|
};
|
|
|
|
// Demonstrate creating a simple log window with basic filtering.
|
|
static void ShowExampleAppLog(bool* p_open)
|
|
{
|
|
static ExampleAppLog log;
|
|
|
|
// For the demo: add a debug button _BEFORE_ the normal log window contents
|
|
// We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.
|
|
// Most of the contents of the window will be added by the log.Draw() call.
|
|
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
|
|
ImGui::Begin("Example: Log", p_open);
|
|
if (ImGui::SmallButton("[Debug] Add 5 entries"))
|
|
{
|
|
static int counter = 0;
|
|
const char* categories[3] = {"info", "warn", "error"};
|
|
const char* words[] = {"Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia",
|
|
"Absquatulate", "Nincompoop", "Pauciloquent"};
|
|
for (int n = 0; n < 5; n++)
|
|
{
|
|
const char* category = categories[counter % IM_ARRAYSIZE(categories)];
|
|
const char* word = words[counter % IM_ARRAYSIZE(words)];
|
|
log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", ImGui::GetFrameCount(),
|
|
category, ImGui::GetTime(), word);
|
|
counter++;
|
|
}
|
|
}
|
|
ImGui::End();
|
|
|
|
// Actually call in the regular Log helper (which will Begin() into the same window as we just did)
|
|
log.Draw("Example: Log", p_open);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate create a window with multiple child windows.
|
|
static void ShowExampleAppLayout(bool* p_open)
|
|
{
|
|
ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver);
|
|
if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar))
|
|
{
|
|
if (ImGui::BeginMenuBar())
|
|
{
|
|
if (ImGui::BeginMenu("File"))
|
|
{
|
|
if (ImGui::MenuItem("Close"))
|
|
*p_open = false;
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenuBar();
|
|
}
|
|
|
|
// Left
|
|
static int selected = 0;
|
|
{
|
|
ImGui::BeginChild("left pane", ImVec2(150, 0), true);
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
// FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav
|
|
char label[128];
|
|
sprintf(label, "MyObject %d", i);
|
|
if (ImGui::Selectable(label, selected == i))
|
|
selected = i;
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
// Right
|
|
{
|
|
ImGui::BeginGroup();
|
|
ImGui::BeginChild("item view",
|
|
ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
|
|
ImGui::Text("MyObject: %d", selected);
|
|
ImGui::Separator();
|
|
if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
|
|
{
|
|
if (ImGui::BeginTabItem("Description"))
|
|
{
|
|
ImGui::TextWrapped(
|
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut "
|
|
"labore et dolore magna aliqua. ");
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Details"))
|
|
{
|
|
ImGui::Text("ID: 0123456789");
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
}
|
|
ImGui::EndChild();
|
|
if (ImGui::Button("Revert"))
|
|
{}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Save"))
|
|
{}
|
|
ImGui::EndGroup();
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void ShowPlaceholderObject(const char* prefix, int uid)
|
|
{
|
|
// Use object uid as identifier. Most commonly you could also use the object pointer as a base ID.
|
|
ImGui::PushID(uid);
|
|
|
|
// Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to
|
|
// make the tree lines equal high.
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::AlignTextToFramePadding();
|
|
bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid);
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::Text("my sailor is rich");
|
|
|
|
if (node_open)
|
|
{
|
|
static float placeholder_members[8] = {0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f};
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
ImGui::PushID(i); // Use field index as identifier.
|
|
if (i < 2)
|
|
{
|
|
ShowPlaceholderObject("Child", 424242);
|
|
}
|
|
else
|
|
{
|
|
// Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well)
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGuiTreeNodeFlags flags =
|
|
ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet;
|
|
ImGui::TreeNodeEx("Field", flags, "Field_%d", i);
|
|
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
|
if (i >= 5)
|
|
ImGui::InputFloat("##value", &placeholder_members[i], 1.0f);
|
|
else
|
|
ImGui::DragFloat("##value", &placeholder_members[i], 0.01f);
|
|
ImGui::NextColumn();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
// Demonstrate create a simple property editor.
|
|
static void ShowExampleAppPropertyEditor(bool* p_open)
|
|
{
|
|
ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("Example: Property editor", p_open))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
HelpMarker(
|
|
"This example shows how you may implement a property editor using two columns.\n"
|
|
"All objects/fields data are dummies here.\n"
|
|
"Remember that in many simple cases, you can use ImGui::SameLine(xxx) to position\n"
|
|
"your cursor horizontally instead of using the Columns() API.");
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
|
|
if (ImGui::BeginTable("split", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable))
|
|
{
|
|
// Iterate placeholder objects (all the same data)
|
|
for (int obj_i = 0; obj_i < 4; obj_i++)
|
|
{
|
|
ShowPlaceholderObject("Object", obj_i);
|
|
// ImGui::Separator();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar();
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Long Text / ShowExampleAppLongText()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate/test rendering huge amount of text, and the incidence of clipping.
|
|
static void ShowExampleAppLongText(bool* p_open)
|
|
{
|
|
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("Example: Long text display", p_open))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
static int test_type = 0;
|
|
static ImGuiTextBuffer log;
|
|
static int lines = 0;
|
|
ImGui::Text("Printing unusually long amount of text.");
|
|
ImGui::Combo("Test type", &test_type,
|
|
"Single call to TextUnformatted()\0"
|
|
"Multiple calls to Text(), clipped\0"
|
|
"Multiple calls to Text(), not clipped (slow)\0");
|
|
ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size());
|
|
if (ImGui::Button("Clear"))
|
|
{
|
|
log.clear();
|
|
lines = 0;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Add 1000 lines"))
|
|
{
|
|
for (int i = 0; i < 1000; i++)
|
|
log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i);
|
|
lines += 1000;
|
|
}
|
|
ImGui::BeginChild("Log");
|
|
switch (test_type)
|
|
{
|
|
case 0:
|
|
// Single call to TextUnformatted() with a big buffer
|
|
ImGui::TextUnformatted(log.begin(), log.end());
|
|
break;
|
|
case 1:
|
|
{
|
|
// Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin(lines);
|
|
while (clipper.Step())
|
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
|
ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
|
|
ImGui::PopStyleVar();
|
|
break;
|
|
}
|
|
case 2:
|
|
// Multiple calls to Text(), not clipped (slow)
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
for (int i = 0; i < lines; i++)
|
|
ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
|
|
ImGui::PopStyleVar();
|
|
break;
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate creating a window which gets auto-resized according to its content.
|
|
static void ShowExampleAppAutoResize(bool* p_open)
|
|
{
|
|
if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
static int lines = 10;
|
|
ImGui::TextUnformatted(
|
|
"Window will resize every-frame to the size of its content.\n"
|
|
"Note that you probably don't want to query the window size to\n"
|
|
"output your content because that would create a feedback loop.");
|
|
ImGui::SliderInt("Number of lines", &lines, 1, 20);
|
|
for (int i = 0; i < lines; i++)
|
|
ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate creating a window with custom resize constraints.
|
|
static void ShowExampleAppConstrainedResize(bool* p_open)
|
|
{
|
|
struct CustomConstraints
|
|
{
|
|
// Helper functions to demonstrate programmatic constraints
|
|
static void Square(ImGuiSizeCallbackData* data)
|
|
{
|
|
data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);
|
|
}
|
|
static void Step(ImGuiSizeCallbackData* data)
|
|
{
|
|
float step = (float)(int)(intptr_t)data->UserData;
|
|
data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step,
|
|
(int)(data->DesiredSize.y / step + 0.5f) * step);
|
|
}
|
|
};
|
|
|
|
const char* test_desc[] = {
|
|
"Resize vertical only", "Resize horizontal only", "Width > 100, Height > 100", "Width 400-500",
|
|
"Height 400-500", "Custom: Always Square", "Custom: Fixed Steps (100)",
|
|
};
|
|
|
|
static bool auto_resize = false;
|
|
static int type = 0;
|
|
static int display_lines = 10;
|
|
if (type == 0)
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only
|
|
if (type == 1)
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only
|
|
if (type == 2)
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100
|
|
if (type == 3)
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500
|
|
if (type == 4)
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500
|
|
if (type == 5)
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX),
|
|
CustomConstraints::Square); // Always Square
|
|
if (type == 6)
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step,
|
|
(void*)(intptr_t)100); // Fixed Step
|
|
|
|
ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;
|
|
if (ImGui::Begin("Example: Constrained Resize", p_open, flags))
|
|
{
|
|
if (ImGui::IsWindowDocked())
|
|
ImGui::Text("Warning: Sizing Constraints won't work if the window is docked!");
|
|
if (ImGui::Button("200x200"))
|
|
{
|
|
ImGui::SetWindowSize(ImVec2(200, 200));
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("500x500"))
|
|
{
|
|
ImGui::SetWindowSize(ImVec2(500, 500));
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("800x200"))
|
|
{
|
|
ImGui::SetWindowSize(ImVec2(800, 200));
|
|
}
|
|
ImGui::SetNextItemWidth(200);
|
|
ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc));
|
|
ImGui::SetNextItemWidth(200);
|
|
ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100);
|
|
ImGui::Checkbox("Auto-resize", &auto_resize);
|
|
for (int i = 0; i < display_lines; i++)
|
|
ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, "");
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate creating a simple static window with no decoration
|
|
// + a context-menu to choose which corner of the screen to use.
|
|
static void ShowExampleAppSimpleOverlay(bool* p_open)
|
|
{
|
|
static int corner = 0;
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking |
|
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings |
|
|
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
|
|
if (corner != -1)
|
|
{
|
|
const float PAD = 10.0f;
|
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
|
|
ImVec2 work_size = viewport->WorkSize;
|
|
ImVec2 window_pos, window_pos_pivot;
|
|
window_pos.x = (corner & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);
|
|
window_pos.y = (corner & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);
|
|
window_pos_pivot.x = (corner & 1) ? 1.0f : 0.0f;
|
|
window_pos_pivot.y = (corner & 2) ? 1.0f : 0.0f;
|
|
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
|
|
ImGui::SetNextWindowViewport(viewport->ID);
|
|
window_flags |= ImGuiWindowFlags_NoMove;
|
|
}
|
|
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
|
|
if (ImGui::Begin("Example: Simple overlay", p_open, window_flags))
|
|
{
|
|
ImGui::Text(
|
|
"Simple overlay\n"
|
|
"in the corner of the screen.\n"
|
|
"(right-click to change position)");
|
|
ImGui::Separator();
|
|
if (ImGui::IsMousePosValid())
|
|
ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
|
|
else
|
|
ImGui::Text("Mouse Position: <invalid>");
|
|
if (ImGui::BeginPopupContextWindow())
|
|
{
|
|
if (ImGui::MenuItem("Custom", NULL, corner == -1))
|
|
corner = -1;
|
|
if (ImGui::MenuItem("Top-left", NULL, corner == 0))
|
|
corner = 0;
|
|
if (ImGui::MenuItem("Top-right", NULL, corner == 1))
|
|
corner = 1;
|
|
if (ImGui::MenuItem("Bottom-left", NULL, corner == 2))
|
|
corner = 2;
|
|
if (ImGui::MenuItem("Bottom-right", NULL, corner == 3))
|
|
corner = 3;
|
|
if (p_open && ImGui::MenuItem("Close"))
|
|
*p_open = false;
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate creating a window covering the entire screen/viewport
|
|
static void ShowExampleAppFullscreen(bool* p_open)
|
|
{
|
|
static bool use_work_area = true;
|
|
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
|
|
|
|
// We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
|
|
// Based on your use case you may want one of the other.
|
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
|
|
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
|
|
|
|
if (ImGui::Begin("Example: Fullscreen window", p_open, flags))
|
|
{
|
|
ImGui::Checkbox("Use work area instead of main area", &use_work_area);
|
|
ImGui::SameLine();
|
|
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.");
|
|
|
|
ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground);
|
|
ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration);
|
|
ImGui::Indent();
|
|
ImGui::CheckboxFlags("ImGuiWindowFlags_NoTitleBar", &flags, ImGuiWindowFlags_NoTitleBar);
|
|
ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse);
|
|
ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar);
|
|
ImGui::Unindent();
|
|
|
|
if (p_open && ImGui::Button("Close this window"))
|
|
*p_open = false;
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate using "##" and "###" in identifiers to manipulate ID generation.
|
|
// This apply to all regular items as well.
|
|
// Read FAQ section "How can I have multiple widgets with the same label?" for details.
|
|
static void ShowExampleAppWindowTitles(bool*)
|
|
{
|
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
const ImVec2 base_pos = viewport->Pos;
|
|
|
|
// By default, Windows are uniquely identified by their title.
|
|
// You can use the "##" and "###" markers to manipulate the display/ID.
|
|
|
|
// Using "##" to display same title but have unique identifier.
|
|
ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver);
|
|
ImGui::Begin("Same title as another window##1");
|
|
ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique.");
|
|
ImGui::End();
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver);
|
|
ImGui::Begin("Same title as another window##2");
|
|
ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique.");
|
|
ImGui::End();
|
|
|
|
// Using "###" to display a changing title but keep a static identifier "AnimatedTitle"
|
|
char buf[128];
|
|
sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3],
|
|
ImGui::GetFrameCount());
|
|
ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver);
|
|
ImGui::Begin(buf);
|
|
ImGui::Text("This window has a changing title.");
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate using the low-level ImDrawList to draw custom shapes.
|
|
static void ShowExampleAppCustomRendering(bool* p_open)
|
|
{
|
|
if (!ImGui::Begin("Example: Custom rendering", p_open))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
|
|
// overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
|
|
// types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
|
|
// exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
|
|
|
|
if (ImGui::BeginTabBar("##TabBar"))
|
|
{
|
|
if (ImGui::BeginTabItem("Primitives"))
|
|
{
|
|
ImGui::PushItemWidth(-ImGui::GetFontSize() * 15);
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
|
|
// Draw gradients
|
|
// (note that those are currently exacerbating our sRGB/Linear issues)
|
|
// Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the
|
|
// IM_COL32() directly as well..
|
|
ImGui::Text("Gradients");
|
|
ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
|
|
{
|
|
ImVec2 p0 = ImGui::GetCursorScreenPos();
|
|
ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
|
|
ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));
|
|
ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));
|
|
draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
|
|
ImGui::InvisibleButton("##gradient1", gradient_size);
|
|
}
|
|
{
|
|
ImVec2 p0 = ImGui::GetCursorScreenPos();
|
|
ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
|
|
ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));
|
|
ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));
|
|
draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
|
|
ImGui::InvisibleButton("##gradient2", gradient_size);
|
|
}
|
|
|
|
// Draw a bunch of primitives
|
|
ImGui::Text("All primitives");
|
|
static float sz = 36.0f;
|
|
static float thickness = 3.0f;
|
|
static int ngon_sides = 6;
|
|
static bool circle_segments_override = false;
|
|
static int circle_segments_override_v = 12;
|
|
static bool curve_segments_override = false;
|
|
static int curve_segments_override_v = 8;
|
|
static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
|
|
ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 100.0f, "%.0f");
|
|
ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f");
|
|
ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12);
|
|
ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override);
|
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
|
circle_segments_override |=
|
|
ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40);
|
|
ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override);
|
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
|
curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40);
|
|
ImGui::ColorEdit4("Color", &colf.x);
|
|
|
|
const ImVec2 p = ImGui::GetCursorScreenPos();
|
|
const ImU32 col = ImColor(colf);
|
|
const float spacing = 10.0f;
|
|
const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;
|
|
const float rounding = sz / 5.0f;
|
|
const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;
|
|
const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;
|
|
float x = p.x + 4.0f;
|
|
float y = p.y + 4.0f;
|
|
for (int n = 0; n < 2; n++)
|
|
{
|
|
// First line uses a thickness of 1.0f, second line uses the configurable thickness
|
|
float th = (n == 0) ? 1.0f : thickness;
|
|
draw_list->AddNgon(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides, th);
|
|
x += sz + spacing; // N-gon
|
|
draw_list->AddCircle(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments, th);
|
|
x += sz + spacing; // Circle
|
|
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th);
|
|
x += sz + spacing; // Square
|
|
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th);
|
|
x += sz + spacing; // Square with all rounded corners
|
|
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
|
|
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
|
|
// 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
|
|
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!)
|
|
draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th);
|
|
x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
|
|
draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th);
|
|
x += sz + spacing; // Diagonal line
|
|
|
|
// Quadratic Bezier Curve (3 control points)
|
|
ImVec2 cp3[3] = {ImVec2(x, y + sz * 0.6f), ImVec2(x + sz * 0.5f, y - sz * 0.4f),
|
|
ImVec2(x + sz, y + sz)};
|
|
draw_list->AddBezierQuadratic(cp3[0], cp3[1], cp3[2], col, th, curve_segments);
|
|
x += sz + spacing;
|
|
|
|
// Cubic Bezier Curve (4 control points)
|
|
ImVec2 cp4[4] = {ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f),
|
|
ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz)};
|
|
draw_list->AddBezierCubic(cp4[0], cp4[1], cp4[2], cp4[3], col, th, curve_segments);
|
|
|
|
x = p.x + 4;
|
|
y += sz + spacing;
|
|
}
|
|
draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides);
|
|
x += sz + spacing; // N-gon
|
|
draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments);
|
|
x += sz + spacing; // Circle
|
|
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col);
|
|
x += sz + spacing; // Square
|
|
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f);
|
|
x += sz + spacing; // Square with all rounded corners
|
|
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
|
|
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
|
|
// 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
|
|
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)
|
|
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)
|
|
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col);
|
|
x += sz; // Pixel (faster than AddLine)
|
|
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));
|
|
|
|
ImGui::Dummy(ImVec2((sz + spacing) * 10.2f, (sz + spacing) * 3.0f));
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Canvas"))
|
|
{
|
|
static ImVector<ImVec2> points;
|
|
static ImVec2 scrolling(0.0f, 0.0f);
|
|
static bool opt_enable_grid = true;
|
|
static bool opt_enable_context_menu = true;
|
|
static bool adding_line = false;
|
|
|
|
ImGui::Checkbox("Enable grid", &opt_enable_grid);
|
|
ImGui::Checkbox("Enable context menu", &opt_enable_context_menu);
|
|
ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
|
|
|
|
// Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
|
|
// Here we demonstrate that this can be replaced by simple offsetting + custom drawing +
|
|
// PushClipRect/PopClipRect() calls. To use a child window instead we could use, e.g:
|
|
// ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
|
|
// ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
|
|
// ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_NoMove);
|
|
// ImGui::PopStyleColor();
|
|
// ImGui::PopStyleVar();
|
|
// [...]
|
|
// ImGui::EndChild();
|
|
|
|
// Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use
|
|
// IsItemHovered()/IsItemActive()
|
|
ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
|
|
ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
|
|
if (canvas_sz.x < 50.0f)
|
|
canvas_sz.x = 50.0f;
|
|
if (canvas_sz.y < 50.0f)
|
|
canvas_sz.y = 50.0f;
|
|
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
|
|
|
|
// Draw border and background color
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));
|
|
draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));
|
|
|
|
// This will catch our interactions
|
|
ImGui::InvisibleButton("canvas", canvas_sz,
|
|
ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
|
|
const bool is_hovered = ImGui::IsItemHovered(); // Hovered
|
|
const bool is_active = ImGui::IsItemActive(); // Held
|
|
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
|
|
const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
|
|
|
|
// Add first and second point
|
|
if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
|
|
{
|
|
points.push_back(mouse_pos_in_canvas);
|
|
points.push_back(mouse_pos_in_canvas);
|
|
adding_line = true;
|
|
}
|
|
if (adding_line)
|
|
{
|
|
points.back() = mouse_pos_in_canvas;
|
|
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
|
|
adding_line = false;
|
|
}
|
|
|
|
// Pan (we use a zero mouse threshold when there's no context menu)
|
|
// You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
|
|
const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;
|
|
if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan))
|
|
{
|
|
scrolling.x += io.MouseDelta.x;
|
|
scrolling.y += io.MouseDelta.y;
|
|
}
|
|
|
|
// Context menu (under default mouse threshold)
|
|
ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
|
|
if (opt_enable_context_menu && ImGui::IsMouseReleased(ImGuiMouseButton_Right) && drag_delta.x == 0.0f &&
|
|
drag_delta.y == 0.0f)
|
|
ImGui::OpenPopupOnItemClick("context");
|
|
if (ImGui::BeginPopup("context"))
|
|
{
|
|
if (adding_line)
|
|
points.resize(points.size() - 2);
|
|
adding_line = false;
|
|
if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0))
|
|
{
|
|
points.resize(points.size() - 2);
|
|
}
|
|
if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0))
|
|
{
|
|
points.clear();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Draw grid + all lines in the canvas
|
|
draw_list->PushClipRect(canvas_p0, canvas_p1, true);
|
|
if (opt_enable_grid)
|
|
{
|
|
const float GRID_STEP = 64.0f;
|
|
for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
|
|
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));
|
|
for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
|
|
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));
|
|
}
|
|
for (int n = 0; n < points.Size; n += 2)
|
|
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);
|
|
draw_list->PopClipRect();
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("BG/FG draw lists"))
|
|
{
|
|
static bool draw_bg = true;
|
|
static bool draw_fg = true;
|
|
ImGui::Checkbox("Draw in Background draw list", &draw_bg);
|
|
ImGui::SameLine();
|
|
HelpMarker("The Background draw list will be rendered below every Dear ImGui windows.");
|
|
ImGui::Checkbox("Draw in Foreground draw list", &draw_fg);
|
|
ImGui::SameLine();
|
|
HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows.");
|
|
ImVec2 window_pos = ImGui::GetWindowPos();
|
|
ImVec2 window_size = ImGui::GetWindowSize();
|
|
ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);
|
|
if (draw_bg)
|
|
ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200),
|
|
0, 10 + 4);
|
|
if (draw_fg)
|
|
ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200),
|
|
0, 10);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Demonstrate using DockSpace() to create an explicit docking node within an existing window.
|
|
// Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use
|
|
// Docking!
|
|
// - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.
|
|
// - Drag from window menu button (upper-left button) to undock an entire node (all windows).
|
|
// About dockspaces:
|
|
// - Use DockSpace() to create an explicit dock node _within_ an existing window.
|
|
// - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport.
|
|
// This is often used with ImGuiDockNodeFlags_PassthruCentralNode.
|
|
// - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! (*)
|
|
// - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked.
|
|
// e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with
|
|
// ImGuiDockNodeFlags_KeepAliveOnly.
|
|
// (*) because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node,
|
|
// because that window is submitted as part of the part of the NewFrame() call. An easy workaround is that you can
|
|
// create your own implicit "Debug##2" window after calling DockSpace() and leave it in the window stack for anyone to
|
|
// use.
|
|
void ShowExampleAppDockSpace(bool* p_open)
|
|
{
|
|
// If you strip some features of, this demo is pretty much equivalent to calling DockSpaceOverViewport()!
|
|
// In most cases you should be able to just call DockSpaceOverViewport() and ignore all the code below!
|
|
// In this specific demo, we are not using DockSpaceOverViewport() because:
|
|
// - we allow the host window to be floating/moveable instead of filling the viewport (when opt_fullscreen == false)
|
|
// - we allow the host window to have padding (when opt_padding == true)
|
|
// - we have a local menu bar in the host window (vs. you could use BeginMainMenuBar() + DockSpaceOverViewport() in
|
|
// your code!) TL;DR; this demo is more complicated than what you would normally use. If we removed all the options
|
|
// we are showcasing, this demo would become:
|
|
// void ShowExampleAppDockSpace()
|
|
// {
|
|
// ImGui::DockSpaceOverViewport(ImGui::GetMainViewport());
|
|
// }
|
|
|
|
static bool opt_fullscreen = true;
|
|
static bool opt_padding = false;
|
|
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
|
|
|
|
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
|
|
// because it would be confusing to have two docking targets within each others.
|
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
|
if (opt_fullscreen)
|
|
{
|
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
ImGui::SetNextWindowPos(viewport->WorkPos);
|
|
ImGui::SetNextWindowSize(viewport->WorkSize);
|
|
ImGui::SetNextWindowViewport(viewport->ID);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
|
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
|
|
ImGuiWindowFlags_NoMove;
|
|
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
|
}
|
|
else
|
|
{
|
|
dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
|
|
}
|
|
|
|
// When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background
|
|
// and handle the pass-thru hole, so we ask Begin() to not render a background.
|
|
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
|
|
window_flags |= ImGuiWindowFlags_NoBackground;
|
|
|
|
// Important: note that we proceed even if Begin() returns false (aka window is collapsed).
|
|
// This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
|
|
// all active windows docked into it will lose their parent and become undocked.
|
|
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
|
|
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
|
|
if (!opt_padding)
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
|
ImGui::Begin("DockSpace Demo", p_open, window_flags);
|
|
if (!opt_padding)
|
|
ImGui::PopStyleVar();
|
|
|
|
if (opt_fullscreen)
|
|
ImGui::PopStyleVar(2);
|
|
|
|
// Submit the DockSpace
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
|
{
|
|
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
|
|
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
|
|
}
|
|
else
|
|
{
|
|
ShowDockingDisabledMessage();
|
|
}
|
|
|
|
if (ImGui::BeginMenuBar())
|
|
{
|
|
if (ImGui::BeginMenu("Options"))
|
|
{
|
|
// Disabling fullscreen would allow the window to be moved to the front of other windows,
|
|
// which we can't undo at the moment without finer window depth/z control.
|
|
ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen);
|
|
ImGui::MenuItem("Padding", NULL, &opt_padding);
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0))
|
|
{
|
|
dockspace_flags ^= ImGuiDockNodeFlags_NoSplit;
|
|
}
|
|
if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0))
|
|
{
|
|
dockspace_flags ^= ImGuiDockNodeFlags_NoResize;
|
|
}
|
|
if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "",
|
|
(dockspace_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0))
|
|
{
|
|
dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode;
|
|
}
|
|
if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0))
|
|
{
|
|
dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar;
|
|
}
|
|
if (ImGui::MenuItem("Flag: PassthruCentralNode", "",
|
|
(dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, opt_fullscreen))
|
|
{
|
|
dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode;
|
|
}
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::MenuItem("Close", NULL, false, p_open != NULL))
|
|
*p_open = false;
|
|
ImGui::EndMenu();
|
|
}
|
|
HelpMarker(
|
|
"When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!"
|
|
"\n"
|
|
"- Drag from window title bar or their tab to dock/undock."
|
|
"\n"
|
|
"- Drag from window menu button (upper-left button) to undock an entire node (all windows)."
|
|
"\n"
|
|
"- Hold SHIFT to disable docking."
|
|
"\n"
|
|
"This demo app has nothing to do with it!"
|
|
"\n\n"
|
|
"This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a "
|
|
"docking node _within_ another window."
|
|
"\n\n"
|
|
"Read comments in ShowExampleAppDockSpace() for more details.");
|
|
|
|
ImGui::EndMenuBar();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Simplified structure to mimic a Document model
|
|
struct MyDocument
|
|
{
|
|
const char* Name; // Document title
|
|
bool Open; // Set when open (we keep an array of all available documents to simplify demo code!)
|
|
bool OpenPrev; // Copy of Open from last update.
|
|
bool Dirty; // Set when the document has been modified
|
|
bool WantClose; // Set when the document
|
|
ImVec4 Color; // An arbitrary variable associated to the document
|
|
|
|
MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
|
|
{
|
|
Name = name;
|
|
Open = OpenPrev = open;
|
|
Dirty = false;
|
|
WantClose = false;
|
|
Color = color;
|
|
}
|
|
void DoOpen() { Open = true; }
|
|
void DoQueueClose() { WantClose = true; }
|
|
void DoForceClose()
|
|
{
|
|
Open = false;
|
|
Dirty = false;
|
|
}
|
|
void DoSave() { Dirty = false; }
|
|
|
|
// Display placeholder contents for the Document
|
|
static void DisplayContents(MyDocument* doc)
|
|
{
|
|
ImGui::PushID(doc);
|
|
ImGui::Text("Document \"%s\"", doc->Name);
|
|
ImGui::PushStyleColor(ImGuiCol_Text, doc->Color);
|
|
ImGui::TextWrapped(
|
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et "
|
|
"dolore magna aliqua.");
|
|
ImGui::PopStyleColor();
|
|
if (ImGui::Button("Modify", ImVec2(100, 0)))
|
|
doc->Dirty = true;
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Save", ImVec2(100, 0)))
|
|
doc->DoSave();
|
|
ImGui::ColorEdit3("color",
|
|
&doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior.
|
|
ImGui::PopID();
|
|
}
|
|
|
|
// Display context menu for the Document
|
|
static void DisplayContextMenu(MyDocument* doc)
|
|
{
|
|
if (!ImGui::BeginPopupContextItem())
|
|
return;
|
|
|
|
char buf[256];
|
|
sprintf(buf, "Save %s", doc->Name);
|
|
if (ImGui::MenuItem(buf, "CTRL+S", false, doc->Open))
|
|
doc->DoSave();
|
|
if (ImGui::MenuItem("Close", "CTRL+W", false, doc->Open))
|
|
doc->DoQueueClose();
|
|
ImGui::EndPopup();
|
|
}
|
|
};
|
|
|
|
struct ExampleAppDocuments
|
|
{
|
|
ImVector<MyDocument> Documents;
|
|
|
|
ExampleAppDocuments()
|
|
{
|
|
Documents.push_back(MyDocument("Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
|
|
Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
|
|
Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
|
|
Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));
|
|
Documents.push_back(MyDocument("A Rather Long Title", false));
|
|
Documents.push_back(MyDocument("Some Document", false));
|
|
}
|
|
};
|
|
|
|
// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
|
|
// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
|
|
// as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
|
|
// 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
|
|
// disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
|
|
// give the impression of a flicker for one frame.
|
|
// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
|
|
// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
|
|
static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app)
|
|
{
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
{
|
|
MyDocument* doc = &app.Documents[doc_n];
|
|
if (!doc->Open && doc->OpenPrev)
|
|
ImGui::SetTabItemClosed(doc->Name);
|
|
doc->OpenPrev = doc->Open;
|
|
}
|
|
}
|
|
|
|
void ShowExampleAppDocuments(bool* p_open)
|
|
{
|
|
static ExampleAppDocuments app;
|
|
|
|
// Options
|
|
enum Target
|
|
{
|
|
Target_None,
|
|
Target_Tab, // Create documents as local tab into a local tab bar
|
|
Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace
|
|
};
|
|
static Target opt_target = Target_Tab;
|
|
static bool opt_reorderable = true;
|
|
static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;
|
|
|
|
// When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g.
|
|
// "Eggplant") that we emit gets docked into the same spot as the parent window ("Example: Documents"). This would
|
|
// create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab
|
|
// not visible, which in turn would stop submitting the "Eggplant" window.
|
|
// We avoid this problem by submitting our documents window even if our parent window is not currently visible.
|
|
// Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking.
|
|
|
|
bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar);
|
|
if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow)
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Menu
|
|
if (ImGui::BeginMenuBar())
|
|
{
|
|
if (ImGui::BeginMenu("File"))
|
|
{
|
|
int open_count = 0;
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
open_count += app.Documents[doc_n].Open ? 1 : 0;
|
|
|
|
if (ImGui::BeginMenu("Open", open_count < app.Documents.Size))
|
|
{
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
{
|
|
MyDocument* doc = &app.Documents[doc_n];
|
|
if (!doc->Open)
|
|
if (ImGui::MenuItem(doc->Name))
|
|
doc->DoOpen();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0))
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
app.Documents[doc_n].DoQueueClose();
|
|
if (ImGui::MenuItem("Exit", "Alt+F4"))
|
|
{}
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMenuBar();
|
|
}
|
|
|
|
// [Debug] List documents with one checkbox for each
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
{
|
|
MyDocument* doc = &app.Documents[doc_n];
|
|
if (doc_n > 0)
|
|
ImGui::SameLine();
|
|
ImGui::PushID(doc);
|
|
if (ImGui::Checkbox(doc->Name, &doc->Open))
|
|
if (!doc->Open)
|
|
doc->DoForceClose();
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::PushItemWidth(ImGui::GetFontSize() * 12);
|
|
ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0DockSpace+Window\0");
|
|
ImGui::PopItemWidth();
|
|
bool redock_all = false;
|
|
if (opt_target == Target_Tab)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Reorderable Tabs", &opt_reorderable);
|
|
}
|
|
if (opt_target == Target_DockSpaceAndWindow)
|
|
{
|
|
ImGui::SameLine();
|
|
redock_all = ImGui::Button("Redock all");
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.
|
|
// They have multiple effects:
|
|
// - Display a dot next to the title.
|
|
// - Tab is selected when clicking the X close button.
|
|
// - Closure is not assumed (will wait for user to stop submitting the tab).
|
|
// Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab
|
|
// bar. We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would
|
|
// leave an empty hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. The
|
|
// rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
|
|
|
|
// Tabs
|
|
if (opt_target == Target_Tab)
|
|
{
|
|
ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);
|
|
if (ImGui::BeginTabBar("##tabs", tab_bar_flags))
|
|
{
|
|
if (opt_reorderable)
|
|
NotifyOfDocumentsClosedElsewhere(app);
|
|
|
|
// [DEBUG] Stress tests
|
|
// if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide
|
|
// a tab. Test various interactions e.g. dragging with this on. if (ImGui::GetIO().KeyCtrl)
|
|
// ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful
|
|
// as-is anyway..
|
|
|
|
// Submit Tabs
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
{
|
|
MyDocument* doc = &app.Documents[doc_n];
|
|
if (!doc->Open)
|
|
continue;
|
|
|
|
ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);
|
|
bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags);
|
|
|
|
// Cancel attempt to close when unsaved add to save queue so we can display a popup.
|
|
if (!doc->Open && doc->Dirty)
|
|
{
|
|
doc->Open = true;
|
|
doc->DoQueueClose();
|
|
}
|
|
|
|
MyDocument::DisplayContextMenu(doc);
|
|
if (visible)
|
|
{
|
|
MyDocument::DisplayContents(doc);
|
|
ImGui::EndTabItem();
|
|
}
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
}
|
|
else if (opt_target == Target_DockSpaceAndWindow)
|
|
{
|
|
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
|
{
|
|
NotifyOfDocumentsClosedElsewhere(app);
|
|
|
|
// Create a DockSpace node where any window can be docked
|
|
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
|
|
ImGui::DockSpace(dockspace_id);
|
|
|
|
// Create Windows
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
{
|
|
MyDocument* doc = &app.Documents[doc_n];
|
|
if (!doc->Open)
|
|
continue;
|
|
|
|
ImGui::SetNextWindowDockID(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver);
|
|
ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0);
|
|
bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags);
|
|
|
|
// Cancel attempt to close when unsaved add to save queue so we can display a popup.
|
|
if (!doc->Open && doc->Dirty)
|
|
{
|
|
doc->Open = true;
|
|
doc->DoQueueClose();
|
|
}
|
|
|
|
MyDocument::DisplayContextMenu(doc);
|
|
if (visible)
|
|
MyDocument::DisplayContents(doc);
|
|
|
|
ImGui::End();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShowDockingDisabledMessage();
|
|
}
|
|
}
|
|
|
|
// Early out other contents
|
|
if (!window_contents_visible)
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Update closing queue
|
|
static ImVector<MyDocument*> close_queue;
|
|
if (close_queue.empty())
|
|
{
|
|
// Close queue is locked once we started a popup
|
|
for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
|
|
{
|
|
MyDocument* doc = &app.Documents[doc_n];
|
|
if (doc->WantClose)
|
|
{
|
|
doc->WantClose = false;
|
|
close_queue.push_back(doc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Display closing confirmation UI
|
|
if (!close_queue.empty())
|
|
{
|
|
int close_queue_unsaved_documents = 0;
|
|
for (int n = 0; n < close_queue.Size; n++)
|
|
if (close_queue[n]->Dirty)
|
|
close_queue_unsaved_documents++;
|
|
|
|
if (close_queue_unsaved_documents == 0)
|
|
{
|
|
// Close documents when all are unsaved
|
|
for (int n = 0; n < close_queue.Size; n++)
|
|
close_queue[n]->DoForceClose();
|
|
close_queue.clear();
|
|
}
|
|
else
|
|
{
|
|
if (!ImGui::IsPopupOpen("Save?"))
|
|
ImGui::OpenPopup("Save?");
|
|
if (ImGui::BeginPopupModal("Save?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
|
{
|
|
ImGui::Text("Save change to the following items?");
|
|
float item_height = ImGui::GetTextLineHeightWithSpacing();
|
|
if (ImGui::BeginChildFrame(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height)))
|
|
{
|
|
for (int n = 0; n < close_queue.Size; n++)
|
|
if (close_queue[n]->Dirty)
|
|
ImGui::Text("%s", close_queue[n]->Name);
|
|
ImGui::EndChildFrame();
|
|
}
|
|
|
|
ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
|
|
if (ImGui::Button("Yes", button_size))
|
|
{
|
|
for (int n = 0; n < close_queue.Size; n++)
|
|
{
|
|
if (close_queue[n]->Dirty)
|
|
close_queue[n]->DoSave();
|
|
close_queue[n]->DoForceClose();
|
|
}
|
|
close_queue.clear();
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("No", button_size))
|
|
{
|
|
for (int n = 0; n < close_queue.Size; n++)
|
|
close_queue[n]->DoForceClose();
|
|
close_queue.clear();
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Cancel", button_size))
|
|
{
|
|
close_queue.clear();
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
// End of Demo code
|
|
# else
|
|
|
|
void ImGui::ShowAboutWindow(bool*) {}
|
|
void ImGui::ShowDemoWindow(bool*) {}
|
|
void ImGui::ShowUserGuide() {}
|
|
void ImGui::ShowStyleEditor(ImGuiStyle*) {}
|
|
|
|
# endif
|
|
|
|
#endif // #ifndef IMGUI_DISABLE
|