Bake vertex AO
This commit is contained in:
Mikulas Florek 2022-12-04 18:54:09 +01:00 committed by GitHub
parent fc588f200b
commit 5160154a80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 2347 additions and 873 deletions

1208
external/imgui/imgui.cpp vendored

File diff suppressed because it is too large Load diff

148
external/imgui/imgui.h vendored
View file

@ -1,4 +1,4 @@
// dear imgui, v1.89 WIP
// dear imgui, v1.89.1 WIP
// (headers)
// Help:
@ -22,8 +22,8 @@
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345')
#define IMGUI_VERSION "1.89 WIP"
#define IMGUI_VERSION_NUM 18821
#define IMGUI_VERSION "1.89.1 WIP"
#define IMGUI_VERSION_NUM 18902
#define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch
#define IMGUI_HAS_DOCK // Docking WIP branch
@ -50,13 +50,12 @@ Index of this file:
#pragma once
// Configuration file with compile-time options (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system')
// Configuration file with compile-time options
// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system')
#ifdef IMGUI_USER_CONFIG
#include IMGUI_USER_CONFIG
#endif
#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H)
#include "imconfig.h"
#endif
#ifndef IMGUI_DISABLE
@ -167,20 +166,26 @@ struct ImGuiTextFilter; // Helper to parse and apply text filters (e
struct ImGuiViewport; // A Platform Window (always 1 unless multi-viewport are enabled. One per platform window to output to). In the future may represent Platform Monitor
struct ImGuiWindowClass; // Window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info)
// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file)
// Enumerations
// - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") 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.
enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value)
typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling
typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions
typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type
typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction
typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier
typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle)
typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier
typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor shape
typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A sorting direction (ascending or descending)
typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling
typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor()
// Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") 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.
typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance
typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build
@ -194,7 +199,7 @@ typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: f
typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused()
typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc.
typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline()
typedef int ImGuiModFlags; // -> enum ImGuiModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super)
typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for storage only for now: an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.
typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
@ -365,6 +370,7 @@ namespace ImGui
IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin()
IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin()
IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin()
IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // set next window scrolling value (use < 0.0f to not affect a given axis).
IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground.
IMGUI_API void SetNextWindowViewport(ImGuiID viewport_id); // set next window viewport
IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects.
@ -386,6 +392,8 @@ namespace ImGui
IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be overridden with SetNextWindowContentSize(), in window coordinates
// Windows Scrolling
// - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin().
// - You may instead use SetNextWindowScroll() prior to calling Begin() to avoid this delay, as an alternative to using SetScrollX()/SetScrollY().
IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()]
IMGUI_API float GetScrollY(); // get scrolling amount [0 .. GetScrollMaxY()]
IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0 .. GetScrollMaxX()]
@ -515,7 +523,7 @@ namespace ImGui
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0));
IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));
// Widgets: Combo Box
// Widgets: Combo Box (Dropdown)
// - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items.
// - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. This is analogous to how ListBox are created.
IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0);
@ -935,8 +943,8 @@ namespace ImGui
IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold)
IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold)
IMGUI_API void ResetMouseDragDelta(ImGuiMouseButton button = 0); //
IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you
IMGUI_API void SetMouseCursor(ImGuiMouseCursor cursor_type); // set desired cursor type
IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired mouse cursor shape. Important: reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you
IMGUI_API void SetMouseCursor(ImGuiMouseCursor cursor_type); // set desired mouse cursor shape
IMGUI_API void SetNextFrameWantCaptureMouse(bool want_capture_mouse); // Override io.WantCaptureMouse flag next frame (said flag is left for your application to handle, typical when true it instucts your app to ignore inputs). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse;" after the next NewFrame() call.
// Clipboard Utilities
@ -983,6 +991,7 @@ namespace ImGui
//-----------------------------------------------------------------------------
// Flags for ImGui::Begin()
// (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly)
enum ImGuiWindowFlags_
{
ImGuiWindowFlags_None = 0,
@ -1023,6 +1032,7 @@ enum ImGuiWindowFlags_
};
// Flags for ImGui::InputText()
// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive)
enum ImGuiInputTextFlags_
{
ImGuiInputTextFlags_None = 0,
@ -1046,6 +1056,7 @@ enum ImGuiInputTextFlags_
ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input)
ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active)
ImGuiInputTextFlags_EscapeClearsAll = 1 << 20, // Escape key clears content if not empty, and deactivate otherwise (constrast to default behavior of Escape to revert)
// Obsolete names (will be removed soon)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@ -1223,12 +1234,6 @@ enum ImGuiTableFlags_
// [Internal] Combinations and masks
ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,
// Obsolete names (will be removed soon)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//, ImGuiTableFlags_ColumnsWidthFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_ColumnsWidthStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2020/12
//, ImGuiTableFlags_SizingPolicyFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingPolicyStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2021/01
#endif
};
// Flags for ImGui::TableSetupColumn()
@ -1266,11 +1271,6 @@ enum ImGuiTableColumnFlags_
ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,
ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered,
ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)
// Obsolete names (will be removed soon)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//ImGuiTableColumnFlags_WidthAuto = ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, // Column will not stretch and keep resizing based on submitted contents.
#endif
};
// Flags for ImGui::TableNextRow()
@ -1407,9 +1407,10 @@ enum ImGuiSortDirection_
ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc.
};
// Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87)
// Keys value >= 512 are named keys (>= 1.87)
enum ImGuiKey_
// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value)
// All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87)
// Since >= 1.89 we increased typing (went from int to enum), some legacy code may need a cast to ImGuiKey.
enum ImGuiKey : int
{
// Keyboard
ImGuiKey_None = 0,
@ -1490,22 +1491,33 @@ enum ImGuiKey_
ImGuiKey_GamepadRStickUp, // [Analog]
ImGuiKey_GamepadRStickDown, // [Analog]
// Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)
// - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing
// them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc.
// - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those
// and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl).
// - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
// In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
// backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
ImGuiKey_ModCtrl, ImGuiKey_ModShift, ImGuiKey_ModAlt, ImGuiKey_ModSuper,
// Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls)
// - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API.
ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY,
// End of list
ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value
// [Internal] Reserved for mod storage
ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper,
ImGuiKey_COUNT,
// Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)
// - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing
// them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc.
// - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those
// and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiMod_Ctrl).
// - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
// In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
// backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
ImGuiMod_None = 0,
ImGuiMod_Ctrl = 1 << 12,
ImGuiMod_Shift = 1 << 13,
ImGuiMod_Alt = 1 << 14, // Option/Menu
ImGuiMod_Super = 1 << 15, // Cmd/Super/Windows
ImGuiMod_Mask_ = 0xF000,
#if defined(__APPLE__)
ImGuiMod_Shortcut = ImGuiMod_Super,
#else
ImGuiMod_Shortcut = ImGuiMod_Ctrl,
#endif
// [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + the io.KeyMap[] array.
// We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE)
@ -1521,21 +1533,11 @@ enum ImGuiKey_
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89
ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87
#endif
};
// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API.
enum ImGuiModFlags_
{
ImGuiModFlags_None = 0,
ImGuiModFlags_Ctrl = 1 << 0,
ImGuiModFlags_Shift = 1 << 1,
ImGuiModFlags_Alt = 1 << 2, // Option/Menu key
ImGuiModFlags_Super = 1 << 3, // Cmd/Super/Windows key
ImGuiModFlags_All = 0x0F
};
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
// OBSOLETED in 1.88 (from July 2022): ImGuiNavInput and io.NavInputs[].
// Official backends between 1.60 and 1.86: will keep working and feed gamepad inputs as long as IMGUI_DISABLE_OBSOLETE_KEYIO is not set.
@ -1746,6 +1748,7 @@ enum ImGuiColorEditFlags_
// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigDragClickToInputText)
enum ImGuiSliderFlags_
{
ImGuiSliderFlags_None = 0,
@ -1959,7 +1962,7 @@ struct ImGuiStyle
//-----------------------------------------------------------------------------
// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.
// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and not io.KeysData[key]->DownDuration.
// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration.
struct ImGuiKeyData
{
bool Down; // True for if key is down
@ -2090,6 +2093,7 @@ struct ImGuiIO
// Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.
// This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
// Old (<1.87): ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.
@ -2114,8 +2118,8 @@ struct ImGuiIO
bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows
// Other state maintained from data above + IO function calls
ImGuiModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame()
ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this.
ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame()
ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this.
bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
ImVec2 MouseClickedPos[5]; // Position at time of clicking
@ -2615,7 +2619,7 @@ struct ImDrawList
// [Internal, used while building lists]
unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
const ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)
ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)
const char* _OwnerName; // Pointer to owner window's name for debugging
ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
@ -2627,7 +2631,7 @@ struct ImDrawList
float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content
// If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui)
ImDrawList(const ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; }
ImDrawList(ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; }
~ImDrawList() { _ClearFreeMemory(); }
IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
@ -2884,6 +2888,7 @@ struct ImFontAtlas
// NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details.
// NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data.
IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin
IMGUI_API const ImWchar* GetGlyphRangesGreek(); // Default + Greek and Coptic
IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters
IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs
IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs
@ -3205,9 +3210,9 @@ struct ImGuiPlatformImeData
namespace ImGui
{
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]
IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]
#else
static inline int GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; }
static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; }
#endif
}
@ -3217,21 +3222,21 @@ namespace ImGui
// OBSOLETED in 1.89 (from August 2022)
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding)
// OBSOLETED in 1.88 (from May 2022)
static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value.
static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value.
// OBSOLETED in 1.86 (from November 2021)
IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper.
// OBSOLETED in 1.85 (from August 2021)
static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }
static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }
// OBSOLETED in 1.81 (from February 2021)
IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items
static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); }
static inline void ListBoxFooter() { EndListBox(); }
// OBSOLETED in 1.79 (from August 2020)
static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry!
static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); }
static inline void ListBoxFooter() { EndListBox(); }
// Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
// [OBSOLETED in 1.78 (from June 2020] Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details.
//-- OBSOLETED in 1.79 (from August 2020)
//static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry!
//-- OBSOLETED in 1.78 (from June 2020): Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details.
//IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f) // OBSOLETED in 1.78 (from June 2020)
//IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020)
//IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020)
@ -3244,7 +3249,7 @@ namespace ImGui
//static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)
//static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)
//static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)
// [OBSOLETED in 1.77 and before]
//-- OBSOLETED in 1.77 and before
//static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } // OBSOLETED in 1.77 (from June 2020)
//static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019)
//static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019)
@ -3252,6 +3257,7 @@ namespace ImGui
//static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019)
//static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018)
//static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018)
//-- OBSOLETED in 1.60 and before
//static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018)
//static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)
//static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
@ -3267,6 +3273,7 @@ namespace ImGui
//static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017): This was misleading and partly broken. You probably want to use the io.WantCaptureMouse flag instead.
//static inline bool IsMouseHoveringAnyWindow() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)
//static inline bool IsMouseHoveringWindow() { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)
//-- OBSOLETED in 1.50 and before
//static inline bool CollapsingHeader(char* label, const char* str_id, bool framed = true, bool default_open = false) { return CollapsingHeader(label, (default_open ? (1 << 5) : 0)); } // OBSOLETED in 1.49
//static inline ImFont*GetWindowFont() { return GetFont(); } // OBSOLETED in 1.48
//static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETED in 1.48
@ -3289,9 +3296,12 @@ enum ImDrawCornerFlags_
ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight,
};
// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022)
typedef int ImGuiKeyModFlags;
enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = ImGuiModFlags_None, ImGuiKeyModFlags_Ctrl = ImGuiModFlags_Ctrl, ImGuiKeyModFlags_Shift = ImGuiModFlags_Shift, ImGuiKeyModFlags_Alt = ImGuiModFlags_Alt, ImGuiKeyModFlags_Super = ImGuiModFlags_Super };
// RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022)
// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place.
typedef ImGuiKeyChord ImGuiModFlags; // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", but you may store only mods in there.
enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super };
//typedef ImGuiKeyChord ImGuiKeyModFlags; // == int
//enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super };
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS

View file

@ -1,4 +1,4 @@
// dear imgui, v1.89 WIP
// dear imgui, v1.89.1 WIP
// (demo code)
// Help:
@ -8,11 +8,15 @@
// Read imgui.cpp for more details, documentation and comments.
// Get the latest version at https://github.com/ocornut/imgui
// -------------------------------------------------
// PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT!
// -------------------------------------------------
// 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.
// 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!
// Also include Metrics! ItemPicker! DebugLog! and other debug features.
// 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.
@ -46,8 +50,10 @@
Index of this file:
// [SECTION] Forward Declarations, Helpers
// [SECTION] Forward Declarations
// [SECTION] Helpers
// [SECTION] Demo Window / ShowDemoWindow()
// - ShowDemoWindow()
// - sub section: ShowDemoWindowWidgets()
// - sub section: ShowDemoWindowLayout()
// - sub section: ShowDemoWindowPopups()
@ -55,6 +61,7 @@ Index of this file:
// - sub section: ShowDemoWindowInputs()
// [SECTION] About Window / ShowAboutWindow()
// [SECTION] Style Editor / ShowStyleEditor()
// [SECTION] User Guide / ShowUserGuide()
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
@ -188,6 +195,19 @@ static void ShowExampleAppWindowTitles(bool* p_open);
static void ShowExampleAppCustomRendering(bool* p_open);
static void ShowExampleMenuFile();
// 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 ShowDemoWindowInputs();
//-----------------------------------------------------------------------------
// [SECTION] Helpers
//-----------------------------------------------------------------------------
// 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)
@ -215,46 +235,16 @@ static void ShowDockingDisabledMessage()
// Helper to wire demo markers located in code to an interactive browser
typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data);
extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback;
extern void* GImGuiDemoMarkerCallbackUserData;
ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL;
void* GImGuiDemoMarkerCallbackUserData = NULL;
extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback;
extern void* GImGuiDemoMarkerCallbackUserData;
ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL;
void* GImGuiDemoMarkerCallbackUserData = NULL;
#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
// 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.");
ImGui::BulletText("CTRL+Tab to select a window.");
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::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::Unindent();
}
//-----------------------------------------------------------------------------
// [SECTION] Demo Window / ShowDemoWindow()
//-----------------------------------------------------------------------------
// - ShowDemoWindow()
// - ShowDemoWindowWidgets()
// - ShowDemoWindowLayout()
// - ShowDemoWindowPopups()
@ -263,29 +253,19 @@ void ImGui::ShowUserGuide()
// - ShowDemoWindowInputs()
//-----------------------------------------------------------------------------
// 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 ShowDemoWindowInputs();
// 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.
// Most 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;
@ -301,7 +281,6 @@ void ImGui::ShowDemoWindow(bool* p_open)
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);
@ -314,7 +293,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
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)
// Dear ImGui Tools/Apps (accessible from the "Tools" menu)
static bool show_app_metrics = false;
static bool show_app_debug_log = false;
static bool show_app_stack_tool = false;
@ -379,10 +358,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
}
// 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);
@ -1165,6 +1142,7 @@ static void ShowDemoWindowWidgets()
IMGUI_DEMO_MARKER("Widgets/Combo");
if (ImGui::TreeNode("Combo"))
{
// Combo Boxes are also called "Dropdown" in other systems
// Expose flags as checkbox for the demo
static ImGuiComboFlags flags = 0;
ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft);
@ -5856,15 +5834,15 @@ static void ShowDemoWindowInputs()
// User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes.
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
const ImGuiKey key_first = ImGuiKey_NamedKey_BEGIN;
const ImGuiKey key_first = (ImGuiKey)ImGuiKey_NamedKey_BEGIN;
#else
struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
const ImGuiKey key_first = 0;
const ImGuiKey key_first = (ImGuiKey)0;
//ImGui::Text("Legacy raw:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
#endif
ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } }
ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } }
ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
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.
@ -6513,6 +6491,40 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::PopItemWidth();
}
//-----------------------------------------------------------------------------
// [SECTION] User Guide / ShowUserGuide()
//-----------------------------------------------------------------------------
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.");
ImGui::BulletText("CTRL+Tab to select a window.");
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::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::Unindent();
}
//-----------------------------------------------------------------------------
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
//-----------------------------------------------------------------------------
@ -6747,72 +6759,76 @@ struct ExampleAppConsole
// 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::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar))
{
if (ImGui::Selectable("Clear")) ClearLog();
ImGui::EndPopup();
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();
// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
// Using a scrollbar or mouse-wheel will take away from the bottom edge.
if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
ImGui::SetScrollHereY(1.0f);
ScrollToBottom = false;
ImGui::PopStyleVar();
}
// 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;
ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))
{
char* s = InputBuf;
@ -7054,63 +7070,66 @@ struct ExampleAppLog
Filter.Draw("Filter", -100.0f);
ImGui::Separator();
ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
if (clear)
Clear();
if (copy)
ImGui::LogToClipboard();
if (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 random access to the result of 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++)
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
const char* buf = Buf.begin();
const char* buf_end = Buf.end();
if (Filter.IsActive())
{
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 have 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++)
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have random access to the result of 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;
ImGui::TextUnformatted(line_start, line_end);
if (Filter.PassFilter(line_start, line_end))
ImGui::TextUnformatted(line_start, line_end);
}
}
clipper.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 have 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();
// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
// Using a scrollbar or mouse-wheel will take away from the bottom edge.
if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
}
ImGui::PopStyleVar();
if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
ImGui::End();
}

View file

@ -1,4 +1,4 @@
// dear imgui, v1.89 WIP
// dear imgui, v1.89.1 WIP
// (drawing and font code)
/*
@ -39,25 +39,12 @@ Index of this file:
#endif
#include <stdio.h> // vsnprintf, sscanf, printf
#if !defined(alloca)
#if defined(__GLIBC__) || defined(__sun) || defined(__APPLE__) || defined(__NEWLIB__)
#include <alloca.h> // alloca (glibc uses <alloca.h>. Note that Cygwin may have _WIN32 defined, so the order matters here)
#elif defined(_WIN32)
#include <malloc.h> // alloca
#if !defined(alloca)
#define alloca _alloca // for clang with MS Codegen
#endif
#else
#include <stdlib.h> // alloca
#endif
#endif
// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (disable: 4127) // condition expression is constant
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead.
#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).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#endif
@ -67,9 +54,6 @@ Index of this file:
#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
#if __has_warning("-Walloca")
#pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged
#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 "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
@ -761,7 +745,8 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
// Temporary buffer
// The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point
ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630
_Data->TempBuffer.reserve_discard(points_count * ((use_texture || !thick_line) ? 3 : 5));
ImVec2* temp_normals = _Data->TempBuffer.Data;
ImVec2* temp_points = temp_normals + points_count;
// Calculate normals (tangents) for each line segment
@ -1009,7 +994,8 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
}
// Compute normals
ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630
_Data->TempBuffer.reserve_discard(points_count);
ImVec2* temp_normals = _Data->TempBuffer.Data;
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
{
const ImVec2& p0 = points[i0];
@ -1303,6 +1289,7 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons
ImVec2 p1 = _Path.back();
if (num_segments == 0)
{
IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated
}
else
@ -1318,6 +1305,7 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
ImVec2 p1 = _Path.back();
if (num_segments == 0)
{
IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated
}
else
@ -1332,6 +1320,7 @@ IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
{
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Obsoleted in 1.82 (from February 2021)
// Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
// ~0 --> ImDrawFlags_RoundCornersAll or 0
if (flags == ~0)
@ -2306,10 +2295,11 @@ void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], fl
void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride)
{
IM_ASSERT_PARANOID(w <= stride);
unsigned char* data = pixels + x + y * stride;
for (int j = h; j > 0; j--, data += stride)
for (int i = 0; i < w; i++)
data[i] = table[data[i]];
for (int j = h; j > 0; j--, data += stride - w)
for (int i = w; i > 0; i--, data++)
*data = table[*data];
}
#ifdef IMGUI_ENABLE_STB_TRUETYPE
@ -2326,7 +2316,7 @@ struct ImFontBuildSrcData
int GlyphsHighest; // Highest requested codepoint
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
ImVector<int> GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap)
ImVector<int> GlyphsList; // Glyph codepoints list (flattened version of GlyphsSet)
};
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
@ -2826,6 +2816,17 @@ const ImWchar* ImFontAtlas::GetGlyphRangesDefault()
return &ranges[0];
}
const ImWchar* ImFontAtlas::GetGlyphRangesGreek()
{
static const ImWchar ranges[] =
{
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x0370, 0x03FF, // Greek and Coptic
0,
};
return &ranges[0];
}
const ImWchar* ImFontAtlas::GetGlyphRangesKorean()
{
static const ImWchar ranges[] =
@ -3338,11 +3339,21 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
return &Glyphs.Data[i];
}
// Wrapping skips upcoming blanks
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
{
while (text < text_end && ImCharIsBlankA(*text))
text++;
if (*text == '\n')
text++;
return text;
}
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const
{
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
// For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!"
// ^ ^ ^ ^ ^__ ^ ^
@ -3354,7 +3365,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
// Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
float line_width = 0.0f;
float word_width = 0.0f;
float blank_width = 0.0f;
@ -3434,6 +3444,10 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
s = next_s;
}
// Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
// +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
if (s == text && text < text_end)
return s + 1;
return s;
}
@ -3458,11 +3472,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol)
{
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width);
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
}
if (s >= word_wrap_eol)
{
@ -3471,13 +3481,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
text_size.y += line_height;
line_width = 0.0f;
word_wrap_eol = NULL;
// Wrapping skips upcoming blanks
while (s < text_end)
{
const char c = *s;
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
}
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
continue;
}
}
@ -3562,15 +3566,25 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
const float scale = size / FontSize;
const float line_height = FontSize * scale;
const bool word_wrap_enabled = (wrap_width > 0.0f);
const char* word_wrap_eol = NULL;
// Fast-forward to first visible line
const char* s = text_begin;
if (y + line_height < clip_rect.y && !word_wrap_enabled)
if (y + line_height < clip_rect.y)
while (y + line_height < clip_rect.y && s < text_end)
{
s = (const char*)memchr(s, '\n', text_end - s);
s = s ? s + 1 : text_end;
const char* line_end = (const char*)memchr(s, '\n', text_end - s);
if (word_wrap_enabled)
{
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA().
// If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPositionA(scale, s, line_end, wrap_width);
s = CalcWordWrapNextLineStartA(s, text_end);
}
else
{
s = line_end ? line_end + 1 : text_end;
}
y += line_height;
}
@ -3602,6 +3616,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
const char* word_wrap_eol = NULL;
while (s < text_end)
{
@ -3609,24 +3624,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol)
{
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x));
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
}
if (s >= word_wrap_eol)
{
x = start_x;
y += line_height;
word_wrap_eol = NULL;
// Wrapping skips upcoming blanks
while (s < text_end)
{
const char c = *s;
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
}
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
continue;
}
}

View file

@ -1,4 +1,4 @@
// dear imgui, v1.89 WIP
// dear imgui, v1.89.1 WIP
// (internal structures/api)
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
@ -153,9 +153,9 @@ typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // E
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressedEx()
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag()
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags
typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressed(), IsMouseClicked(), SetKeyOwner(), SetItemKeyOwner() etc.
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests
@ -213,7 +213,7 @@ namespace ImStb
#endif
// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam.
#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__);
#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__)
#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
@ -312,6 +312,7 @@ namespace ImStb
// - Helper: ImSpan<>, ImSpanAllocator<>
// - Helper: ImPool<>
// - Helper: ImChunkStream<>
// - Helper: ImGuiTextIndex
//-----------------------------------------------------------------------------
// Helpers: Hashing
@ -344,8 +345,11 @@ IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* bu
IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
IMGUI_API void ImStrTrimBlanks(char* str);
IMGUI_API const char* ImStrSkipBlank(const char* str);
IM_MSVC_RUNTIME_CHECKS_OFF
static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
IM_MSVC_RUNTIME_CHECKS_RESTORE
// Helpers: Formatting
IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);
@ -701,6 +705,20 @@ struct ImChunkStream
};
// Helper: ImGuiTextIndex<>
// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.
struct ImGuiTextIndex
{
ImVector<int> LineOffsets;
int EndOffset = 0; // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?)
void clear() { LineOffsets.clear(); EndOffset = 0; }
int size() { return LineOffsets.Size; }
const char* get_line_begin(const char* base, int n) { return base + LineOffsets[n]; }
const char* get_line_end(const char* base, int n) { return base + (n + 1 < LineOffsets.Size ? (LineOffsets[n + 1] - 1) : EndOffset); }
void append(const char* base, int old_size, int new_size);
};
//-----------------------------------------------------------------------------
// [SECTION] ImDrawList support
//-----------------------------------------------------------------------------
@ -742,6 +760,9 @@ struct IMGUI_API ImDrawListSharedData
ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
// [Internal] Temp write buffer
ImVector<ImVec2> TempBuffer;
// [Internal] Lookup tables
ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
float ArcFastRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo()
@ -766,7 +787,10 @@ struct ImDrawDataBuilder
// [SECTION] Widgets support: flags, enums, data structures
//-----------------------------------------------------------------------------
// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin().
// Flags used by upcoming items
// - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags.
// - output: stored in g.LastItemData.InFlags
// Current window shared by all windows.
// This is going to be exposed in imgui.h when stabilized enough.
enum ImGuiItemFlags_
{
@ -780,12 +804,14 @@ enum ImGuiItemFlags_
ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window
ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable()
// Controlled by widget code
ImGuiItemFlags_Inputable = 1 << 8, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
};
// Storage for LastItem data
// Status flags for an already submitted item
// - output: stored in g.LastItemData.StatusFlags
enum ImGuiItemStatusFlags_
{
ImGuiItemStatusFlags_None = 0,
@ -798,6 +824,7 @@ enum ImGuiItemStatusFlags_
ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.
ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing.
ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon)
ImGuiItemStatusFlags_Visible = 1 << 9, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).
#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode)
@ -833,8 +860,10 @@ enum ImGuiButtonFlagsPrivate_
ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held
ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)
ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated
ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used everytime an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags)
ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item
ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,
ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease,
};
@ -864,6 +893,7 @@ enum ImGuiSelectableFlagsPrivate_
ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow.
ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem)
ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27, // Disable padding each side with ItemSpacing * 0.5f
ImGuiSelectableFlags_NoSetKeyOwner = 1 << 28, // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
};
// Extend ImGuiTreeNodeFlags_
@ -1033,7 +1063,7 @@ struct IMGUI_API ImGuiInputTextState
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool Edited; // edited this frame
ImGuiInputTextFlags Flags; // copy of InputText() flags
ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
ImGuiInputTextState() { memset(this, 0, sizeof(*this)); }
void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }
@ -1191,28 +1221,25 @@ struct ImGuiPtrOrIndex
typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN> ImBitArrayForNamedKeys;
// Extend ImGuiKey_
enum ImGuiKeyPrivate_
{
ImGuiKey_LegacyNativeKey_BEGIN = 0,
ImGuiKey_LegacyNativeKey_END = 512,
ImGuiKey_Keyboard_BEGIN = ImGuiKey_NamedKey_BEGIN,
ImGuiKey_Keyboard_END = ImGuiKey_GamepadStart,
ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart,
ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickDown + 1,
ImGuiKey_Aliases_BEGIN = ImGuiKey_MouseLeft,
ImGuiKey_Aliases_END = ImGuiKey_COUNT,
// [Internal] Key ranges
#define ImGuiKey_LegacyNativeKey_BEGIN 0
#define ImGuiKey_LegacyNativeKey_END 512
#define ImGuiKey_Keyboard_BEGIN (ImGuiKey_NamedKey_BEGIN)
#define ImGuiKey_Keyboard_END (ImGuiKey_GamepadStart)
#define ImGuiKey_Gamepad_BEGIN (ImGuiKey_GamepadStart)
#define ImGuiKey_Gamepad_END (ImGuiKey_GamepadRStickDown + 1)
#define ImGuiKey_Aliases_BEGIN (ImGuiKey_MouseLeft)
#define ImGuiKey_Aliases_END (ImGuiKey_MouseWheelY + 1)
// [Internal] Named shortcuts for Navigation
ImGuiKey_NavKeyboardTweakSlow = ImGuiKey_ModCtrl,
ImGuiKey_NavKeyboardTweakFast = ImGuiKey_ModShift,
ImGuiKey_NavGamepadTweakSlow = ImGuiKey_GamepadL1,
ImGuiKey_NavGamepadTweakFast = ImGuiKey_GamepadR1,
ImGuiKey_NavGamepadActivate = ImGuiKey_GamepadFaceDown,
ImGuiKey_NavGamepadCancel = ImGuiKey_GamepadFaceRight,
ImGuiKey_NavGamepadMenu = ImGuiKey_GamepadFaceLeft,
ImGuiKey_NavGamepadInput = ImGuiKey_GamepadFaceUp,
};
// [Internal] Named shortcuts for Navigation
#define ImGuiKey_NavKeyboardTweakSlow ImGuiMod_Ctrl
#define ImGuiKey_NavKeyboardTweakFast ImGuiMod_Shift
#define ImGuiKey_NavGamepadTweakSlow ImGuiKey_GamepadL1
#define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1
#define ImGuiKey_NavGamepadActivate ImGuiKey_GamepadFaceDown
#define ImGuiKey_NavGamepadCancel ImGuiKey_GamepadFaceRight
#define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft
#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp
enum ImGuiInputEventType
{
@ -1262,23 +1289,99 @@ struct ImGuiInputEvent
ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text
ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus
};
bool IgnoredAsSame;
bool AddedByTestEngine;
ImGuiInputEvent() { memset(this, 0, sizeof(*this)); }
};
// Flags for IsKeyPressedEx(). In upcoming feature this will be used more (and IsKeyPressedEx() renamed)
// Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior.
#define ImGuiKeyOwner_Any ((ImGuiID)0) // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
#define ImGuiKeyOwner_None ((ImGuiID)-1) // Require key to have no owner.
typedef ImS16 ImGuiKeyRoutingIndex;
// Routing table entry (sizeof() == 16 bytes)
struct ImGuiKeyRoutingData
{
ImGuiKeyRoutingIndex NextEntryIndex;
ImU16 Mods; // Technically we'd only need 4 bits but for simplify we store ImGuiMod_ values which need 16 bits.
ImU8 RoutingNextScore; // Lower is better (0: perfect score)
ImGuiID RoutingCurr;
ImGuiID RoutingNext;
ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; }
};
// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.
// Stored in main context (1 instance)
struct ImGuiKeyRoutingTable
{
ImGuiKeyRoutingIndex Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[]
ImVector<ImGuiKeyRoutingData> Entries;
ImVector<ImGuiKeyRoutingData> EntriesNext; // Double-buffer to avoid reallocation (could use a shared buffer)
ImGuiKeyRoutingTable() { Clear(); }
void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); }
};
// This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features)
// Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData.
struct ImGuiKeyOwnerData
{
ImGuiID OwnerCurr;
ImGuiID OwnerNext;
bool LockThisFrame; // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame.
bool LockUntilRelease; // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well.
ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_None; LockThisFrame = LockUntilRelease = false; }
};
// Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner()
// Don't mistake with ImGuiInputTextFlags! (for ImGui::InputText() function)
enum ImGuiInputFlags_
{
// Flags for IsKeyPressedEx()
// Flags for IsKeyPressed(), IsMouseClicked(), Shortcut()
ImGuiInputFlags_None = 0,
ImGuiInputFlags_Repeat = 1 << 0, // Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.
ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default)
ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast
ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster
ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak,
// Flags for SetItemKeyOwner()
ImGuiInputFlags_CondHovered = 1 << 4, // Only set if item is hovered (default to both)
ImGuiInputFlags_CondActive = 1 << 5, // Only set if item is active (default to both)
ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
// Flags for SetKeyOwner(), SetItemKeyOwner()
ImGuiInputFlags_LockThisFrame = 1 << 6, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.
ImGuiInputFlags_LockUntilRelease = 1 << 7, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.
// Routing policies for Shortcut() + low-level SetShortcutRouting()
// - The general idea is that several callers register interest in a shortcut, and only one owner gets it.
// - When a policy (other than _RouteAlways) is set, Shortcut() will register itself with SetShortcutRouting(),
// allowing the system to decide where to route the input among other route-aware calls.
// - Shortcut() uses ImGuiInputFlags_RouteFocused by default: meaning that a simple Shortcut() poll
// will register a route and only succeed when parent window is in the focus stack and if no-one
// with a higher priority is claiming the shortcut.
// - Using ImGuiInputFlags_RouteAlways is roughly equivalent to doing e.g. IsKeyPressed(key) + testing mods.
// - Priorities: GlobalHigh > Focused (when owner is active item) > Global > Focused (when focused window) > GlobalLow.
// - Can select only 1 policy among all available.
ImGuiInputFlags_RouteFocused = 1 << 8, // (Default) Register focused route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.
ImGuiInputFlags_RouteGlobalLow = 1 << 9, // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority.
ImGuiInputFlags_RouteGlobal = 1 << 10, // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText).
ImGuiInputFlags_RouteGlobalHigh = 1 << 11, // Register route globally (highest priority: unlikely you need to use that: will interfere with every active items)
ImGuiInputFlags_RouteMask_ = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh, // _Always not part of this!
ImGuiInputFlags_RouteAlways = 1 << 12, // Do not register route, poll keys directly.
ImGuiInputFlags_RouteUnlessBgFocused= 1 << 13, // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.
ImGuiInputFlags_RouteExtraMask_ = ImGuiInputFlags_RouteAlways | ImGuiInputFlags_RouteUnlessBgFocused,
// [Internal] Mask of which function support which flags
ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_,
ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteExtraMask_,
ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,
ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,
};
//-----------------------------------------------------------------------------
@ -1808,15 +1911,13 @@ struct ImGuiContext
ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree.
ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
ImVec2 WheelingWindowRefMousePos;
float WheelingWindowTimer;
float WheelingWindowReleaseTimer;
// Item/widgets state and tracking information
ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
bool HoveredIdAllowOverlap;
bool HoveredIdUsingMouseWheel; // Hovered widget will use mouse wheel. Blocks scrolling the underlying window.
bool HoveredIdPreviousFrameUsingMouseWheel;
bool HoveredIdDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.
float HoveredIdTimer; // Measure contiguous hovering time
float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active
@ -1840,15 +1941,22 @@ struct ImGuiContext
ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation.
float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
// Input Ownership
// [EXPERIMENTAL] Key/Input Ownership + Shortcut Routing system
// - The idea is that instead of "eating" a given key, we can link to an owner.
// - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID.
// - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame().
ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT];
ImGuiKeyRoutingTable KeysRoutingTable;
ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
ImBitArrayForNamedKeys ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.
bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetActiveIdUsingKey(ImGuiKey_Escape); SetActiveIdUsingKey(ImGuiKey_NavGamepadCancel);'
ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'
#endif
// Next window/item data
ImGuiID CurrentFocusScopeId; // == g.FocusScopeStack.back()
ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back()
ImGuiID DebugLocateId; // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location
ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions
ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd)
ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions
@ -1857,7 +1965,7 @@ struct ImGuiContext
ImVector<ImGuiColorMod> ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
ImVector<ImGuiStyleMod> StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
ImVector<ImGuiID> FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - not inherited by Begin(), unless child window
ImVector<ImGuiID> FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
ImVector<ImGuiItemFlags>ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
ImVector<ImGuiGroupData>GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
ImVector<ImGuiPopupData>OpenPopupStack; // Which popups are open (persistent)
@ -1885,7 +1993,7 @@ struct ImGuiContext
ImGuiActivateFlags NavActivateFlags;
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
ImGuiModFlags NavJustMovedToKeyMods;
ImGuiKeyChord NavJustMovedToKeyMods;
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard.
@ -1906,7 +2014,7 @@ struct ImGuiContext
bool NavMoveForwardToNextFrame;
ImGuiNavMoveFlags NavMoveFlags;
ImGuiScrollFlags NavMoveScrollFlags;
ImGuiModFlags NavMoveKeyMods;
ImGuiKeyChord NavMoveKeyMods;
ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down)
ImGuiDir NavMoveDirForDebug;
ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename?
@ -1921,6 +2029,8 @@ struct ImGuiContext
ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab, for reconfiguration (see #4828)
ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab
ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.
ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents
@ -2037,6 +2147,8 @@ struct ImGuiContext
// Debug Tools
ImGuiDebugLogFlags DebugLogFlags;
ImGuiTextBuffer DebugLogBuf;
ImGuiTextIndex DebugLogIndex;
ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker())
ImU8 DebugItemPickerMouseButton;
ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID
@ -2076,12 +2188,11 @@ struct ImGuiContext
HoveredWindowUnderMovingWindow = NULL;
MovingWindow = NULL;
WheelingWindow = NULL;
WheelingWindowTimer = 0.0f;
WheelingWindowReleaseTimer = 0.0f;
DebugHookIdInfo = 0;
HoveredId = HoveredIdPreviousFrame = 0;
HoveredIdAllowOverlap = false;
HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false;
HoveredIdDisabled = false;
HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
ActiveId = 0;
@ -2105,11 +2216,12 @@ struct ImGuiContext
LastActiveIdTimer = 0.0f;
ActiveIdUsingNavDirMask = 0x00;
ActiveIdUsingKeyInputMask.ClearAllBits();
ActiveIdUsingAllKeyboardKeys = false;
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
ActiveIdUsingNavInputMask = 0x00;
#endif
CurrentFocusScopeId = 0;
CurrentItemFlags = ImGuiItemFlags_None;
BeginMenuCount = 0;
@ -2123,7 +2235,7 @@ struct ImGuiContext
NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0;
NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
NavJustMovedToKeyMods = ImGuiModFlags_None;
NavJustMovedToKeyMods = ImGuiMod_None;
NavInputSource = ImGuiInputSource_None;
NavLayer = ImGuiNavLayer_Main;
NavIdIsAlive = false;
@ -2139,12 +2251,14 @@ struct ImGuiContext
NavMoveForwardToNextFrame = false;
NavMoveFlags = ImGuiNavMoveFlags_None;
NavMoveScrollFlags = ImGuiScrollFlags_None;
NavMoveKeyMods = ImGuiModFlags_None;
NavMoveKeyMods = ImGuiMod_None;
NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
NavScoringDebugCount = 0;
NavTabbingDir = 0;
NavTabbingCounter = 0;
ConfigNavWindowingKeyNext = ImGuiMod_Ctrl | ImGuiKey_Tab;
ConfigNavWindowingKeyPrev = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab;
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false;
@ -2207,6 +2321,8 @@ struct ImGuiContext
LogDepthToExpand = LogDepthToExpandDefault = 2;
DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
DebugLocateId = 0;
DebugLocateFrames = 0;
DebugItemPickerActive = false;
DebugItemPickerMouseButton = ImGuiMouseButton_Left;
DebugItemPickerBreakId = 0;
@ -2249,7 +2365,6 @@ struct IMGUI_API ImGuiWindowTempData
ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1)
short NavLayersActiveMask; // Which layers have been written to (result from previous frame)
short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame)
ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending
bool NavHideHighlightOneFrame;
bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f)
@ -2319,6 +2434,7 @@ struct IMGUI_API ImGuiWindow
bool HasCloseButton; // Set when the window has a close button (p_open != NULL)
signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3)
short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
short BeginCountPreviousFrame; // Number of Begin() during the previous frame
short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0.
short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.
short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused.
@ -2376,6 +2492,7 @@ struct IMGUI_API ImGuiWindow
ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1)
ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space
ImGuiID NavRootFocusScopeId; // Focus Scope ID at the time of Begin()
int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy
int MemoryDrawListVtxCapacity;
@ -2845,7 +2962,6 @@ namespace ImGui
IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);
// Scrolling
IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // Use -1.0f on one axis to leave as-is
IMGUI_API void SetScrollX(ImGuiWindow* window, float scroll_x);
IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y);
IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio);
@ -2889,7 +3005,7 @@ namespace ImGui
IMGUI_API ImVec2 GetContentRegionMaxAbs();
IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);
// Parameter stacks
// Parameter stacks (shared)
IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled);
IMGUI_API void PopItemFlag();
@ -2938,38 +3054,77 @@ namespace ImGui
IMGUI_API void SetNavWindow(ImGuiWindow* window);
IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
// Focus Scope (WIP)
// This is generally used to identify a selection set (multiple of which may be in the same window), as selection
// patterns generally need to react (e.g. clear selection) when landing on an item of the set.
IMGUI_API void PushFocusScope(ImGuiID id);
IMGUI_API void PopFocusScope();
inline ImGuiID GetFocusedFocusScope() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; } // Focus scope which is actually active
inline ImGuiID GetFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.NavFocusScopeIdCurrent; } // Focus scope we are outputting into, set by PushFocusScope()
// Inputs
// FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }
inline bool IsNamedKeyOrModKey(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; }
inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }
inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }
inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }
inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key)
{
if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl;
if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift;
if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt;
if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper;
return key;
}
IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key);
IMGUI_API void GetKeyChordName(ImGuiModFlags mods, ImGuiKey key, char* out_buf, int out_buf_size);
IMGUI_API void SetItemUsingMouseWheel();
IMGUI_API void SetActiveIdUsingAllKeyboardKeys();
inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; }
inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); }
inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return ImGuiKey_MouseLeft + button; }
IMGUI_API void GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size);
inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); }
IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
IMGUI_API ImGuiModFlags GetMergedModFlags();
IMGUI_API ImVec2 GetKeyVector2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down);
IMGUI_API float GetNavTweakPressedAmount(ImGuiAxis axis);
IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
IMGUI_API void GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate);
IMGUI_API bool IsKeyPressedEx(ImGuiKey key, ImGuiInputFlags flags = 0);
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87]
#endif
IMGUI_API void SetActiveIdUsingAllKeyboardKeys();
inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
// [EXPERIMENTAL] Low-Level: Key/Input Ownership
// - The idea is that instead of "eating" a given input, we can link to an owner id.
// - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead).
// - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID.
// - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0).
// - Input ownership is automatically released on the frame after a key is released. Therefore:
// - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case).
// - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover).
// - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state.
// - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step.
// Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved.
IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key);
IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id'
inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &GImGui->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }
// [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership
// - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag.
// - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0)
// - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'.
// Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
// - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API.
IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id);
IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat.
IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id);
IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id);
IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id);
// [EXPERIMENTAL] Shortcut Routing
// - ImGuiKeyChord = a ImGuiKey optionally OR-red with ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
// ImGuiKey_C (accepted by functions taking ImGuiKey or ImGuiKeyChord)
// ImGuiKey_C | ImGuiMod_Ctrl (accepted by functions taking ImGuiKeyChord)
// ONLY ImGuiMod_XXX values are legal to 'OR' with an ImGuiKey. You CANNOT 'OR' two ImGuiKey values.
// - When using one of the routing flags (e.g. ImGuiInputFlags_RouteFocused): routes requested ahead of time given a chord (key + modifiers) and a routing policy.
// - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.
// - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route.
// - Multiple read sites may use the same owner id and will all get the granted route.
// - For routing: when owner_id is 0 we use the current Focus Scope ID as a default owner in order to identify our location.
IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id);
IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord);
// Docking
// (some functions are only declared in imgui.cpp, see Docking section)
@ -3023,11 +3178,24 @@ namespace ImGui
IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name);
IMGUI_API void DockBuilderFinish(ImGuiID node_id);
// [EXPERIMENTAL] Focus Scope
// This is generally used to identify a unique input location (for e.g. a selection set)
// There is one per window (automatically set in Begin), but:
// - Selection patterns generally need to react (e.g. clear a selection) when landing on one item of the set.
// So in order to identify a set multiple lists in same window may each need a focus scope.
// If you imagine an hypothetical BeginSelectionGroup()/EndSelectionGroup() api, it would likely call PushFocusScope()/EndFocusScope()
// - Shortcut routing also use focus scope as a default location identifier if an owner is not provided.
// We don't use the ID Stack for this as it is common to want them separate.
IMGUI_API void PushFocusScope(ImGuiID id);
IMGUI_API void PopFocusScope();
inline ImGuiID GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; } // Focus scope we are outputting into, set by PushFocusScope()
// Drag and Drop
IMGUI_API bool IsDragDropActive();
IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
IMGUI_API void ClearDragDrop();
IMGUI_API bool IsDragDropPayloadBeingAccepted();
IMGUI_API void RenderDragDropTargetRect(const ImRect& bb);
// Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)
IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect);
@ -3105,7 +3273,8 @@ namespace ImGui
IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos);
IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar);
IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window);
IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button);
IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker);
IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window);
IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col);
IMGUI_API void TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped);
@ -3211,6 +3380,9 @@ namespace ImGui
IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time!
IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time!
IMGUI_API void DebugLocateItemResolveWithLastItem();
inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); }
inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
@ -3235,6 +3407,7 @@ namespace ImGui
// Obsolete functions
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline void SetItemUsingMouseWheel() { SetItemKeyOwner(ImGuiKey_MouseWheelY); } // Changed in 1.89
inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89
// Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets that used FocusableItemRegister():
@ -3245,6 +3418,9 @@ namespace ImGui
inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()
inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
#endif
} // namespace ImGui

View file

@ -1,4 +1,4 @@
// dear imgui, v1.89 WIP
// dear imgui, v1.89.1 WIP
// (tables and columns code)
/*
@ -936,11 +936,19 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
width_remaining_for_stretched_columns -= 1.0f;
}
// Determine if table is hovered which will be used to flag columns as hovered.
// - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
// but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily
// clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).
// - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
table->HoveredColumnBody = -1;
table->HoveredColumnBorder = -1;
const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
const ImGuiID backup_active_id = g.ActiveId;
g.ActiveId = 0;
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
g.ActiveId = backup_active_id;
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
// Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
@ -1163,8 +1171,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent);
ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit);
ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav);
//GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));
KeepAliveID(column_id);
bool hovered = false, held = false;
bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);
@ -4014,8 +4022,7 @@ void ImGui::EndColumns()
const ImGuiID column_id = columns->ID + ImGuiID(n);
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
KeepAliveID(column_id);
if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test
if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
continue;
bool hovered = false, held = false;

View file

@ -1,4 +1,4 @@
// dear imgui, v1.89 WIP
// dear imgui, v1.89.1 WIP
// (widgets code)
/*
@ -41,7 +41,6 @@ Index of this file:
#include "imgui_internal.h"
// System includes
#include <ctype.h> // toupper
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
@ -542,18 +541,20 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
hovered = false;
// Mouse handling
const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id;
if (hovered)
{
if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
{
// Poll buttons
int mouse_button_clicked = -1;
if ((flags & ImGuiButtonFlags_MouseButtonLeft) && g.IO.MouseClicked[0]) { mouse_button_clicked = 0; }
else if ((flags & ImGuiButtonFlags_MouseButtonRight) && g.IO.MouseClicked[1]) { mouse_button_clicked = 1; }
else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && g.IO.MouseClicked[2]) { mouse_button_clicked = 2; }
if ((flags & ImGuiButtonFlags_MouseButtonLeft) && IsMouseClicked(0, test_owner_id)) { mouse_button_clicked = 0; }
else if ((flags & ImGuiButtonFlags_MouseButtonRight) && IsMouseClicked(1, test_owner_id)) { mouse_button_clicked = 1; }
else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && IsMouseClicked(2, test_owner_id)) { mouse_button_clicked = 2; }
if (mouse_button_clicked != -1 && g.ActiveId != id)
{
if (!(flags & ImGuiButtonFlags_NoSetKeyOwner))
SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id);
if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere))
{
SetActiveID(id, window);
@ -578,9 +579,9 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
if (flags & ImGuiButtonFlags_PressedOnRelease)
{
int mouse_button_released = -1;
if ((flags & ImGuiButtonFlags_MouseButtonLeft) && g.IO.MouseReleased[0]) { mouse_button_released = 0; }
else if ((flags & ImGuiButtonFlags_MouseButtonRight) && g.IO.MouseReleased[1]) { mouse_button_released = 1; }
else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && g.IO.MouseReleased[2]) { mouse_button_released = 2; }
if ((flags & ImGuiButtonFlags_MouseButtonLeft) && IsMouseReleased(0, test_owner_id)) { mouse_button_released = 0; }
else if ((flags & ImGuiButtonFlags_MouseButtonRight) && IsMouseReleased(1, test_owner_id)) { mouse_button_released = 1; }
else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && IsMouseReleased(2, test_owner_id)) { mouse_button_released = 2; }
if (mouse_button_released != -1)
{
const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior
@ -595,7 +596,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
// 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
// Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
if (g.ActiveId == id && (flags & ImGuiButtonFlags_Repeat))
if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, true))
if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat))
pressed = true;
}
@ -641,8 +642,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
const int mouse_button = g.ActiveIdMouseButton;
IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
if (g.IO.MouseDown[mouse_button])
if (IsMouseDown(mouse_button, test_owner_id))
{
held = true;
}
@ -655,7 +655,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
// Report as pressed when releasing the mouse (this is the most common path)
bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2;
bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release>
if (!is_double_click_release && !is_repeating_already)
bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id);
if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned)
pressed = true;
}
ClearActiveID();
@ -932,8 +933,6 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
if (window->SkipItems)
return false;
KeepAliveID(id);
const float bb_frame_width = bb_frame.GetWidth();
const float bb_frame_height = bb_frame.GetHeight();
if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)
@ -965,6 +964,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
// Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
bool held = false;
bool hovered = false;
ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav);
ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v);
@ -1484,11 +1484,7 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
bool item_add = ItemAdd(bb, id);
g.CurrentItemFlags = item_flags_backup;
if (!item_add)
if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav))
return false;
bool hovered, held;
@ -1759,7 +1755,7 @@ bool ImGui::BeginComboPreview()
ImGuiWindow* window = g.CurrentWindow;
ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;
if (window->SkipItems || !window->ClipRect.Overlaps(g.LastItemData.Rect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result
if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible))
return false;
IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag?
if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional)
@ -1903,7 +1899,6 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
//-------------------------------------------------------------------------
// [SECTION] Data Type and Data Formatting Helpers [Internal]
//-------------------------------------------------------------------------
// - PatchFormatStringFloatToInt()
// - DataTypeGetInfo()
// - DataTypeFormatString()
// - DataTypeApplyOp()
@ -1934,30 +1929,6 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] =
};
IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT);
// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f".
// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls.
// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?!
static const char* PatchFormatStringFloatToInt(const char* fmt)
{
if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case.
return "%d";
const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%)
const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user).
if (fmt_end > fmt_start && fmt_end[-1] == 'f')
{
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
if (fmt_start == fmt && fmt_end[0] == 0)
return "%d";
const char* tmp_format;
ImFormatStringToTempBuffer(&tmp_format, NULL, "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision.
return tmp_format;
#else
IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d"
#endif
}
return fmt;
}
const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type)
{
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
@ -2371,8 +2342,6 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
// Default format string when passing NULL
if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.)
format = PatchFormatStringFloatToInt(format);
const bool hovered = ItemHoverable(frame_bb, id);
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
@ -2380,9 +2349,11 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
{
// Tabbing or CTRL-clicking on Drag turns it into an InputText
const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
const bool clicked = (hovered && g.IO.MouseClicked[0]);
const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2);
const bool clicked = hovered && IsMouseClicked(0, id);
const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id));
const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id);
if (make_active && (clicked || double_clicked))
SetKeyOwner(ImGuiKey_MouseLeft, id);
if (make_active && temp_input_allowed)
if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id)
temp_input_is_active = true;
@ -2964,8 +2935,6 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
// Default format string when passing NULL
if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.)
format = PatchFormatStringFloatToInt(format);
const bool hovered = ItemHoverable(frame_bb, id);
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
@ -2973,8 +2942,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
{
// Tabbing or CTRL-clicking on Slider turns it into an input box
const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
const bool clicked = (hovered && g.IO.MouseClicked[0]);
const bool clicked = hovered && IsMouseClicked(0, id);
const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id);
if (make_active && clicked)
SetKeyOwner(ImGuiKey_MouseLeft, id);
if (make_active && temp_input_allowed)
if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)
temp_input_is_active = true;
@ -3131,12 +3102,13 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
// Default format string when passing NULL
if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.)
format = PatchFormatStringFloatToInt(format);
const bool hovered = ItemHoverable(frame_bb, id);
if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id)
const bool clicked = hovered && IsMouseClicked(0, id);
if (clicked || g.NavActivateId == id || g.NavActivateInputId == id)
{
if (clicked)
SetKeyOwner(ImGuiKey_MouseLeft, id);
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
@ -3753,11 +3725,12 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st
{
stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len);
ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW);
state->cursor = state->select_start = state->select_end = 0;
if (text_len <= 0)
return;
if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len))
{
state->cursor = text_len;
state->cursor = state->select_start = state->select_end = text_len;
state->has_preferred_x = 0;
return;
}
@ -4131,24 +4104,24 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
// Declare our inputs
}
if (g.ActiveId == id)
{
// Declare some inputs, the other are registered and polled via Shortcut() routing system.
if (user_clicked)
SetKeyOwner(ImGuiKey_MouseLeft, id);
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
SetActiveIdUsingKey(ImGuiKey_Escape);
SetActiveIdUsingKey(ImGuiKey_NavGamepadCancel);
SetActiveIdUsingKey(ImGuiKey_Home);
SetActiveIdUsingKey(ImGuiKey_End);
SetKeyOwner(ImGuiKey_Home, id);
SetKeyOwner(ImGuiKey_End, id);
if (is_multiline)
{
SetActiveIdUsingKey(ImGuiKey_PageUp);
SetActiveIdUsingKey(ImGuiKey_PageDown);
SetKeyOwner(ImGuiKey_PageUp, id);
SetKeyOwner(ImGuiKey_PageDown, id);
}
if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
{
SetActiveIdUsingKey(ImGuiKey_Tab);
}
SetKeyOwner(ImGuiKey_Tab, id);
}
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
@ -4160,7 +4133,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
clear_active_id = true;
// Lock the decision of whether we are going to take the path displaying the cursor or selection
const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active);
bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active);
bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);
bool value_changed = false;
bool validated = false;
@ -4308,7 +4281,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
// Process other shortcuts/key-presses
bool cancel_edit = false;
bool revert_edit = false;
if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
{
IM_ASSERT(state != NULL);
@ -4318,26 +4291,26 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
const bool is_osx = io.ConfigMacOSXBehaviors;
const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiModFlags_Super | ImGuiModFlags_Shift));
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
const bool is_ctrl_key_only = (io.KeyMods == ImGuiModFlags_Ctrl);
const bool is_shift_key_only = (io.KeyMods == ImGuiModFlags_Shift);
const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiModFlags_Super) : (io.KeyMods == ImGuiModFlags_Ctrl);
const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly;
const bool is_undo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable);
const bool is_redo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable;
const bool is_select_all = is_shortcut_key && IsKeyPressed(ImGuiKey_A);
// Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText)
// Otherwise we could simply assume that we own the keys as we are active.
const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat;
const bool is_cut = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_X, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, id, f_repeat)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
const bool is_copy = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_C, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, id)) && !is_password && (!is_multiline || state->HasSelection());
const bool is_paste = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_V, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, id, f_repeat)) && !is_readonly;
const bool is_undo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Z, id, f_repeat)) && !is_readonly && is_undoable;
const bool is_redo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Y, id, f_repeat) || (is_osx && Shortcut(ImGuiMod_Shortcut | ImGuiMod_Shift | ImGuiKey_Z, id, f_repeat))) && !is_readonly && is_undoable;
const bool is_select_all = Shortcut(ImGuiMod_Shortcut | ImGuiKey_A, id);
// We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true);
const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false));
const bool is_cancel = IsKeyPressed(ImGuiKey_Escape, false) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, false));
const bool is_cancel = Shortcut(ImGuiKey_Escape, id, f_repeat) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, id, f_repeat));
// FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of.
if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
@ -4379,7 +4352,23 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
else if (is_cancel)
{
clear_active_id = cancel_edit = true;
if (flags & ImGuiInputTextFlags_EscapeClearsAll)
{
if (state->CurLenA > 0)
{
revert_edit = true;
}
else
{
render_cursor = render_selection = false;
clear_active_id = true;
}
}
else
{
clear_active_id = revert_edit = true;
render_cursor = render_selection = false;
}
}
else if (is_undo || is_redo)
{
@ -4450,11 +4439,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (g.ActiveId == id)
{
IM_ASSERT(state != NULL);
if (cancel_edit)
if (revert_edit && !is_readonly)
{
// Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
if (!is_readonly && strcmp(buf, state->InitialTextA.Data) != 0)
if (flags & ImGuiInputTextFlags_EscapeClearsAll)
{
// Clear input
apply_new_text = "";
apply_new_text_length = 0;
STB_TEXTEDIT_CHARTYPE empty_string;
stb_textedit_replace(state, &state->Stb, &empty_string, 0);
}
else if (strcmp(buf, state->InitialTextA.Data) != 0)
{
// Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
// Push records into the undo stack so we can CTRL+Z the revert operation itself
apply_new_text = state->InitialTextA.Data;
apply_new_text_length = state->InitialTextA.Size - 1;
@ -4479,7 +4476,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
// If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail.
// This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize).
const bool apply_edit_back_to_user_buffer = !cancel_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
if (apply_edit_back_to_user_buffer)
{
// Apply new value immediately - copy modified buffer back
@ -4573,9 +4570,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
apply_new_text_length = state->CurLenA;
}
}
// Clear temporary user storage
state->Flags = ImGuiInputTextFlags_None;
}
// Copy result to user buffer. This can currently only happen when (g.ActiveId == id)
@ -4869,6 +4863,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
ImStb::STB_TexteditState* stb_state = &state->Stb;
ImStb::StbUndoState* undo_state = &stb_state->undostate;
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
DebugLocateItemOnHover(state->ID);
Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, state->CurLenW, stb_state->cursor, stb_state->select_start, stb_state->select_end);
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), true)) // Visualize undo state
@ -5098,16 +5093,19 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if (BeginPopup("picker"))
{
picker_active_window = g.CurrentWindow;
if (label != label_display_end)
if (g.CurrentWindow->BeginCount == 1)
{
TextEx(label, label_display_end);
Spacing();
picker_active_window = g.CurrentWindow;
if (label != label_display_end)
{
TextEx(label, label_display_end);
Spacing();
}
ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
}
ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
EndPopup();
}
}
@ -5170,7 +5168,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
g.LastItemData.ID = g.ActiveId;
if (value_changed)
if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId
MarkItemEdited(g.LastItemData.ID);
return value_changed;
@ -5558,7 +5556,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)
value_changed = false;
if (value_changed)
if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId
MarkItemEdited(g.LastItemData.ID);
PopID();
@ -6334,6 +6332,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
// We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
ImGuiButtonFlags button_flags = 0;
if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
if (flags & ImGuiSelectableFlags_NoSetKeyOwner) { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; }
if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
@ -6350,7 +6349,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
// - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
// - (2) usage will fail with clipped items
// The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent)
if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)
if (g.NavJustMovedToId == id)
selected = pressed = true;
@ -6359,7 +6358,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
{
if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
{
SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect)
SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)
g.NavDisableHighlight = true;
}
}
@ -6850,7 +6849,7 @@ void ImGui::EndMenuBar()
// To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
// This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering)
const ImGuiNavLayer layer = ImGuiNavLayer_Menu;
IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check
IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary)
FocusWindow(window);
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
@ -6984,9 +6983,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
// The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
if (window->Flags & ImGuiWindowFlags_ChildMenu)
flags |= ImGuiWindowFlags_ChildWindow;
window_flags |= ImGuiWindowFlags_ChildWindow;
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
// We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.
@ -6994,7 +6993,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
if (g.MenusIdSubmittedThisFrame.contains(id))
{
if (menu_is_open)
menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
else
g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values
return menu_is_open;
@ -7006,10 +7005,10 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
ImVec2 label_size = CalcTextSize(label, NULL, true);
// Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
// This is only done for items for the menu set and not the full parent window.
const bool menuset_is_open = IsRootOfOpenMenuSet();
ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open)
g.NavWindow = window;
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
@ -7020,7 +7019,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
BeginDisabled();
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
bool pressed;
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups;
// We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
// Menu inside an horizontal menu bar
@ -7058,7 +7059,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover;
if (menuset_is_open)
g.NavWindow = backed_nav_window;
PopItemFlag();
bool want_open = false;
bool want_close = false;
@ -7131,23 +7132,32 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
PopID();
if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
{
// Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
// Don't reopen/recycle same menu level in the same frame, first close the other menu and yield for a frame.
OpenPopup(label);
return false;
}
menu_is_open |= want_open;
if (want_open)
else if (want_open)
{
menu_is_open = true;
OpenPopup(label);
}
if (menu_is_open)
{
SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
ImGuiLastItemData last_item_in_parent = g.LastItemData;
SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding
menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
PopStyleVar();
if (menu_is_open)
{
// Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu()
// (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck)
g.LastItemData = last_item_in_parent;
if (g.HoveredWindow == window)
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
}
}
else
{
@ -7164,18 +7174,18 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
void ImGui::EndMenu()
{
// Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu).
// A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
// However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
// FIXME: This doesn't work if the parent BeginMenu() is not on a menu.
// Nav: When a left move request our menu failed, close ourselves.
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window)
{
ClosePopupToLevel(g.BeginPopupStack.Size, true);
NavMoveRequestCancel();
}
IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginMenu()/EndMenu() calls
ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert.
if (window->BeginCount == window->BeginCountPreviousFrame)
if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet())
if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical)
{
ClosePopupToLevel(g.BeginPopupStack.Size - 1, true);
NavMoveRequestCancel();
}
EndPopup();
}
@ -7191,10 +7201,10 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
ImVec2 pos = window->DC.CursorPos;
ImVec2 label_size = CalcTextSize(label, NULL, true);
// See BeginMenuEx() for comments about this.
const bool menuset_is_open = IsRootOfOpenMenuSet();
ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open)
g.NavWindow = window;
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
@ -7203,7 +7213,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
if (!enabled)
BeginDisabled();
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover;
// We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover;
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
@ -7215,7 +7226,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f));
PopStyleVar();
RenderText(text_pos, label);
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
RenderText(text_pos, label);
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
}
else
@ -7229,24 +7241,27 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame
float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label);
if (icon_w > 0.0f)
RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
if (shortcut_w > 0.0f)
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
{
PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false);
PopStyleColor();
RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label);
if (icon_w > 0.0f)
RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
if (shortcut_w > 0.0f)
{
PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false);
PopStyleColor();
}
if (selected)
RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f);
}
if (selected)
RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f);
}
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
if (!enabled)
EndDisabled();
PopID();
if (menuset_is_open)
g.NavWindow = backed_nav_window;
PopItemFlag();
return pressed;
}
@ -7573,8 +7588,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
// Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,
// and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window.
const char* tab_name = tab_bar->GetTabName(tab);
const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true;
tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button).x;
const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument);
tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x;
int section_n = TabItemGetSectionIdx(tab);
ImGuiTabBarSection* section = &sections[section_n];
@ -8116,7 +8131,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
if (p_open && !*p_open)
{
ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus);
ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);
return false;
}
@ -8142,7 +8157,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab);
// Calculate tab contents size
ImVec2 size = TabItemCalcSize(label, p_open != NULL);
ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument));
tab->RequestedWidth = -1.0f;
if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
size.x = tab->RequestedWidth = g.NextItemData.Width;
@ -8154,6 +8169,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);
const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument);
const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;
tab->LastFrameVisible = g.FrameCount;
tab->Flags = flags;
@ -8198,7 +8214,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
// and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'.
if (tab_appearing && (!tab_bar_appearing || tab_is_new))
{
ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus);
ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);
if (is_tab_button)
return false;
return tab_contents_visible;
@ -8348,7 +8364,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0;
bool just_closed;
bool text_clipped;
TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);
TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);
if (just_closed && p_open != NULL)
{
*p_open = false;
@ -8407,18 +8423,23 @@ void ImGui::SetTabItemClosed(const char* label)
}
}
ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button)
ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker)
{
ImGuiContext& g = *GImGui;
ImVec2 label_size = CalcTextSize(label, NULL, true);
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);
if (has_close_button)
if (has_close_button_or_unsaved_marker)
size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle.
else
size.x += g.Style.FramePadding.x + 1.0f;
return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);
}
ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window)
{
return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument));
}
void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)
{
// While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it.

View file

@ -582,7 +582,8 @@ if has_plugin("renderer") then
"../external/meshoptimizer/vertexcodec.cpp",
"../external/meshoptimizer/vertexfilter.cpp",
"../external/meshoptimizer/vfetchanalyzer.cpp",
"../external/meshoptimizer/vfetchoptimizer.cpp"
"../external/meshoptimizer/vfetchoptimizer.cpp",
"../src/renderer/editor/voxelizer_ui.cpp"
}
if build_studio then

View file

@ -113,9 +113,9 @@ struct StudioAppImpl final : StudioApp
}
memset(m_imgui_key_map, 0, sizeof(m_imgui_key_map));
m_imgui_key_map[(int)os::Keycode::CTRL] = ImGuiKey_ModCtrl;
m_imgui_key_map[(int)os::Keycode::MENU] = ImGuiKey_ModAlt;
m_imgui_key_map[(int)os::Keycode::SHIFT] = ImGuiKey_ModShift;
m_imgui_key_map[(int)os::Keycode::CTRL] = ImGuiMod_Ctrl;
m_imgui_key_map[(int)os::Keycode::MENU] = ImGuiMod_Alt;
m_imgui_key_map[(int)os::Keycode::SHIFT] = ImGuiMod_Shift;
m_imgui_key_map[(int)os::Keycode::LSHIFT] = ImGuiKey_LeftShift;
m_imgui_key_map[(int)os::Keycode::RSHIFT] = ImGuiKey_RightShift;
m_imgui_key_map[(int)os::Keycode::SPACE] = ImGuiKey_Space;

View file

@ -31,6 +31,7 @@
namespace Lumix
{
void addCube(UniverseView& view, const DVec3& pos, const Vec3& right, const Vec3& up, const Vec3& dir, Color color) {
UniverseView::Vertex* vertices = view.render(true, 24);
const DVec3& cam_pos = view.getViewport().pos;

View file

@ -141,7 +141,7 @@ Frustum ShiftedFrustum::getRelative(const DVec3& origin) const
res.setPlane(Frustum::Planes::TOP, n_top, points[0] + offset);
res.setPlane(Frustum::Planes::BOTTOM, n_bottom, points[2] + offset);
for(Vec3& p : res.points) {
for (Vec3& p : res.points) {
p += offset;
}
@ -192,7 +192,7 @@ void Frustum::transform(const Matrix& mtx)
else if (ys[i] != 0) p = Vec3(0, -ds[i] / ys[i], 0);
else p = Vec3(0, 0, -ds[i] / zs[i]);
Vec3 n = {xs[i], ys[i], zs[i]};
Vec3 n = { xs[i], ys[i], zs[i] };
n = mtx.transformVector(n);
p = mtx.transformPoint(p);
@ -228,7 +228,7 @@ Frustum Frustum::transformed(const Matrix& mtx) const
return res;
}
Sphere Frustum::computeBoundingSphere() const
Sphere Frustum::computeBoundingSphere() const
{
Sphere sphere;
sphere.position = points[0];
@ -987,13 +987,13 @@ LUMIX_ENGINE_API bool getSphereTriangleIntersection(const Vec3& center,
}
static void getProjections(const Vec3& axis,
const Vec3 vertices[8],
float& min,
float& max)
const Vec3 vertices[8],
float& min,
float& max)
{
max = dot(vertices[0], axis);
min = max;
for(int i = 1; i < 8; ++i)
for (int i = 1; i < 8; ++i)
{
float d = dot(vertices[i], axis);
min = minimum(d, min);
@ -1001,6 +1001,68 @@ static void getProjections(const Vec3& axis,
}
}
bool testAABBTriangleCollision(const AABB& aabb, const Vec3& a, const Vec3& b, const Vec3& c) {
const Vec3 aabbcenter = (aabb.max + aabb.min) * 0.5f;
const Vec3 halfboxsize = (aabb.max - aabb.min) * 0.5f;
Vec3 v1 = a - aabbcenter;
Vec3 v2 = b - aabbcenter;
Vec3 v3 = c - aabbcenter;
const Vec3 us[] = {
{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
};
const Vec3 es[] = {
v2 - v1,
v3 - v2,
v1 - v3
};
auto abs = [](const Vec3& v) {
return Vec3(fabsf(v.x), fabsf(v.y), fabsf(v.z));
};
for (Vec3 u : us) {
for (Vec3 e : es) {
Vec3 n = cross(u, e);
float p0 = dot(v1, n);
float p1 = dot(v2, n);
float p2 = dot(v3, n);
float r = dot(halfboxsize, abs(n));
if (maximum(-maximum(p0, p1, p2), minimum(p0, p1, p2)) > r) {
return false;
}
}
}
float min = minimum(v1.x, v2.x, v3.x);
float max = maximum(v1.x, v2.x, v3.x);
if (min > halfboxsize.x || max < -halfboxsize.x) return false;
min = minimum(v1.y, v2.y, v3.y);
max = maximum(v1.y, v2.y, v3.y);
if (min > halfboxsize.y || max < -halfboxsize.y) return false;
min = minimum(v1.z, v2.z, v3.z);
max = maximum(v1.z, v2.z, v3.z);
if (min > halfboxsize.z || max < -halfboxsize.z) return false;
Vec3 normal = cross(es[0], es[1]);
float d = dot(normal, v1);
Vec3 vmin;
vmin.x = normal.x > 0.0f ? -halfboxsize.x : halfboxsize.x;
vmin.y = normal.y > 0.0f ? -halfboxsize.y : halfboxsize.y;
vmin.z = normal.z > 0.0f ? -halfboxsize.z : halfboxsize.z;
if (dot(normal, vmin) > d) return false;
return -dot(normal, vmin) >= d;
}
static bool overlaps(float min1, float max1, float min2, float max2)
{
return (min1 <= min2 && min2 <= max1) || (min2 <= min1 && min1 <= max2);
@ -1014,26 +1076,26 @@ bool testOBBCollision(const AABB& a, const Matrix& mtx_b, const AABB& b)
a.getCorners(Matrix::IDENTITY, box_a_points);
b.getCorners(mtx_b, box_b_points);
const Vec3 normals[] = {Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)};
for(int i = 0; i < 3; i++)
const Vec3 normals[] = { Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1) };
for (int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals[i], box_a_points, box_a_min, box_a_max);
getProjections(normals[i], box_b_points, box_b_min, box_b_max);
if(!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
if (!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}
}
Vec3 normals_b[] = {
normalize(mtx_b.getXVector()), normalize(mtx_b.getYVector()), normalize(mtx_b.getZVector())};
for(int i = 0; i < 3; i++)
normalize(mtx_b.getXVector()), normalize(mtx_b.getYVector()), normalize(mtx_b.getZVector()) };
for (int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals_b[i], box_a_points, box_a_min, box_a_max);
getProjections(normals_b[i], box_b_points, box_b_min, box_b_max);
if(!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
if (!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}

View file

@ -201,5 +201,6 @@ LUMIX_ENGINE_API float getLineSegmentDistance(const Vec3& origin, const Vec3& di
LUMIX_ENGINE_API bool getRayTriangleIntersection(const Vec3& origin, const Vec3& dir, const Vec3& a, const Vec3& b, const Vec3& c, float* out_t);
LUMIX_ENGINE_API bool getSphereTriangleIntersection(const Vec3& center, float radius, const Vec3& v0, const Vec3& v1, const Vec3& v2);
LUMIX_ENGINE_API bool testOBBCollision(const AABB& a, const Matrix& mtx_b, const AABB& b);
LUMIX_ENGINE_API bool testAABBTriangleCollision(const AABB& aabb, const Vec3& a, const Vec3& b, const Vec3& c);
} // namespace Lumix

View file

@ -350,7 +350,7 @@ public:
void erase(const Key& key) {
const u32 pos = findPos(key);
if (m_keys[pos].valid) erase(Iterator{this, pos});
if (pos < m_capacity && m_keys[pos].valid) erase(Iterator{this, pos});
}
bool empty() const { return m_size == 0; }

View file

@ -450,7 +450,9 @@ Vec3 Vec3::operator*(const IVec3& rhs) const {
Vec3 Vec3::operator/(const IVec3& rhs) const {
return Vec3(x / rhs.x, y / rhs.y, z / rhs.z);
}
Vec3 Vec3::operator/(const Vec3& rhs) const {
return Vec3(x / rhs.x, y / rhs.y, z / rhs.z);
}
Vec3 Vec3::operator/(float s) const {
float tmp = 1 / s;
return Vec3(x * tmp, y * tmp, z * tmp);
@ -464,9 +466,9 @@ void Vec3::operator/=(float rhs) {
*this *= 1.0f / rhs;
}
Vec2 Vec3::xz() const {
return {x, z};
}
Vec2 Vec3::xz() const { return {x, z}; }
Vec2 Vec3::yz() const { return {y, z}; }
Vec2 Vec3::xy() const { return {x, y}; }
DVec3::DVec3(float a) : x(a), y(a), z(a) {}
@ -508,6 +510,12 @@ Vec3::Vec3(const DVec3& rhs)
, z((float)rhs.z)
{}
Vec3::Vec3(const IVec3& rhs)
: x((float)rhs.x)
, y((float)rhs.y)
, z((float)rhs.z)
{}
void Vec3::operator*=(float rhs) {
x *= rhs;
y *= rhs;

View file

@ -38,6 +38,7 @@ struct LUMIX_ENGINE_API IVec3 {
explicit IVec3(const Vec3& rhs);
bool operator==(const IVec3& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; }
DVec3 operator *(double i) const;
IVec2 xy() const { return {x, y}; }
i32 x;
i32 y;
@ -104,6 +105,7 @@ struct LUMIX_ENGINE_API Vec3 {
Vec3(float a, float b, float c);
explicit Vec3(float a);
explicit Vec3(const DVec3& rhs);
explicit Vec3(const IVec3& rhs);
float& operator[](u32 i);
float operator[](u32 i) const;
@ -117,6 +119,7 @@ struct LUMIX_ENGINE_API Vec3 {
Vec3 operator*(float s) const;
Vec3 operator*(const Vec3& rhs) const;
Vec3 operator*(const IVec3& rhs) const;
Vec3 operator/(const Vec3& rhs) const;
Vec3 operator/(const IVec3& rhs) const;
Vec3 operator-(float s) const;
Vec3 operator/(float s) const;
@ -124,6 +127,8 @@ struct LUMIX_ENGINE_API Vec3 {
void operator*=(float rhs);
Vec2 xz() const;
Vec2 yz() const;
Vec2 xy() const;
union {
struct {
@ -389,9 +394,8 @@ template <typename T> LUMIX_FORCE_INLINE T minimum(T a) {
return a;
}
template <typename T1, typename... T2> LUMIX_FORCE_INLINE T1 minimum(T1 a, T2... b) {
T1 min_b = minimum(b...);
return a < min_b ? a : min_b;
template <typename T> LUMIX_FORCE_INLINE T minimum(T a, T b) {
return a < b ? a : b;
}
LUMIX_FORCE_INLINE Vec2 minimum(const Vec2& a, const Vec2& b) {
@ -417,6 +421,11 @@ LUMIX_FORCE_INLINE Vec3 minimum(const Vec3& a, const Vec3& b) {
};
}
template <typename T1, typename... T2> LUMIX_FORCE_INLINE T1 minimum(T1 a, T2... b) {
T1 min_b = minimum(b...);
return minimum(a, min_b);
}
template <typename T> LUMIX_FORCE_INLINE T maximum(T a) {
return a;
}

View file

@ -28,6 +28,7 @@
#include "renderer/render_scene.h"
#include "renderer/renderer.h"
#include "renderer/shader.h"
#include "renderer/voxels.h"
namespace Lumix {
@ -629,9 +630,9 @@ static bool doesFlipHandness(const Matrix& mtx) {
return z.z < 0;
}
const FBXImporter::ImportGeometry& FBXImporter::getImportGeometry(const ofbx::Geometry* geom) const
FBXImporter::ImportGeometry& FBXImporter::getImportGeometry(const ofbx::Geometry* geom)
{
for (const ImportGeometry& import_geom : m_geometries) {
for (ImportGeometry& import_geom : m_geometries) {
if (import_geom.fbx == geom) {
return import_geom;
}
@ -671,7 +672,7 @@ void FBXImporter::postprocessMeshes(const ImportConfig& cfg, const char* path)
}
import_geom.unique_vertex_count = (u32)meshopt_generateVertexRemapMulti(import_geom.indices.begin(), nullptr, vertex_count, vertex_count, streams, stream_count);
if (!normals) {
computeNormals(import_geom.computed_normals, vertices, vertex_count, import_geom.indices.begin(), m_allocator);
normals = import_geom.computed_normals.begin();
@ -687,6 +688,8 @@ void FBXImporter::postprocessMeshes(const ImportConfig& cfg, const char* path)
}
});
if (cfg.bake_vertex_ao) bakeVertexAO(cfg);
jobs::forEach(m_meshes.size(), 1, [&](i32 mesh_idx, i32){
ImportMesh& import_mesh = m_meshes[mesh_idx];
import_mesh.vertex_data.clear();
@ -767,6 +770,12 @@ void FBXImporter::postprocessMeshes(const ImportConfig& cfg, const char* path)
if (normals) writePackedVec3(normals[i], transform_matrix, &import_mesh.vertex_data);
if (uvs) writeUV(uvs[i], &import_mesh.vertex_data);
if (cfg.bake_vertex_ao) {
const float ao = import_geom.computed_ao[i];
u32 ao8 = u8(clamp(ao * 255.f, 0.f, 255.f) + 0.5f);
u32 ao32 = ao8 | ao8 << 8 | ao8 << 16 | ao8 << 24;
import_mesh.vertex_data.write(ao32);
}
if (colors) {
if (cfg.vertex_color_is_ao) {
const u8 ao[4] = { u8(colors[i].x * 255.f + 0.5f) };
@ -818,6 +827,7 @@ void FBXImporter::postprocessMeshes(const ImportConfig& cfg, const char* path)
mem += vertex_size;
}
});
for (int mesh_idx = m_meshes.size() - 1; mesh_idx >= 0; --mesh_idx)
{
if (m_meshes[mesh_idx].indices.empty()) m_meshes.swapAndPop(mesh_idx);
@ -1252,7 +1262,7 @@ void FBXImporter::writeMaterials(const char* src, const ImportConfig& cfg)
writeString("shader \"/pipelines/standard.shd\"\n");
if (material.alpha_cutout) writeString("defines {\"ALPHA_CUTOUT\"}\n");
if (material.textures[2].is_valid) writeString("metallic(1.000000)");
if (material.textures[2].is_valid) writeString("uniform(\"Metallic\", 1.000000)");
auto writeTexture = [this](const ImportTexture& texture, u32 idx) {
if (texture.is_valid && idx < 2) {
@ -1732,6 +1742,7 @@ int FBXImporter::getVertexSize(const ofbx::Geometry& geom, bool is_skinned, cons
int size = POSITION_SIZE + NORMAL_SIZE;
if (geom.getUVs()) size += UV_SIZE;
if (cfg.bake_vertex_ao) size += AO_SIZE;
if (geom.getColors() && cfg.import_vertex_colors) size += cfg.vertex_color_is_ao ? AO_SIZE : COLOR_SIZE;
if (hasTangents(geom)) size += TANGENT_SIZE;
if (is_skinned) size += BONE_INDICES_WEIGHTS_SIZE;
@ -2076,6 +2087,11 @@ void FBXImporter::writeMeshes(const char* src, int mesh_idx, const ImportConfig&
write(gpu::AttributeType::FLOAT);
write((u8)2);
}
if (cfg.bake_vertex_ao) {
write(Mesh::AttributeSemantic::AO);
write(gpu::AttributeType::U8);
write((u8)4); // 1+3 because of padding
}
if (geom->getColors() && cfg.import_vertex_colors) {
if (cfg.vertex_color_is_ao) {
write(Mesh::AttributeSemantic::AO);
@ -2212,6 +2228,7 @@ int FBXImporter::getAttributeCount(const ImportMesh& mesh, const ImportConfig& c
{
int count = 2; // position & normals
if (mesh.fbx->getGeometry()->getUVs()) ++count;
if (cfg.bake_vertex_ao) ++count;
if (mesh.fbx->getGeometry()->getColors() && cfg.import_vertex_colors) ++count;
if (hasTangents(*mesh.fbx->getGeometry())) ++count;
if (mesh.is_skinned) count += 2;
@ -2225,6 +2242,53 @@ bool FBXImporter::areIndices16Bit(const ImportMesh& mesh, const ImportConfig& cf
return !(mesh.import && mesh.vertex_data.size() / vertex_size > (1 << 16));
}
void FBXImporter::bakeVertexAO(const ImportConfig& cfg) {
PROFILE_FUNCTION();
AABB aabb(Vec3(FLT_MAX), Vec3(-FLT_MAX));
for (ImportMesh& import_mesh : m_meshes) {
const ofbx::Mesh& mesh = *import_mesh.fbx;
const ofbx::Geometry* geom = import_mesh.fbx->getGeometry();
const i32 vertex_count = geom->getVertexCount();
const ofbx::Vec3* vertices = (ofbx::Vec3*)geom->getVertices();
for (i32 i = 0; i < vertex_count; ++i) {
aabb.addPoint(toLumixVec3(vertices[i]));
}
}
Voxels voxels(m_allocator);
voxels.beginRaster(aabb, 64);
for (ImportMesh& import_mesh : m_meshes) {
const ofbx::Mesh& mesh = *import_mesh.fbx;
const ofbx::Geometry* geom = import_mesh.fbx->getGeometry();
const i32 vertex_count = geom->getVertexCount();
const ofbx::Vec3* vertices = (ofbx::Vec3*)geom->getVertices();
for (i32 i = 0; i < vertex_count; i += 3) {
voxels.raster(toLumixVec3(vertices[i]), toLumixVec3(vertices[i + 1]), toLumixVec3(vertices[i + 2]));
}
}
voxels.computeAO(32);
voxels.blurAO();
for (ImportMesh& import_mesh : m_meshes) {
const ofbx::Mesh& mesh = *import_mesh.fbx;
const ofbx::Geometry* geom = import_mesh.fbx->getGeometry();
ImportGeometry& import_geom = getImportGeometry(geom);
const i32 vertex_count = geom->getVertexCount();
const ofbx::Vec3* vertices = (ofbx::Vec3*)geom->getVertices();
import_geom.computed_ao.reserve(vertex_count);
for (i32 i = 0; i < vertex_count; ++i) {
Vec3 p = toLumixVec3(vertices[i]);
float ao;
bool res = voxels.sampleAO(p, &ao);
ASSERT(res);
import_geom.computed_ao.push(ao);
}
}
}
void FBXImporter::writeModelHeader()
{

View file

@ -42,6 +42,7 @@ struct FBXImporter
bool mikktspace_tangents = false;
bool import_vertex_colors = true;
bool vertex_color_is_ao = false;
bool bake_vertex_ao = false;
Physics physics = Physics::NONE;
u32 lod_count = 1;
float lods_distances[4] = {-10, -100, -1000, -10000};
@ -119,6 +120,7 @@ struct FBXImporter
, vertex_data(allocator)
, computed_tangents(allocator)
, computed_normals(allocator)
, computed_ao(allocator)
{
}
@ -127,6 +129,7 @@ struct FBXImporter
OutputMemoryStream vertex_data;
Array<ofbx::Vec3> computed_tangents;
Array<ofbx::Vec3> computed_normals;
Array<float> computed_ao;
u32 unique_vertex_count;
};
@ -182,7 +185,7 @@ struct FBXImporter
private:
void createAutoLODs(const ImportConfig& cfg, ImportMesh& import_mesh);
bool findTexture(const char* src_dir, const char* ext, FBXImporter::ImportTexture& tex) const;
const ImportGeometry& getImportGeometry(const ofbx::Geometry* geom) const;
ImportGeometry& getImportGeometry(const ofbx::Geometry* geom);
const ImportMesh* getAnyMeshFromBone(const ofbx::Object* node, int bone_idx) const;
void gatherMaterials(const char* fbx_filename, const char* src_dir);
@ -212,6 +215,7 @@ private:
int getAttributeCount(const ImportMesh& mesh, const ImportConfig& cfg) const;
bool areIndices16Bit(const ImportMesh& mesh, const ImportConfig& cfg) const;
void writeModelHeader();
void bakeVertexAO(const ImportConfig& cfg);
IAllocator& m_allocator;
struct FileSystem& m_filesystem;

View file

@ -1822,6 +1822,7 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
bool split = false;
bool create_impostor = false;
bool bake_impostor_normals = false;
bool bake_vertex_ao = false;
bool use_mikktspace = false;
bool force_skin = false;
bool import_vertex_colors = false;
@ -1883,6 +1884,7 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "culling_scale", &meta.culling_scale);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "split", &meta.split);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "bake_impostor_normals", &meta.bake_impostor_normals);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "bake_vertex_ao", &meta.bake_vertex_ao);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "create_impostor", &meta.create_impostor);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "import_vertex_colors", &meta.import_vertex_colors);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "vertex_color_is_ao", &meta.vertex_color_is_ao);
@ -1893,7 +1895,6 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
if (LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "autolod2", &meta.autolod_coefs[2])) meta.autolod_mask |= 4;
if (LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "autolod3", &meta.autolod_coefs[3])) meta.autolod_mask |= 8;
if (LuaWrapper::getField(L, LUA_GLOBALSINDEX, "bake_vertex_ao") != LUA_TNIL) logWarning(path, ": `bake_vertex_ao` deprecated");
if (LuaWrapper::getField(L, LUA_GLOBALSINDEX, "position_error") != LUA_TNIL) logWarning(path, ": `position_error` deprecated");
if (LuaWrapper::getField(L, LUA_GLOBALSINDEX, "rotation_error") != LUA_TNIL) logWarning(path, ": `rotation_error` deprecated");
if (LuaWrapper::getField(L, LUA_GLOBALSINDEX, "clips") == LUA_TTABLE) {
@ -1916,7 +1917,7 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
lua_pop(L, 1);
}
}
lua_pop(L, 4);
lua_pop(L, 3);
char tmp[64];
if (LuaWrapper::getOptionalStringField(L, LUA_GLOBALSINDEX, "physics", Span(tmp))) {
@ -1996,6 +1997,7 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
cfg.mesh_scale = meta.scale;
cfg.bounding_scale = meta.culling_scale;
cfg.physics = meta.physics;
cfg.bake_vertex_ao = meta.bake_vertex_ao;
cfg.import_vertex_colors = meta.import_vertex_colors;
cfg.vertex_color_is_ao = meta.vertex_color_is_ao;
cfg.lod_count = meta.lod_count;
@ -2428,6 +2430,8 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
m_meta = getMeta(model->getPath());
m_meta_res = model->getPath().getHash();
}
ImGuiEx::Label("Bake vertex AO");
ImGui::Checkbox("##impnrm", &m_meta.bake_vertex_ao);
ImGuiEx::Label("Mikktspace tangents");
ImGui::Checkbox("##mikktspace", &m_meta.use_mikktspace);
ImGuiEx::Label("Force skinned");
@ -2570,6 +2574,7 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
if (ImGui::Button(ICON_FA_CHECK "Apply")) {
String src(m_app.getAllocator());
src.cat("create_impostor = ").cat(m_meta.create_impostor ? "true" : "false")
.cat("\nbake_vertex_ao = ").cat(m_meta.bake_vertex_ao ? "true" : "false")
.cat("\nbake_impostor_normals = ").cat(m_meta.bake_impostor_normals ? "true" : "false")
.cat("\nuse_mikktspace = ").cat(m_meta.use_mikktspace ? "true" : "false")
.cat("\nforce_skin = ").cat(m_meta.force_skin ? "true" : "false")

View file

@ -0,0 +1,212 @@
#include "voxelizer_ui.h"
#include "editor/asset_browser.h"
#include "editor/settings.h"
#include "editor/world_editor.h"
#include "engine/engine.h"
#include "engine/profiler.h"
#include "engine/resource_manager.h"
#include "engine/universe.h"
#include "renderer/model.h"
#include <imgui/imgui.h>
// this file is disabled in genie.lua, because this is only a helper class to develop voxelization
namespace Lumix {
VoxelizerUI::~VoxelizerUI() {
m_app.removeAction(&m_toggle_ui);
}
VoxelizerUI::VoxelizerUI(StudioApp& app)
: m_app(app)
, m_debug_triangles(app.getAllocator())
, m_scene(app.getAllocator())
{
m_toggle_ui.init("Voxelizer editor", "Toggle voxelizer editor", "voxelizer_editor", "", true);
m_toggle_ui.func.bind<&VoxelizerUI::toggleOpen>(this);
m_toggle_ui.is_selected.bind<&VoxelizerUI::isOpen>(this);
m_app.addWindowAction(&m_toggle_ui);
}
void VoxelizerUI::visualizeAO() {
if (m_scene.m_ao.empty()) {
m_scene.computeAO(m_ray_count);
}
m_debug_triangles.clear();
const u32 idcs[] = {
0, 1, 2, 1, 2, 3, // +x
4, 5, 6, 5, 6, 7, // -x
0, 1, 4, 1, 4, 5, // +y
2, 3, 6, 3, 6, 7, // -y
0, 2, 4, 2, 4, 6, // +z
1, 3, 5, 3, 5, 7, // -z
};
float voxel_size = m_scene.m_voxel_size;
IVec3 grid_resolution = m_scene.m_grid_resolution;
const Array<float>& ao = m_scene.m_ao;
const OutputMemoryStream& voxels = m_scene.m_voxels;
Vec3 x(voxel_size * 0.5f, 0, 0);
Vec3 y(0, voxel_size * 0.5f, 0);
Vec3 z(0, 0, voxel_size * 0.5f);
Vec3 origin_shift = x * (float)grid_resolution.x + y * (float)grid_resolution.y + z * (float)grid_resolution.z;
for (u32 v = 0, c = (u32)voxels.size(); v < c; ++v) {
if (!voxels[v]) continue;
const i32 k = v / (grid_resolution.x * grid_resolution.y);
const i32 j = (v / grid_resolution.x) % grid_resolution.y;
const i32 i = v % grid_resolution.x;
const Vec3 from = Vec3((float)i * x * 2 + (float)j * y * 2 + (float)k * z * 2) - origin_shift;
const Vec3 points[] = {
from + x + y + z,
from + x + y - z,
from + x - y + z,
from + x - y - z,
from - x + y + z,
from - x + y - z,
from - x - y + z,
from - x - y - z,
};
for (u32 idx : idcs) {
const u8 g = u8(clamp(ao[v] * m_ao_multiplier * 255.f, 0.f, 255.f));
u32 abgr = 0xff000000 | g << 16 | g << 8 | g;
m_debug_triangles.push({points[idx], abgr});
}
}
}
void VoxelizerUI::visualize() {
m_debug_triangles.clear();
const u32 idcs[] = {
0, 1, 2, 1, 2, 3, // +x
4, 5, 6, 5, 6, 7, // -x
0, 1, 4, 1, 4, 5, // +y
2, 3, 6, 3, 6, 7, // -y
0, 2, 4, 2, 4, 6, // +z
1, 3, 5, 3, 5, 7, // -z
};
float voxel_size = m_scene.m_voxel_size;
IVec3 grid_resolution = m_scene.m_grid_resolution;
OutputMemoryStream& voxels = m_scene.m_voxels;
Vec3 x(voxel_size * 0.5f, 0, 0);
Vec3 y(0, voxel_size * 0.5f, 0);
Vec3 z(0, 0, voxel_size * 0.5f);
Vec3 origin_shift = x * (float)grid_resolution.x + y * (float)grid_resolution.y + z * (float)grid_resolution.z;
for (u32 v = 0, c = (u32)voxels.size(); v < c; ++v) {
if (!voxels[v]) continue;
const i32 k = v / (grid_resolution.x * grid_resolution.y);
const i32 j = (v / grid_resolution.x) % grid_resolution.y;
const i32 i = v % grid_resolution.x;
const Vec3 from = Vec3((float)i * x * 2 + (float)j * y * 2 + (float)k * z * 2) - origin_shift;
const Vec3 points[] = {
from + x + y + z,
from + x + y - z,
from + x - y + z,
from + x - y - z,
from - x + y + z,
from - x + y - z,
from - x - y + z,
from - x - y - z,
};
for (u32 idx : idcs) {
m_debug_triangles.push({points[idx], 0xffFFffFF});
}
}
}
void VoxelizerUI::open(const char* path) {
if (m_model) m_model->decRefCount();
m_model = m_app.getEngine().getResourceManager().load<Model>(Path(path));
}
void VoxelizerUI::draw() {
PROFILE_FUNCTION();
WorldEditor& editor = m_app.getWorldEditor();
UniverseView& view = editor.getView();
const Array<EntityRef>& selected = editor.getSelectedEntities();
if (selected.size() != 1) return;
if (m_debug_triangles.empty()) return;
const DVec3 p = editor.getUniverse()->getPosition(selected[0]) - Vec3(0.5f * m_scene.m_voxel_size);
const DVec3 cam_pos = view.getViewport().pos;
UniverseView::Vertex* vertices = view.render(false, m_debug_triangles.size());
for (u32 i = 0, c = m_debug_triangles.size(); i < c; ++i) {
vertices[i].pos = Vec3(p - cam_pos) + m_debug_triangles[i].pos;
vertices[i].abgr = m_debug_triangles[i].color;
}
}
void VoxelizerUI::onWindowGUI() {
if (!m_is_open) return;
ImGui::SetNextWindowSize(ImVec2(300, 300), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Voxelizer", &m_is_open, ImGuiWindowFlags_MenuBar)) {
ImGui::End();
return;
}
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) {
ImGui::SetNextWindowSize(ImVec2(300, 300), ImGuiCond_FirstUseEver);
if (ImGuiEx::BeginResizableMenu("Open", nullptr, true)) {
char buf[LUMIX_MAX_PATH];
static FilePathHash selected_res_hash;
if (m_app.getAssetBrowser().resourceList(Span(buf), selected_res_hash, Model::TYPE, 0, false)) {
open(buf);
m_scene.m_voxels.clear();
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
if (ImGui::InputInt("Ray count", (i32*)&m_ray_count, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue)) {
m_scene.computeAO(m_ray_count);
visualizeAO();
}
if (ImGui::InputInt("Resolution", (i32*)&m_max_resolution, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue)) {
m_scene.m_voxels.clear();
}
if (ImGui::Checkbox("Draw", &m_debug_draw)) {
visualize();
}
if (ImGui::Button("Blur AO")) m_scene.blurAO();
ImGui::DragFloat("AO multiplier", &m_ao_multiplier);
if (ImGui::Button("AO")) {
visualizeAO();
}
if (m_model && m_model->isReady() && m_scene.m_voxels.empty()) {
m_scene.voxelize(*m_model, m_max_resolution);
visualize();
}
if (m_debug_draw) draw();
ImGui::End();
}
void VoxelizerUI::onSettingsLoaded() {
m_is_open = m_app.getSettings().getValue(Settings::GLOBAL, "is_voxelizer_ui_open", false);
}
void VoxelizerUI::onBeforeSettingsSaved() {
m_app.getSettings().setValue(Settings::GLOBAL, "is_voxelizer_ui_open", m_is_open);
}
} // namespace Lumix

View file

@ -0,0 +1,45 @@
#pragma once
#define LUMIX_NO_CUSTOM_CRT
#include "engine/math.h"
#include "editor/studio_app.h"
#include "editor/utils.h"
#include "renderer/voxels.h"
namespace Lumix {
struct VoxelizerUI : StudioApp::GUIPlugin {
VoxelizerUI(StudioApp& app);
~VoxelizerUI();
void onWindowGUI() override;
const char* getName() const override { return "voxelizer"; }
void onSettingsLoaded() override;
void onBeforeSettingsSaved() override;
private:
bool isOpen() const { return m_is_open; }
void toggleOpen() { m_is_open = !m_is_open; }
void open(const char* path);
void draw();
void visualize();
void visualizeAO();
Voxels m_scene;
Action m_toggle_ui;
bool m_is_open = false;
StudioApp& m_app;
struct Model* m_model = nullptr;
u32 m_max_resolution = 32;
bool m_debug_draw = true;
float m_ao_multiplier = 2;
struct Vertex {
Vec3 pos;
u32 color;
};
Array<Vertex> m_debug_triangles;
u32 m_ray_count = 16;
};
} // namespace Lumix

235
src/renderer/voxels.cpp Normal file
View file

@ -0,0 +1,235 @@
#include "engine/profiler.h"
#include "renderer/model.h"
#include "voxels.h"
namespace Lumix {
template <typename F>
static void forEachTriangle(const Mesh& mesh, const F& f) {
const u16* indices16 = (u16*)mesh.indices.data();
const u32* indices32 = (u32*)mesh.indices.data();
const bool areindices16 = mesh.areIndices16();
for (u32 i = 0; i < (u32)mesh.indices_count; i += 3) {
u32 indices[3];
if (areindices16) {
indices[0] = indices16[i];
indices[1] = indices16[i + 1];
indices[2] = indices16[i + 2];
}
else {
indices[0] = indices32[i];
indices[1] = indices32[i + 1];
indices[2] = indices32[i + 2];
}
const Vec3 p0 = mesh.vertices[indices[0]];
const Vec3 p1 = mesh.vertices[indices[1]];
const Vec3 p2 = mesh.vertices[indices[2]];
f(p0, p1, p2);
}
}
Voxels::Voxels(IAllocator& allocator)
: m_allocator(allocator)
, m_voxels(allocator)
, m_ao(allocator)
{
}
void Voxels::set(const Voxels& rhs) {
m_grid_resolution = rhs.m_grid_resolution;
m_voxels = rhs.m_voxels;
m_aabb = rhs.m_aabb;
m_voxel_size = rhs.m_voxel_size;
m_ao.resize(rhs.m_ao.size());
memcpy(m_ao.begin(), rhs.m_ao.begin(), m_ao.byte_size());
}
bool Voxels::sample(const Vec3& p, u8* out) const {
IVec3 ip = IVec3((p - m_aabb.min) / (m_aabb.max - m_aabb.min) * Vec3(m_grid_resolution));
return sample(ip.x, ip.y, ip.z, out);
}
bool Voxels::sampleAO(const Vec3& p, float* out) const {
IVec3 ip = IVec3((p - m_aabb.min) / (m_aabb.max - m_aabb.min) * Vec3(m_grid_resolution));
return sampleAO(ip.x, ip.y, ip.z, out);
}
bool Voxels::sampleAO(i32 x, i32 y, i32 z, float* out) const {
if (x < 0) return false;
if (y < 0) return false;
if (z < 0) return false;
if (x >= m_grid_resolution.x) return false;
if (y >= m_grid_resolution.y) return false;
if (z >= m_grid_resolution.z) return false;
*out = m_ao[x + (y + z * m_grid_resolution.y) * m_grid_resolution.x];
return true;
}
bool Voxels::sample(i32 x, i32 y, i32 z, u8* out) const {
if (x < 0) return false;
if (y < 0) return false;
if (z < 0) return false;
if (x >= m_grid_resolution.x) return false;
if (y >= m_grid_resolution.y) return false;
if (z >= m_grid_resolution.z) return false;
*out = m_voxels[x + (y + z * m_grid_resolution.y) * m_grid_resolution.x];
return true;
}
bool Voxels::castRay(Vec3 p, Vec3 d) const {
Vec3 s = Vec3(m_grid_resolution);
auto sample = [&](Vec3 p){
IVec3 i(p);
return m_voxels[i.x + (i.y + i.z * m_grid_resolution.y) * m_grid_resolution.x];
};
p += d;
while (p.x > 0 && p.y > 0 && p.z > 0 && p.x < s.x && p.y < s.y && p.z < s.z) {
if (sample(p)) return true;
p += d;
}
return false;
}
void Voxels::computeAO(u32 ray_count) {
m_ao.resize(m_grid_resolution.x * m_grid_resolution.y * m_grid_resolution.z);
for (i32 z = 0; z < m_grid_resolution.z; ++z) {
for (i32 y = 0; y < m_grid_resolution.y; ++y) {
for (i32 x = 0; x < m_grid_resolution.x; ++x) {
float ao = 1;
for (u32 d = 0; d < ray_count; ++d) {
Vec3 dir = Vec3(randFloat(), randFloat(), randFloat()) * 2.f - 1.f;
dir /= maximum(fabsf(dir.x), fabsf(dir.y), fabsf(dir.z));
Vec3 p((float)x + 0.5f, (float)y + 0.5f, (float)z + 0.5f);
p += dir;
if (castRay(p, dir)) {
ao -= 1.f / ray_count;
}
}
m_ao[x + (y + z * m_grid_resolution.y) * m_grid_resolution.x] = ao;
}
}
}
}
void Voxels::blurAO() {
Array<float> blurred(m_allocator);
blurred.resize(m_ao.size());
auto sampleAO = [&](i32 x, i32 y, i32 z){
x = clamp(x, 0, m_grid_resolution.x - 1);
y = clamp(y, 0, m_grid_resolution.y - 1);
z = clamp(z, 0, m_grid_resolution.z - 1);
const u32 idx = x + (y + z * m_grid_resolution.y) * m_grid_resolution.x;
return m_ao[idx];
};
for (i32 z = 0; z < m_grid_resolution.z; ++z) {
for (i32 y = 0; y < m_grid_resolution.y; ++y) {
for (i32 x = 0; x < m_grid_resolution.x; ++x) {
float v = 0;
for (i32 c = -1; c <= 1; ++c) {
for (i32 b = -1; b <= 1; ++b) {
for (i32 a = -1; a <= 1; ++a) {
v += sampleAO(x + a, y + b, z + c);
}
}
}
const u32 idx = x + (y + z * m_grid_resolution.y) * m_grid_resolution.x;
blurred[idx] = v / 9.f;
}
}
}
m_ao = blurred.move();
}
void Voxels::beginRaster(AABB aabb, u32 max_res) {
Vec3 max = aabb.max;
Vec3 min = aabb.min;
const float voxel_size = maximum(max.x - min.x, max.y - min.y, max.z - min.z) / max_res;
min -= Vec3(voxel_size * 1.5f);
max += Vec3(voxel_size * 1.5f);
IVec3 resolution = IVec3(i32((max.x - min.x) / voxel_size), i32((max.y - min.y) / voxel_size), i32((max.z - min.z) / voxel_size));
m_voxels.resize(resolution.x * resolution.y * resolution.z);
memset(m_voxels.getMutableData(), 0, m_voxels.size());
m_grid_resolution = resolution;
m_voxel_size = voxel_size;
m_aabb = {min, max};
}
void Voxels::raster(const Vec3& p0, const Vec3& p1, const Vec3& p2) {
auto to_grid = [&](const Vec3& p){
return IVec3((p - m_aabb.min) / m_voxel_size + Vec3(0.5f));
};
auto from_grid = [&](const IVec3& p){
return Vec3(p) * m_voxel_size + Vec3(0.5f * m_voxel_size) + m_aabb.min;
};
auto intersect = [&](const Vec3& p0, const Vec3& p1, const Vec3& p2, IVec3 voxel){
Vec3 center = from_grid(voxel);
Vec3 half(0.5f * m_voxel_size);
return testAABBTriangleCollision(AABB(center - half, center + half), p0, p1, p2);
};
auto setVoxel = [&](i32 i, i32 j, i32 k, u8 value){
if (i < 0 || j < 0 || k < 0) return;
if (i >= m_grid_resolution.x) return;
if (j >= m_grid_resolution.y) return;
if (k >= m_grid_resolution.z) return;
m_voxels[i + j * (m_grid_resolution.x) + k * (m_grid_resolution.x * m_grid_resolution.y)] = value;
};
AABB aabb;
aabb.min = aabb.max = p0;
aabb.addPoint(p1);
aabb.addPoint(p2);
const IVec3 ming = to_grid(aabb.min - Vec3(m_voxel_size));
const IVec3 maxg = to_grid(aabb.max);
for (i32 k = ming.z; k <= maxg.z; ++k) {
for (i32 j = ming.y; j <= maxg.y; ++j) {
for (i32 i = ming.x; i <= maxg.x; ++i) {
if (intersect(p0, p1, p2, IVec3(i, j, k))) {
setVoxel(i, j, k, 1);
}
}
}
}
}
void Voxels::voxelize(Model& model, u32 max_res) {
PROFILE_FUNCTION();
ASSERT(model.isReady());
Vec3 min = Vec3(FLT_MAX);
Vec3 max = Vec3(-FLT_MAX);
for (u32 mesh_idx = 0; mesh_idx < (u32)model.getMeshCount(); ++mesh_idx) {
const Mesh& mesh = model.getMesh(mesh_idx);
forEachTriangle(mesh, [&](const Vec3& p0, const Vec3& p1, const Vec3& p2){
min = minimum(min, p0);
min = minimum(min, p1);
min = minimum(min, p2);
max = maximum(max, p0);
max = maximum(max, p1);
max = maximum(max, p2);
});
}
beginRaster({min, max}, max_res);
for (u32 mesh_idx = 0; mesh_idx < (u32)model.getMeshCount(); ++mesh_idx) {
const Mesh& mesh = model.getMesh(mesh_idx);
forEachTriangle(mesh, [&](const Vec3& p0, const Vec3& p1, const Vec3& p2){
raster(p0, p1, p2);
});
}
}
} // namespace Lumix

33
src/renderer/voxels.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include "engine/array.h"
#include "engine/geometry.h"
#include "engine/math.h"
#include "engine/stream.h"
namespace Lumix {
struct Voxels {
Voxels(IAllocator& allocator);
void set(const Voxels& rhs);
void beginRaster(struct AABB aabb, u32 max_res);
void raster(const Vec3& a, const Vec3& b, const Vec3& c);
void voxelize(struct Model& model, u32 max_res);
void computeAO(u32 ray_count);
void blurAO();
bool castRay(Vec3 p, Vec3 d) const;
bool sample(i32 x, i32 y, i32 z, u8* out) const;
bool sampleAO(i32 x, i32 y, i32 z, float* out) const;
bool sample(const Vec3& p, u8* out) const;
bool sampleAO(const Vec3& p, float* out) const;
IAllocator& m_allocator;
IVec3 m_grid_resolution;
OutputMemoryStream m_voxels;
AABB m_aabb;
Array<float> m_ao;
float m_voxel_size;
};
} // namespace Lumix