From 1ecbc8f95f9b2cdcaeddf7f8594b93d805aa75fa Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 10 Mar 2015 08:05:28 +0100 Subject: update 3rdparty libs --- .../bgfx/3rdparty/ib-compress/writebitstream.h | 14 +- 3rdparty/bgfx/3rdparty/ocornut-imgui/imconfig.h | 9 +- 3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.cpp | 1706 +++++++++++++------- 3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.h | 206 ++- 3rdparty/bgfx/README.md | 8 + 3rdparty/bgfx/examples/common/entry/entry.cpp | 37 + 3rdparty/bgfx/examples/common/entry/entry.h | 1 + .../bgfx/examples/common/entry/entry_android.cpp | 5 + .../bgfx/examples/common/entry/entry_asmjs.cpp | 5 + 3rdparty/bgfx/examples/common/entry/entry_ios.mm | 5 + 3rdparty/bgfx/examples/common/entry/entry_nacl.cpp | 5 + 3rdparty/bgfx/examples/common/entry/entry_osx.mm | 149 +- 3rdparty/bgfx/examples/common/entry/entry_p.h | 2 + 3rdparty/bgfx/examples/common/entry/entry_qnx.cpp | 5 + 3rdparty/bgfx/examples/common/entry/entry_sdl.cpp | 57 +- .../bgfx/examples/common/entry/entry_windows.cpp | 5 + .../bgfx/examples/common/entry/entry_winrt.cpp | 5 + 3rdparty/bgfx/examples/common/entry/entry_x11.cpp | 5 + .../bgfx/examples/common/imgui/ocornut_imgui.cpp | 2 +- 3rdparty/bgfx/scripts/bgfx.lua | 15 +- 3rdparty/bx/include/bx/handlealloc.h | 16 + 3rdparty/genie/README.md | 4 +- 3rdparty/genie/src/actions/make/make_cpp.lua | 6 +- 3rdparty/genie/src/host/premake.c | 8 +- 3rdparty/genie/src/host/scripts.c | 21 +- 3rdparty/mongoose/docs/API.md | 11 +- .../examples/multi_threaded_server/Makefile | 2 +- 3rdparty/mongoose/examples/proxy_server/Makefile | 2 +- 3rdparty/mongoose/examples/web_server/Makefile | 18 +- .../mongoose/examples/web_server/certs/cert.pem | 46 + 3rdparty/mongoose/examples/web_server/web_server.c | 5 + .../mongoose/examples/websocket_ssl_proxy/Makefile | 18 +- 3rdparty/mongoose/mongoose.c | 11 +- 33 files changed, 1687 insertions(+), 727 deletions(-) create mode 100644 3rdparty/mongoose/examples/web_server/certs/cert.pem diff --git a/3rdparty/bgfx/3rdparty/ib-compress/writebitstream.h b/3rdparty/bgfx/3rdparty/ib-compress/writebitstream.h index cc9e9bff247..ccba9b5cdec 100644 --- a/3rdparty/bgfx/3rdparty/ib-compress/writebitstream.h +++ b/3rdparty/bgfx/3rdparty/ib-compress/writebitstream.h @@ -34,14 +34,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define WBS_INLINE __forceinline #else #define WBS_INLINE inline -#endif +#endif -// Very simple bitstream for writing that will grow to accomodate written bits. +// Very simple bitstream for writing that will grow to accomodate written bits. class WriteBitstream { public: - // Construct the bit stream with an initial buffer capacity - should be a multiple of 8 and > 0 + // Construct the bit stream with an initial buffer capacity - should be a multiple of 8 and > 0 WriteBitstream( size_t initialBufferCapacity = 16 ) { m_bufferCursor = @@ -66,7 +66,7 @@ public: // Write a V int to the stream. void WriteVInt( uint32_t value ); - // Get the size in bytes + // Get the size in bytes size_t ByteSize() const { return ( m_size + 7 ) >> 3; } // Finish writing by flushing the buffer. @@ -80,7 +80,7 @@ private: // If we need to grow the buffer. void GrowBuffer(); - // Not copyable + // Not copyable WriteBitstream( const WriteBitstream& ); // Not assignable @@ -96,7 +96,7 @@ private: WBS_INLINE void WriteBitstream::Write( uint32_t value, uint32_t bitCount ) { - m_bitBuffer |= ( static_cast( value ) << ( 64 - m_bitsLeft ) ) & ( m_bitsLeft == 0 ? 0 : 0xFFFFFFFFFFFFFFFF ); + m_bitBuffer |= ( static_cast( value ) << ( 64 - m_bitsLeft ) ) & ( m_bitsLeft == 0 ? 0 : UINT64_C(0xFFFFFFFFFFFFFFFF) ); if ( bitCount > m_bitsLeft ) { @@ -175,4 +175,4 @@ WBS_INLINE void WriteBitstream::GrowBuffer() m_bufferEnd = m_buffer + newBufferSize; } -#endif // -- WRITE_BIT_STREAM_H__ \ No newline at end of file +#endif // -- WRITE_BIT_STREAM_H__ diff --git a/3rdparty/bgfx/3rdparty/ocornut-imgui/imconfig.h b/3rdparty/bgfx/3rdparty/ocornut-imgui/imconfig.h index 02e70da9aa2..bcb0972e72c 100644 --- a/3rdparty/bgfx/3rdparty/ocornut-imgui/imconfig.h +++ b/3rdparty/bgfx/3rdparty/ocornut-imgui/imconfig.h @@ -14,8 +14,13 @@ //---- Define assertion handler. Defaults to calling assert(). //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) -//---- Don't implement default clipboard handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) +//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. +//#define IMGUI_API __declspec( dllexport ) +//#define IMGUI_API __declspec( dllimport ) + +//---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS //---- Include imgui_user.inl at the end of imgui.cpp so you can include code that extends ImGui using its private data/functions. //#define IMGUI_INCLUDE_IMGUI_USER_INL @@ -36,7 +41,7 @@ //---- Freely implement extra functions within the ImGui:: namespace. //---- Declare helpers or widgets implemented in imgui_user.inl or elsewhere, so end-user doesn't need to include multiple files. -//---- e.g. you can create variants of the ImGui::Value() helper for your low-level math types. +//---- e.g. you can create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. /* namespace ImGui { diff --git a/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.cpp b/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.cpp index 349868a9603..9db3eced5b8 100644 --- a/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.cpp +++ b/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.cpp @@ -1,4 +1,4 @@ -// ImGui library v1.32 wip +// ImGui library v1.35 // See ImGui::ShowTestWindow() for sample code. // Read 'Programmer guide' below for notes on how to setup ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui @@ -9,9 +9,9 @@ Index - MISSION STATEMENT - END-USER GUIDE - - PROGRAMMER GUIDE - - API BREAKING CHANGES - - TROUBLESHOOTING & FREQUENTLY ASKED QUESTIONS + - PROGRAMMER GUIDE (read me!) + - API BREAKING CHANGES (read me when you update!) + - FREQUENTLY ASKED QUESTIONS (FAQ) & TROUBLESHOOTING (read me!) - ISSUES & TODO-LIST - CODE - SAMPLE CODE @@ -27,14 +27,14 @@ - minimize screen real-estate usage - minimize setup and maintenance - minimize state storage on user side - - portable, minimize dependencies, run on target (consoles, etc.) + - portable, minimize dependencies, run on target (consoles, phones, etc.) - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything) - - read about immediate-mode GUI principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html + - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: - doesn't look fancy, doesn't animate - limited layout features, intricate layouts are typically crafted in code - - occasionally use statically sized buffers for string manipulations - won't crash, but some long text may be clipped + - occasionally uses statically sized buffers for string manipulations - won't crash, but some long text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction. END-USER GUIDE @@ -63,12 +63,12 @@ PROGRAMMER GUIDE ================ - - your code creates the UI, if your code doesn't run the UI is gone! == dynamic UI, no construction step, less data retention on your side, no state duplication, less sync, less errors. - - call and read ImGui::ShowTestWindow() for user-side sample code + - read the FAQ below this section! + - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs. + - call and read ImGui::ShowTestWindow() for sample code demonstrating most features. - see examples/ folder for standalone sample applications. - customization: use the style editor or PushStyleColor/PushStyleVar to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme). - - getting started: - initialisation: call ImGui::GetIO() and fill the 'Settings' data. - every frame: @@ -96,7 +96,7 @@ // TODO: store your texture pointer/identifier in 'io.Fonts->TexID' // Application main loop - for (;;) + while (true) { // 1) get low-level input // e.g. on Win32, GetKeyboardState(), or poll your events, etc. @@ -129,6 +129,10 @@ Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. + - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth + - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond), kept inline redirection function. + - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. + - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) @@ -164,29 +168,57 @@ - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes - TROUBLESHOOTING & FREQUENTLY ASKED QUESTIONS - ============================================ + FREQUENTLY ASKED QUESTIONS (FAQ) & TROUBLESHOOTING + ================================================== If text or lines are blurry when integrating ImGui in your engine: + - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) - If you are confused about the meaning or use of ID in ImGui: - - many widgets requires state to be carried over multiple frames (most typically ImGui often wants remember what is the "active" widget). - to do so they need an unique ID. unique ID are typically derived from a string label, an indice or a pointer. - when you call Button("OK") the button shows "OK" and also use "OK" as an ID. + A primer on the meaning and use of ID in ImGui: + + - widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget). + to do so they need an unique ID. unique ID are typically derived from a string label, an integer index or a pointer. + + Button("OK"); // Label = "OK", ID = hash of "OK" + Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel" + - ID are uniquely scoped within Windows so no conflict can happen if you have two buttons called "OK" in two different Windows. - within a same Window, use PushID() / PopID() to easily create scopes and avoid ID conflicts. - so if you have a loop creating "multiple" items, you can use PushID() / PopID() with the index of each item, or their pointer, etc. - some functions like TreeNode() implicitly creates a scope for you by calling PushID() - - when dealing with trees, ID are important because you want to preserve the opened/closed state of tree nodes. - depending on your use cases you may want to use strings, indices or pointers as ID. experiment and see what makes more sense! - e.g. When displaying a single object that may change over time, using a static string as ID will preserve your node open/closed state when the targeted object change - e.g. When displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state per object - - when passing a label you can optionally specify extra unique ID information within the same string using "##". This helps solving the simpler collision cases. - e.g. "Label" display "Label" and uses "Label" as ID - e.g. "Label##Foobar" display "Label" and uses "Label##Foobar" as ID - e.g. "##Foobar" display an empty label and uses "##Foobar" as ID - - read articles about immediate-mode ui principles (see web links) to understand the requirement and use of ID. + + - when passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases. + use "##" to pass a complement to the ID that won't be visible to the end-user: + + Button("Play##0"); // Label = "Play", ID = hash of "Play##0" + Button("Play##1"); // Label = "Play", ID = hash of "Play##1" (different from above) + + use "###" to pass a label that isn't part of ID. You can use that to change labels while preserving a constant ID. + + Button("Hello###ID"; // Label = "Hello", ID = hash of "ID" + Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above) + + - use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window: + + Button("Click"); // Label = "Click", ID = hash of "Click" + PushID("node"); + Button("Click"); // Label = "Click", ID = hash of "node" and "Click" + for (int i = 0; i < 100; i++) + { + PushID(i); + Button("Click"); // Label = "Click", ID = hash of "node" and i and "label" + PopID(); + } + PopID(); + PushID(my_ptr); + Button("Click"); // Label = "Click", ID = hash of ptr and "Click" + PopID(); + + so if you have a loop creating multiple items, you can use PushID() / PopID() with the index of each item, or their pointer, etc. + some functions like TreeNode() implicitly creates a scope for you by calling PushID(). + + - when working with trees, ID are used to preserve the opened/closed state of tree nodes. + depending on your use cases you may want to use strings, indices or pointers as ID. + e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change. + e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense! If you want to load a different font than the default (ProggyClean.ttf, size 13) @@ -204,30 +236,7 @@ io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() - - If you want to input Japanese/Chinese/Korean in the text input widget: - - - when loading the font, pass a range that contains the characters you need, e.g.: io.Fonts->GetGlyphRangesJapanese() - - to have the Microsoft IME cursor appears at the right location in the screen, setup a handler for the io.ImeSetInputScreenPosFn function: - - #include - #include - static void ImImpl_ImeSetInputScreenPosFn(int x, int y) - { - // Notify OS Input Method Editor of text input position - HWND hwnd = glfwGetWin32Window(window); - if (HIMC himc = ImmGetContext(hwnd)) - { - COMPOSITIONFORM cf; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y; - cf.dwStyle = CFS_FORCE_POSITION; - ImmSetCompositionWindow(himc, &cf); - } - } - - // Set pointer to handler in ImGuiIO structure - io.ImeSetInputScreenPosFn = ImImpl_ImeSetInputScreenPosFn; + io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code. - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug" @@ -254,8 +263,6 @@ - main: IsItemHovered() returns true even if mouse is active on another widget (e.g. dragging outside of sliders). Maybe not a sensible default? Add parameter or alternate function? - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? - - scrollbar: use relative mouse movement when first-clicking inside of scroll grab box. - - scrollbar: make the grab visible and a minimum size for long scroll regions !- input number: very large int not reliably supported because of int<>float conversions. - input number: optional range min/max for Input*() functions - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) @@ -268,6 +275,7 @@ - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) - columns: columns header to act as button (~sort op) and allow resize/reorder - columns: user specify columns size + - columns: tree node example, removing the last NextColumn() makes a padding difference (it should not) - combo: turn child handling code into pop up helper - combo: contents should extends to fit label if combo widget is small - listbox: multiple selection @@ -281,8 +289,8 @@ - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) - file selection widget -> build the tool in our codebase to improve model-dialog idioms - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() - - slider: initial absolute click is imprecise. change to relative movement slider? hide mouse cursor, allow more precise input using less screen-space. - - text edit: clean up the mess caused by converting UTF-8 <> wchar. the code is rather ineficient right now. + - slider: initial absolute click is imprecise. change to relative movement slider? hide mouse cursor, allow more precise input using less screen-space. same as scrollbar. + - text edit: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now. - text edit: centered text for slider as input text so it matches typical positioning. - text edit: flag to disable live update of the user buffer. - text edit: field resize behavior - field could stretch when being edited? hover tooltip shows more text? @@ -293,6 +301,7 @@ ! style: store rounded corners in texture to use 1 quad per corner (filled and wireframe). so rounding have minor cost. - style: checkbox: padding for "active" color should be a multiplier of the - style: colorbox not always square? + - text: simple markup language for color change? - log: LogButtons() options for specifying depth and/or hiding depth slider - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope) - log: be able to right-click and log a window or tree-node into tty/file/clipboard / generalized context menu? @@ -340,11 +349,10 @@ #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. -#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion chanjges signedness // -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wtype-limits" -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#pragma GCC diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #endif //------------------------------------------------------------------------- @@ -384,8 +392,8 @@ namespace IMGUI_STB_NAMESPACE #endif #include "stb_truetype.h" -#define STB_TEXTEDIT_STRING ImGuiTextEditState -#define STB_TEXTEDIT_CHARTYPE ImWchar +#define STB_TEXTEDIT_STRING ImGuiTextEditState +#define STB_TEXTEDIT_CHARTYPE ImWchar #include "stb_textedit.h" #ifdef __clang__ @@ -401,7 +409,14 @@ using namespace IMGUI_STB_NAMESPACE; // Forward Declarations //------------------------------------------------------------------------- +struct ImGuiColMod; +struct ImGuiStyleMod; struct ImGuiAabb; +struct ImGuiDrawContext; +struct ImGuiTextEditState; +struct ImGuiIniData; +struct ImGuiState; +struct ImGuiWindow; static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, bool repeat = false, bool pressed_on_click = false); static void LogText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL); @@ -413,15 +428,16 @@ static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool static void RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale = 1.0f, bool shadow = false); static void SetFont(ImFont* font); -static bool ItemAdd(const ImGuiAabb& aabb, const ImGuiID* id); +static bool ItemAdd(const ImGuiAabb& bb, const ImGuiID* id); static void ItemSize(ImVec2 size, ImVec2* adjust_start_offset = NULL); -static void ItemSize(const ImGuiAabb& aabb, ImVec2* adjust_start_offset = NULL); +static void ItemSize(const ImGuiAabb& bb, ImVec2* adjust_start_offset = NULL); static void PushColumnClipRect(int column_index = -1); -static bool IsClipped(const ImGuiAabb& aabb); +static bool IsClipped(const ImGuiAabb& bb); -static bool IsMouseHoveringBox(const ImGuiAabb& box); +static bool IsMouseHoveringBox(const ImGuiAabb& bb); static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); +static void Scrollbar(ImGuiWindow* window); static bool CloseWindowButton(bool* p_opened = NULL); static void FocusWindow(ImGuiWindow* window); static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs); @@ -436,17 +452,18 @@ static size_t ImFormatString(char* buf, size_t buf_size, const char* fmt, static size_t ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args); // Helpers: Misc -static ImU32 ImCrc32(const void* data, size_t data_size, ImU32 seed); +static ImU32 ImHash(const void* data, size_t data_size, ImU32 seed); static bool ImLoadFileToMemory(const char* filename, const char* file_open_mode, void** out_file_data, size_t* out_file_size, size_t padding_bytes = 0); -static int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } +static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } +static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } // Helpers: UTF-8 <> wchar static int ImTextCharToUtf8(char* buf, size_t buf_size, unsigned int in_char); // return output UTF-8 bytes count static ptrdiff_t ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count -static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count +static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count static int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -static int ImTextCountUtf8BytesFromWchar(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points +static int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points //----------------------------------------------------------------------------- // Platform dependent default implementations @@ -454,6 +471,7 @@ static int ImTextCountUtf8BytesFromWchar(const ImWchar* in_text, const static const char* GetClipboardTextFn_DefaultImpl(); static void SetClipboardTextFn_DefaultImpl(const char* text); +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); //----------------------------------------------------------------------------- // Texture Atlas data @@ -486,7 +504,8 @@ ImGuiStyle::ImGuiStyle() WindowFillAlphaDefault = 0.70f; // Default alpha of window background, if not specified in ImGui::Begin() TreeNodeSpacing = 22.0f; // Horizontal spacing when entering a tree node ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns - ScrollBarWidth = 16.0f; // Width of the vertical scroll bar + ScrollbarWidth = 16.0f; // Width of the vertical scrollbar + GrabMinSize = 10.0f; // Minimum width/height of a slider or scrollbar grab Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); @@ -529,8 +548,8 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_TooltipBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f); } -// Statically allocated font atlas. This is merely a maneuver to keep its definition at the bottom of the .H file. -// Because we cannot new() at this point (before users may define IO.MemAllocFn) +// Statically allocated font atlas. This is merely a maneuver to keep ImFontAtlas definition at the bottom of the .h file (otherwise it'd be inside ImGuiIO) +// Also we wouldn't be able to new() one at this point, before users may define IO.MemAllocFn. static ImFontAtlas GDefaultFontAtlas; ImGuiIO::ImGuiIO() @@ -557,12 +576,12 @@ ImGuiIO::ImGuiIO() MemFreeFn = free; GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; - ImeSetInputScreenPosFn = NULL; + ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; } // Pass in translated ASCII characters for text input. // - with glfw you can get those from the callback set in glfwSetCharCallback() -// - on Windows you can get those using ToAscii+keyboard state, or via the VM_CHAR message +// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message void ImGuiIO::AddInputCharacter(ImWchar c) { const size_t n = ImStrlenW(InputCharacters); @@ -588,7 +607,7 @@ const float PI = 3.14159265358979323846f; #define IM_INT_MAX 2147483647 #endif -// Play it nice with Windows users. Notepad in 2014 still doesn't display text data with Unix-style \n. +// Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n. #ifdef _MSC_VER #define STR_NEWLINE "\r\n" #else @@ -647,8 +666,7 @@ static char* ImStrdup(const char *str) static size_t ImStrlenW(const ImWchar* str) { size_t n = 0; - while (*str++) - n++; + while (*str++) n++; return n; } @@ -675,7 +693,8 @@ static const char* ImStristr(const char* haystack, const char* needle, const cha } // Pass data_size==0 for zero-terminated string -static ImU32 ImCrc32(const void* data, size_t data_size, ImU32 seed = 0) +// Try to replace with FNV1a hash? +static ImU32 ImHash(const void* data, size_t data_size, ImU32 seed = 0) { static ImU32 crc32_lut[256] = { 0 }; if (!crc32_lut[1]) @@ -689,7 +708,9 @@ static ImU32 ImCrc32(const void* data, size_t data_size, ImU32 seed = 0) crc32_lut[i] = crc; } } - ImU32 crc = ~seed; + + seed = ~seed; + ImU32 crc = seed; const unsigned char* current = (const unsigned char*)data; if (data_size > 0) @@ -702,7 +723,16 @@ static ImU32 ImCrc32(const void* data, size_t data_size, ImU32 seed = 0) { // Zero-terminated string while (unsigned char c = *current++) + { + // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. + // Because this syntax is rarely used we are optimizing for the common case. + // - If we reach ### in the string we discard the hash so far and reset to the seed. + // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. + if (c == '#' && current[0] == '#' && current[1] == '#') + crc = seed; + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; + } } return ~crc; } @@ -841,7 +871,7 @@ struct ImGuiStyleMod // Style modifier, backup of modified data so we can res ImVec2 PreviousValue; }; -struct ImGuiAabb // 2D axis aligned bounding-box +struct ImGuiAabb // 2D axis aligned bounding-box { ImVec2 Min; ImVec2 Max; @@ -887,7 +917,6 @@ struct ImGuiDrawContext ImVector TextWrapPos; ImGuiColorEditMode ColorEditMode; ImGuiStorage* StateStorage; - int OpenNextNode; // FIXME: Reformulate this feature like SetNextWindowCollapsed() API float ColumnsStartX; // Start position from left of window (increased by TreePush/TreePop, etc.) float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. @@ -898,6 +927,7 @@ struct ImGuiDrawContext float ColumnsCellMaxY; bool ColumnsShowBorders; ImGuiID ColumnsSetID; + ImVector ColumnsOffsetsT; // Columns offset normalized 0.0 (far left) -> 1.0 (far right) ImGuiDrawContext() { @@ -909,7 +939,6 @@ struct ImGuiDrawContext LastItemAabb = ImGuiAabb(0.0f,0.0f,0.0f,0.0f); LastItemHovered = false; StateStorage = NULL; - OpenNextNode = -1; ColumnsStartX = 0.0f; ColumnsOffsetX = 0.0f; @@ -925,14 +954,16 @@ struct ImGuiDrawContext // Internal state of the currently focused/edited text input box struct ImGuiTextEditState { + ImGuiID Id; // widget id owning the text state ImWchar Text[1024]; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - char InitialText[1024*3+1]; // backup of end-user buffer at the time of focus (in UTF-8, unconverted) - size_t BufSize; // end-user buffer size, <= 1024 (or increase above) + char InitialText[1024*4+1]; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. + size_t BufSizeA; // end-user buffer size, <= 1024 (or increase above) float Width; // widget width float ScrollX; STB_TexteditState StbState; float CursorAnim; - ImVec2 LastCursorPos; // Cursor position in screen space to be used by IME callback. + ImVec2 InputCursorScreenPos; // Cursor position in screen space to be used by IME callback. bool SelectedAllMouseLock; ImFont* Font; float FontSize; @@ -954,9 +985,11 @@ struct ImGuiTextEditState static void RenderTextScrolledClipped(ImFont* font, float font_size, const char* text, ImVec2 pos_base, float width, float scroll_x); }; +// Data saved in imgui.ini file struct ImGuiIniData { char* Name; + ImGuiID ID; ImVec2 Pos; ImVec2 Size; bool Collapsed; @@ -965,6 +998,7 @@ struct ImGuiIniData ~ImGuiIniData() { if (Name) { ImGui::MemFree(Name); Name = NULL; } } }; +// Main state for ImGui struct ImGuiState { bool Initialized; @@ -987,17 +1021,23 @@ struct ImGuiState ImGuiID ActiveId; ImGuiID ActiveIdPreviousFrame; bool ActiveIdIsAlive; + bool ActiveIdIsFocusedOnly; // Set only by active widget. Denote focus but no active interaction. + ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. Only valid if ActiveID is the "#MOVE" identifier of a window. float SettingsDirtyTimer; ImVector Settings; ImVector ColorModifiers; ImVector StyleModifiers; ImVector FontStack; + ImVec2 SetNextWindowPosVal; - ImGuiSetCondition SetNextWindowPosCond; + ImGuiSetCond SetNextWindowPosCond; ImVec2 SetNextWindowSizeVal; - ImGuiSetCondition SetNextWindowSizeCond; + ImGuiSetCond SetNextWindowSizeCond; bool SetNextWindowCollapsedVal; - ImGuiSetCondition SetNextWindowCollapsedCond; + ImGuiSetCond SetNextWindowCollapsedCond; + bool SetNextWindowFocus; + bool SetNextTreeNodeOpenedVal; + ImGuiSetCond SetNextTreeNodeOpenedCond; // Render ImVector RenderDrawLists; @@ -1008,6 +1048,7 @@ struct ImGuiState ImGuiID SliderAsInputTextId; ImGuiStorage ColorEditModeStorage; // for user selection ImGuiID ActiveComboID; + float ScrollbarClickDeltaToGrabCenter; // distance between mouse and center of grab box, normalized in parent space char Tooltip[1024]; char* PrivateClipboard; // if no custom clipboard handler is defined @@ -1018,9 +1059,18 @@ struct ImGuiState int LogStartDepth; int LogAutoExpandMaxDepth; + // Misc + float FramerateSecPerFrame[120]; // calculate estimate of framerate for user + int FramerateSecPerFrameIdx; + float FramerateSecPerFrameAccum; + ImGuiState() { Initialized = false; + Font = NULL; + FontSize = 0.0f; + FontTexUvWhitePixel = ImVec2(0.0f, 0.0f); + Time = 0.0f; FrameCount = 0; FrameCountRendered = -1; @@ -1028,23 +1078,39 @@ struct ImGuiState FocusedWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; + HoveredId = 0; + ActiveId = 0; + ActiveIdPreviousFrame = 0; ActiveIdIsAlive = false; + ActiveIdIsFocusedOnly = false; + MovedWindow = NULL; SettingsDirtyTimer = 0.0f; + SetNextWindowPosVal = ImVec2(0.0f, 0.0f); SetNextWindowPosCond = 0; SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); SetNextWindowSizeCond = 0; SetNextWindowCollapsedVal = false; SetNextWindowCollapsedCond = 0; + SetNextWindowFocus = false; + SetNextTreeNodeOpenedVal = false; + SetNextTreeNodeOpenedCond = 0; + SliderAsInputTextId = 0; ActiveComboID = 0; + ScrollbarClickDeltaToGrabCenter = 0.0f; memset(Tooltip, 0, sizeof(Tooltip)); PrivateClipboard = NULL; + LogEnabled = false; LogFile = NULL; + LogClipboard = NULL; LogStartDepth = 0; LogAutoExpandMaxDepth = 2; - LogClipboard = NULL; + + memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); + FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameAccum = 0.0f; } }; @@ -1060,7 +1126,8 @@ struct ImGuiWindow ImVec2 Pos; // Position rounded-up to nearest pixel ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed - ImVec2 SizeContentsFit; // Size of contents (extents reach by the drawing cursor) - may not fit within Size. + ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame + ImVec2 SizeContentsCurrent; // Size of contents, currently extending float ScrollY; float NextScrollY; bool ScrollbarY; @@ -1070,13 +1137,14 @@ struct ImGuiWindow bool SkipItems; // == Visible && !Collapsed int AutoFitFrames; bool AutoFitOnlyGrows; - int SetWindowPosAllowFlags; // bit ImGuiSetCondition_*** specify if SetWindowPos() call is allowed with this particular flag. - int SetWindowSizeAllowFlags; // bit ImGuiSetCondition_*** specify if SetWindowSize() call is allowed with this particular flag. - int SetWindowCollapsedAllowFlags; // bit ImGuiSetCondition_*** specify if SetWindowCollapsed() call is allowed with this particular flag. + int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call is allowed with this particular flag. + int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call is allowed with this particular flag. + int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call is allowed with this particular flag. ImGuiDrawContext DC; ImVector IDStack; ImVector ClipRectStack; // Scissoring / clipping rectangle. x1, y1, x2, y2. + ImGuiAabb ClippedAabb; // = ClipRectStack.front() after setup in Begin() int LastFrameDrawn; float ItemWidthDefault; ImGuiStorage StateStorage; @@ -1109,12 +1177,12 @@ public: ImVec2 CursorPos() const { return DC.CursorPos; } float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0 : FontSize() + GImGui->Style.FramePadding.y * 2.0f; } ImGuiAabb TitleBarAabb() const { return ImGuiAabb(Pos, Pos + ImVec2(SizeFull.x, TitleBarHeight())); } - ImVec2 WindowPadding() const { return ((Flags & ImGuiWindowFlags_ChildWindow) && !(Flags & ImGuiWindowFlags_ShowBorders)) ? ImVec2(1,1) : GImGui->Style.WindowPadding; } + ImVec2 WindowPadding() const { return ((Flags & ImGuiWindowFlags_ChildWindow) && !(Flags & ImGuiWindowFlags_ShowBorders)) ? ImVec2(0,0) : GImGui->Style.WindowPadding; } ImU32 Color(ImGuiCol idx, float a=1.f) const { ImVec4 c = GImGui->Style.Colors[idx]; c.w *= GImGui->Style.Alpha * a; return ImGui::ColorConvertFloat4ToU32(c); } ImU32 Color(const ImVec4& col) const { ImVec4 c = col; c.w *= GImGui->Style.Alpha; return ImGui::ColorConvertFloat4ToU32(c); } }; -static ImGuiWindow* GetCurrentWindow() +static inline ImGuiWindow* GetCurrentWindow() { ImGuiState& g = *GImGui; IM_ASSERT(g.CurrentWindow != NULL); // ImGui::NewFrame() hasn't been called yet? @@ -1122,10 +1190,18 @@ static ImGuiWindow* GetCurrentWindow() return g.CurrentWindow; } +static inline ImGuiWindow* GetParentWindow() +{ + ImGuiState& g = *GImGui; + IM_ASSERT(g.CurrentWindowStack.size() >= 2); + return g.CurrentWindowStack[g.CurrentWindowStack.size() - 2]; +} + static void SetActiveId(ImGuiID id) { ImGuiState& g = *GImGui; g.ActiveId = id; + g.ActiveIdIsFocusedOnly = false; } static void RegisterAliveId(const ImGuiID& id) @@ -1251,23 +1327,27 @@ void ImGuiStorage::SetAllInt(int v) //----------------------------------------------------------------------------- // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -ImGuiTextFilter::ImGuiTextFilter() +ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) { - InputBuf[0] = 0; - CountGrep = 0; + if (default_filter) + { + ImFormatString(InputBuf, IM_ARRAYSIZE(InputBuf), "%s", default_filter); + Build(); + } + else + { + InputBuf[0] = 0; + CountGrep = 0; + } } void ImGuiTextFilter::Draw(const char* label, float width) { - ImGuiWindow* window = GetCurrentWindow(); - if (width < 0.0f) - { - ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); - width = ImMax(window->Pos.x + ImGui::GetContentRegionMax().x - window->DC.CursorPos.x - (label_size.x + GImGui->Style.ItemSpacing.x*4), 10.0f); - } - ImGui::PushItemWidth(width); + if (width > 0.0f) + ImGui::PushItemWidth(width); ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); - ImGui::PopItemWidth(); + if (width > 0.0f) + ImGui::PopItemWidth(); Build(); } @@ -1383,12 +1463,13 @@ void ImGuiTextBuffer::append(const char* fmt, ...) ImGuiWindow::ImGuiWindow(const char* name) { Name = ImStrdup(name); - ID = GetID(name); + ID = ImHash(name, 0); IDStack.push_back(ID); + Flags = 0; PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); - SizeContentsFit = ImVec2(0.0f, 0.0f); + SizeContents = SizeContentsCurrent = ImVec2(0.0f, 0.0f); ScrollY = 0.0f; NextScrollY = 0.0f; ScrollbarY = false; @@ -1398,7 +1479,7 @@ ImGuiWindow::ImGuiWindow(const char* name) SkipItems = false; AutoFitFrames = -1; AutoFitOnlyGrows = false; - SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCondition_Always | ImGuiSetCondition_FirstUseThisSession | ImGuiSetCondition_FirstUseEver; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver; LastFrameDrawn = -1; ItemWidthDefault = 0.0f; @@ -1412,6 +1493,7 @@ ImGuiWindow::ImGuiWindow(const char* name) DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList)); new(DrawList) ImDrawList(); + RootWindow = NULL; FocusIdxAllCounter = FocusIdxTabCounter = -1; FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = IM_INT_MAX; @@ -1429,16 +1511,16 @@ ImGuiWindow::~ImGuiWindow() ImGuiID ImGuiWindow::GetID(const char* str) { - const ImGuiID seed = IDStack.empty() ? 0 : IDStack.back(); - const ImGuiID id = ImCrc32(str, 0, seed); + ImGuiID seed = IDStack.back(); + const ImGuiID id = ImHash(str, 0, seed); RegisterAliveId(id); return id; } ImGuiID ImGuiWindow::GetID(const void* ptr) { - const ImGuiID seed = IDStack.empty() ? 0 : IDStack.back(); - const ImGuiID id = ImCrc32(&ptr, sizeof(void*), seed); + ImGuiID seed = IDStack.back(); + const ImGuiID id = ImHash(&ptr, sizeof(void*), seed); RegisterAliveId(id); return id; } @@ -1510,10 +1592,11 @@ void ImGui::MemFree(void* ptr) static ImGuiIniData* FindWindowSettings(const char* name) { ImGuiState& g = *GImGui; + ImGuiID id = ImHash(name, 0); for (size_t i = 0; i != g.Settings.size(); i++) { ImGuiIniData* ini = g.Settings[i]; - if (ImStricmp(ini->Name, name) == 0) + if (ini->ID == id) return ini; } return NULL; @@ -1524,6 +1607,7 @@ static ImGuiIniData* AddWindowSettings(const char* name) ImGuiIniData* ini = (ImGuiIniData*)ImGui::MemAlloc(sizeof(ImGuiIniData)); new(ini) ImGuiIniData(); ini->Name = ImStrdup(name); + ini->ID = ImHash(name, 0); ini->Collapsed = false; ini->Pos = ImVec2(FLT_MAX,FLT_MAX); ini->Size = ImVec2(0,0); @@ -1608,7 +1692,10 @@ static void SaveSettings() const ImGuiIniData* settings = g.Settings[i]; if (settings->Pos.x == FLT_MAX) continue; - fprintf(f, "[%s]\n", settings->Name); + const char* name = settings->Name; + if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + name = p; + fprintf(f, "[%s]\n", name); fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); fprintf(f, "Collapsed=%d\n", settings->Collapsed); @@ -1715,12 +1802,20 @@ void ImGui::NewFrame() for (size_t i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) g.IO.KeysDownTime[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownTime[i] < 0.0f ? 0.0f : g.IO.KeysDownTime[i] + g.IO.DeltaTime) : -1.0f; + // Calculate frame-rate for the user, as a purely luxurious feature + g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; + g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; + g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); + g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame)); + // Clear reference to active widget if the widget isn't alive anymore g.HoveredId = 0; if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) SetActiveId(0); g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = false; + if (!g.ActiveId) + g.MovedWindow = NULL; // Delay saving settings so we don't spam disk too much if (g.SettingsDirtyTimer > 0.0f) @@ -1795,13 +1890,11 @@ void ImGui::NewFrame() } // Mark all windows as not visible - // Clear root windows at this point. for (size_t i = 0; i != g.Windows.size(); i++) { ImGuiWindow* window = g.Windows[i]; window->Visible = false; window->Accessed = false; - window->RootWindow = NULL; } // No window should be open at the beginning of the frame. @@ -1809,7 +1902,7 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); // Create implicit window - we will only render it if the user has added something to it. - ImGui::Begin("Debug", NULL, ImVec2(400,400), -1.0f, ImGuiWindowFlags_NoSavedSettings); + ImGui::Begin("Debug", NULL, ImVec2(400,400)); } // NB: behaviour of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations. @@ -1932,9 +2025,13 @@ void ImGui::Render() g.CurrentWindow->Visible = false; ImGui::End(); - // Select window for move/focus when we're done with all our widgets (we only consider non-child windows here) + // Select window for move/focus when we're done with all our widgets (we use the root window ID here) if (g.ActiveId == 0 && g.HoveredId == 0 && g.HoveredRootWindow != NULL && g.IO.MouseClicked[0]) + { + IM_ASSERT(g.MovedWindow == NULL); + g.MovedWindow = g.HoveredWindow; SetActiveId(g.HoveredRootWindow->GetID("#MOVE")); + } // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet @@ -2010,7 +2107,9 @@ void ImGui::Render() static const char* FindTextDisplayEnd(const char* text, const char* text_end = NULL) { const char* text_display_end = text; - while ((!text_end || text_display_end < text_end) && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) + if (!text_end) + text_end = (const char*)-1; + while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) text_display_end++; return text_display_end; } @@ -2217,7 +2316,7 @@ static void RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale, bool if (shadow && (window->Flags & ImGuiWindowFlags_ShowBorders) != 0) window->DrawList->AddTriangleFilled(a+ImVec2(2,2), b+ImVec2(2,2), c+ImVec2(2,2), window->Color(ImGuiCol_BorderShadow)); - window->DrawList->AddTriangleFilled(a, b, c, window->Color(ImGuiCol_Border)); + window->DrawList->AddTriangleFilled(a, b, c, window->Color(ImGuiCol_Text)); } // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. @@ -2294,7 +2393,9 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) continue; if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0) continue; - ImGuiAabb bb(window->Pos - g.Style.TouchExtraPadding, window->Pos + window->Size + g.Style.TouchExtraPadding); + + // Using the clipped AABB so a child window will typically be clipped by its parent. + ImGuiAabb bb(window->ClippedAabb.Min - g.Style.TouchExtraPadding, window->ClippedAabb.Max + g.Style.TouchExtraPadding); if (bb.Contains(pos)) return window; } @@ -2304,13 +2405,13 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) // Test if mouse cursor is hovering given aabb // NB- Box is clipped by our current clip setting // NB- Expand the aabb to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -static bool IsMouseHoveringBox(const ImGuiAabb& box) +static bool IsMouseHoveringBox(const ImGuiAabb& bb) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); // Clip - ImGuiAabb box_clipped = box; + ImGuiAabb box_clipped = bb; if (!window->ClipRectStack.empty()) { const ImVec4 clip_rect = window->ClipRectStack.back(); @@ -2417,6 +2518,12 @@ bool ImGui::IsItemActive() return false; } +bool ImGui::IsAnyItemActive() +{ + ImGuiState& g = *GImGui; + return g.ActiveId != 0; +} + ImVec2 ImGui::GetItemBoxMin() { ImGuiWindow* window = GetCurrentWindow(); @@ -2467,14 +2574,7 @@ void ImGui::EndTooltip() ImGui::End(); } -void ImGui::BeginChild(ImGuiID id, ImVec2 size, bool border, ImGuiWindowFlags extra_flags) -{ - char str_id[32]; - ImFormatString(str_id, IM_ARRAYSIZE(str_id), "child_%x", id); - ImGui::BeginChild(str_id, size, border, extra_flags); -} - -void ImGui::BeginChild(const char* str_id, ImVec2 size, bool border, ImGuiWindowFlags extra_flags) +bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -2483,6 +2583,7 @@ void ImGui::BeginChild(const char* str_id, ImVec2 size, bool border, ImGuiWindow const ImVec2 content_max = window->Pos + ImGui::GetContentRegionMax(); const ImVec2 cursor_pos = window->Pos + ImGui::GetCursorPos(); + ImVec2 size = size_arg; if (size.x <= 0.0f) { if (size.x == 0.0f) @@ -2503,10 +2604,20 @@ void ImGui::BeginChild(const char* str_id, ImVec2 size, bool border, ImGuiWindow ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s", window->Name, str_id); const float alpha = 1.0f; - ImGui::Begin(title, NULL, size, alpha, flags); + bool ret = ImGui::Begin(title, NULL, size, alpha, flags); if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders; + + return ret; +} + +bool ImGui::BeginChild(ImGuiID id, const ImVec2& size, bool border, ImGuiWindowFlags extra_flags) +{ + char str_id[32]; + ImFormatString(str_id, IM_ARRAYSIZE(str_id), "child_%x", id); + bool ret = ImGui::BeginChild(str_id, size, border, extra_flags); + return ret; } void ImGui::EndChild() @@ -2528,7 +2639,11 @@ void ImGui::EndChild() sz.y = 0; ImGui::End(); + + window = GetCurrentWindow(); + ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + sz); ItemSize(sz); + ItemAdd(bb, NULL); } } @@ -2551,10 +2666,11 @@ void ImGui::EndChildFrame() static ImGuiWindow* FindWindowByName(const char* name) { - // FIXME-OPT: Consider optimizing this (e.g. sorted hashes to window pointers) + // FIXME-OPT: Store sorted hashes -> pointers. ImGuiState& g = *GImGui; - for (size_t i = 0; i != g.Windows.size(); i++) - if (strcmp(g.Windows[i]->Name, name) == 0) + ImGuiID id = ImHash(name, 0); + for (size_t i = 0; i < g.Windows.size(); i++) + if (g.Windows[i]->ID == id) return g.Windows[i]; return NULL; } @@ -2587,9 +2703,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl } else { - window->SetWindowPosAllowFlags &= ~ImGuiSetCondition_FirstUseEver; - window->SetWindowSizeAllowFlags &= ~ImGuiSetCondition_FirstUseEver; - window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCondition_FirstUseEver; + window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver; } if (settings->Pos.x != FLT_MAX) @@ -2608,13 +2724,15 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl } // Push a new ImGui window to add widgets to. -// - A default window called "Debug" is automatically stacked at the beginning of every frame. -// - This can be called multiple times during the frame with the same window name to append content to the same window. -// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). Note that you can use ## to append unique data that isn't displayed, e.g. "My window##1" will use "My window##1" as unique window ID but display "My window" to the user. +// - 'size' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation. +// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. +// - Begin/End can be called multiple times during the frame with the same window name to append content. +// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). +// You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. // - Passing 'bool* p_opened' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. -// - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCondition_FirstUseEver) prior to calling Begin(). -bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alpha, ImGuiWindowFlags flags) +// - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin(). +bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size, float bg_alpha, ImGuiWindowFlags flags) { ImGuiState& g = *GImGui; const ImGuiStyle& style = g.Style; @@ -2651,11 +2769,16 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph ImGui::SetWindowCollapsed(g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond); g.SetNextWindowCollapsedCond = 0; } + if (g.SetNextWindowFocus) + { + ImGui::SetWindowFocus(); + g.SetNextWindowFocus = false; + } // Find parent ImGuiWindow* parent_window = (flags & ImGuiWindowFlags_ChildWindow) != 0 ? g.CurrentWindowStack[g.CurrentWindowStack.size()-2] : NULL; - // Find root (if we are a child window) + // Update known root window (if we are a child window, otherwise window == window->RootWindow) size_t root_idx = g.CurrentWindowStack.size() - 1; while (root_idx > 0) { @@ -2666,8 +2789,8 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph window->RootWindow = g.CurrentWindowStack[root_idx]; // Default alpha - if (fill_alpha < 0.0f) - fill_alpha = style.WindowFillAlphaDefault; + if (bg_alpha < 0.0f) + bg_alpha = style.WindowFillAlphaDefault; // When reusing window again multiple times a frame, just append content (don't need to setup again) const int current_frame = ImGui::GetFrameCount(); @@ -2684,6 +2807,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph window->LastFrameDrawn = current_frame; window->ClipRectStack.resize(0); + // Reset contents size for auto-fitting + window->SizeContents = window->SizeContentsCurrent; + window->SizeContentsCurrent = ImVec2(0.0f, 0.0f); + if (flags & ImGuiWindowFlags_ChildWindow) { parent_window->DC.ChildWindows.push_back(window); @@ -2706,11 +2833,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph // Setup and draw window if (first_begin_of_the_frame) { - // Seed ID stack with our window pointer - window->IDStack.resize(0); - ImGui::PushID(window); + // Reset ID stack + window->IDStack.resize(1); - // Move window (at the beginning of the frame to avoid input lag or sheering) + // Move window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. const ImGuiID move_id = window->GetID("#MOVE"); RegisterAliveId(move_id); if (g.ActiveId == move_id) @@ -2723,11 +2849,13 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) MarkSettingsDirty(); } - FocusWindow(window); + IM_ASSERT(g.MovedWindow != NULL); + FocusWindow(g.MovedWindow); } else { SetActiveId(0); + g.MovedWindow = NULL; // Not strictly necessary but doing it for sanity. } } @@ -2777,7 +2905,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph window->ScrollY = window->NextScrollY; window->ScrollY = ImMax(window->ScrollY, 0.0f); if (!window->Collapsed && !window->SkipItems) - window->ScrollY = ImMin(window->ScrollY, ImMax(0.0f, (float)window->SizeContentsFit.y - window->SizeFull.y)); + window->ScrollY = ImMin(window->ScrollY, ImMax(0.0f, window->SizeContents.y - window->SizeFull.y)); window->NextScrollY = window->ScrollY; // At this point we don't have a clipping rectangle setup yet, so we can test and draw in title bar @@ -2817,12 +2945,12 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph if ((window->Flags & ImGuiWindowFlags_Tooltip) != 0) { // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose. - const ImVec2 size_auto_fit = window->SizeContentsFit + style.WindowPadding - ImVec2(0.0f, style.ItemSpacing.y); + const ImVec2 size_auto_fit = window->SizeContents + style.WindowPadding - ImVec2(0.0f, style.ItemSpacing.y); window->SizeFull = size_auto_fit; } else { - const ImVec2 size_auto_fit = ImClamp(window->SizeContentsFit + style.AutoFitPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - style.AutoFitPadding)); + const ImVec2 size_auto_fit = ImClamp(window->SizeContents + style.AutoFitPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - style.AutoFitPadding)); if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) { // Don't continuously mark settings as dirty, the size of the window doesn't need to be stored. @@ -2842,7 +2970,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph { // Manual resize grip const ImGuiAabb resize_aabb(window->Aabb().GetBR()-ImVec2(18,18), window->Aabb().GetBR()); - const ImGuiID resize_id = window->GetID("##RESIZE"); + const ImGuiID resize_id = window->GetID("#RESIZE"); bool hovered, held; ButtonBehaviour(resize_aabb, resize_id, &hovered, &held, true); resize_col = window->Color(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); @@ -2870,19 +2998,19 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph } // Scrollbar - window->ScrollbarY = (window->SizeContentsFit.y > window->Size.y) && !(window->Flags & ImGuiWindowFlags_NoScrollbar); + window->ScrollbarY = (window->SizeContents.y > window->Size.y) && !(window->Flags & ImGuiWindowFlags_NoScrollbar); // Window background - if (fill_alpha > 0.0f) + if (bg_alpha > 0.0f) { if ((window->Flags & ImGuiWindowFlags_ComboBox) != 0) - window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_ComboBg, fill_alpha), window_rounding); + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_ComboBg, bg_alpha), window_rounding); else if ((window->Flags & ImGuiWindowFlags_Tooltip) != 0) - window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_TooltipBg, fill_alpha), window_rounding); + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_TooltipBg, bg_alpha), window_rounding); else if ((window->Flags & ImGuiWindowFlags_ChildWindow) != 0) - window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size-ImVec2(window->ScrollbarY?style.ScrollBarWidth:0.0f,0.0f), window->Color(ImGuiCol_ChildWindowBg, fill_alpha), window_rounding, window->ScrollbarY ? (1|8) : (0xF)); + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size-ImVec2(window->ScrollbarY?style.ScrollbarWidth:0.0f,0.0f), window->Color(ImGuiCol_ChildWindowBg, bg_alpha), window_rounding, window->ScrollbarY ? (1|8) : (0xF)); else - window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_WindowBg, fill_alpha), window_rounding); + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_WindowBg, bg_alpha), window_rounding); } // Title bar @@ -2900,38 +3028,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph // Scrollbar if (window->ScrollbarY) - { - ImGuiAabb scrollbar_bb(window->Aabb().Max.x - style.ScrollBarWidth, title_bar_aabb.Max.y+1, window->Aabb().Max.x, window->Aabb().Max.y-1); - //window->DrawList->AddLine(scrollbar_bb.GetTL(), scrollbar_bb.GetBL(), g.Colors[ImGuiCol_Border]); - window->DrawList->AddRectFilled(scrollbar_bb.Min, scrollbar_bb.Max, window->Color(ImGuiCol_ScrollbarBg)); - scrollbar_bb.Expand(ImVec2(-3,-3)); - - const float grab_size_y_norm = ImSaturate(window->Size.y / ImMax(window->SizeContentsFit.y, window->Size.y)); - const float grab_size_y = scrollbar_bb.GetHeight() * grab_size_y_norm; - - // Handle input right away (none of the code above is relying on scrolling position) - bool held = false; - bool hovered = false; - if (grab_size_y_norm < 1.0f) - { - const ImGuiID scrollbar_id = window->GetID("#SCROLLY"); - ButtonBehaviour(scrollbar_bb, scrollbar_id, &hovered, &held, true); - if (held) - { - g.HoveredId = scrollbar_id; - const float pos_y_norm = ImSaturate((g.IO.MousePos.y - (scrollbar_bb.Min.y + grab_size_y*0.5f)) / (scrollbar_bb.GetHeight() - grab_size_y)) * (1.0f - grab_size_y_norm); - window->ScrollY = (float)(int)(pos_y_norm * window->SizeContentsFit.y); - window->NextScrollY = window->ScrollY; - } - } - - // Normalized height of the grab - const float pos_y_norm = ImSaturate(window->ScrollY / ImMax(0.0f, window->SizeContentsFit.y)); - const ImU32 grab_col = window->Color(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); - window->DrawList->AddRectFilled( - ImVec2(scrollbar_bb.Min.x, ImLerp(scrollbar_bb.Min.y, scrollbar_bb.Max.y, pos_y_norm)), - ImVec2(scrollbar_bb.Max.x, ImLerp(scrollbar_bb.Min.y, scrollbar_bb.Max.y, pos_y_norm + grab_size_y_norm)), grab_col); - } + Scrollbar(window); // Render resize grip // (after the input handling so we don't have a frame of latency) @@ -2975,10 +3072,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPos.y; window->DC.TreeDepth = 0; window->DC.StateStorage = &window->StateStorage; - window->DC.OpenNextNode = -1; - // Reset contents size for auto-fitting - window->SizeContentsFit = ImVec2(0.0f, 0.0f); if (window->AutoFitFrames > 0) window->AutoFitFrames--; @@ -2999,6 +3093,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph const ImVec2 text_max = window->Pos + ImVec2(window->Size.x - (p_opened ? (title_bar_aabb.GetHeight()-3) : style.FramePadding.x), style.FramePadding.y*2 + text_size.y); RenderTextClipped(text_min, name, NULL, &text_size, text_max); } + + // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() + window->ClippedAabb = window->Aabb(); + window->ClippedAabb.Clip(window->ClipRectStack.front()); } // Inner clipping rectangle @@ -3006,7 +3104,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph const ImGuiAabb title_bar_aabb = window->TitleBarAabb(); ImVec4 clip_rect(title_bar_aabb.Min.x+0.5f+window->WindowPadding().x*0.5f, title_bar_aabb.Max.y+0.5f, window->Aabb().Max.x+0.5f-window->WindowPadding().x*0.5f, window->Aabb().Max.y-1.5f); if (window->ScrollbarY) - clip_rect.z -= style.ScrollBarWidth; + clip_rect.z -= style.ScrollbarWidth; PushClipRect(clip_rect); // Clear 'accessed' flag last thing @@ -3018,8 +3116,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, ImVec2 size, float fill_alph if (flags & ImGuiWindowFlags_ChildWindow) { IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); + window->Collapsed = parent_window && parent_window->Collapsed; + const ImVec4 clip_rect_t = window->ClipRectStack.back(); - window->Collapsed = (clip_rect_t.x >= clip_rect_t.z || clip_rect_t.y >= clip_rect_t.w); + window->Collapsed |= (clip_rect_t.x >= clip_rect_t.z || clip_rect_t.y >= clip_rect_t.w); // We also hide the window from rendering because we've already added its border to the command list. // (we could perform the check earlier in the function but it is simpler at this point) @@ -3049,17 +3149,92 @@ void ImGui::End() ImGui::LogFinish(); // Pop - // NB: we don't clear 'window->RootWindow' yet, it will be used then cleared in NewFrame() + // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin(). g.CurrentWindowStack.pop_back(); g.CurrentWindow = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); } +// Vertical scrollbar +// The entire piece of code below is rather confusing because: +// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) +// - We store values as ratio and in a form that allows the window content to change while we are holding on a scrollbar +static void Scrollbar(ImGuiWindow* window) +{ + ImGuiState& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID("#SCROLLY"); + + // Render background + ImGuiAabb bb(window->Aabb().Max.x - style.ScrollbarWidth, window->Pos.y + window->TitleBarHeight()+1, window->Aabb().Max.x, window->Aabb().Max.y-1); + window->DrawList->AddRectFilled(bb.Min, bb.Max, window->Color(ImGuiCol_ScrollbarBg)); + bb.Expand(ImVec2(-3,-3)); + const float scrollbar_height = bb.GetHeight(); + + // The grabable box size generally represent the amount visible (vs the total scrollable amount) + // But we maintain a minimum size in pixel to allow for the user to still aim inside. + const float grab_h_pixels = ImMax(style.GrabMinSize, scrollbar_height * ImSaturate(window->Size.y / ImMax(window->SizeContents.y, window->Size.y))); + const float grab_h_norm = grab_h_pixels / scrollbar_height; + + // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). + bool held = false; + bool hovered = false; + const bool previously_held = (g.ActiveId == id); + ButtonBehaviour(bb, id, &hovered, &held, true); + + const float scroll_max = ImMax(1.0f, window->SizeContents.y - window->Size.y); + float scroll_ratio = ImSaturate(window->ScrollY / scroll_max); + float grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height; + if (held) + { + const float clicked_y_norm = ImSaturate((g.IO.MousePos.y - bb.Min.y) / scrollbar_height); // Click position in scrollbar space (0.0f->1.0f) + g.HoveredId = id; + + bool seek_absolute = false; + if (!previously_held) + { + // On initial click calculate the distance between mouse and the center of the grab + if (clicked_y_norm >= grab_y_norm && clicked_y_norm <= grab_y_norm + grab_h_norm) + { + g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f; + } + else + { + seek_absolute = true; + g.ScrollbarClickDeltaToGrabCenter = 0; + } + } + + // Apply scroll + const float scroll_y_norm = ImSaturate((clicked_y_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); + window->ScrollY = (float)(int)(0.5f + scroll_y_norm * (window->SizeContents.y - window->Size.y)); + window->NextScrollY = window->ScrollY; + + // Update values for rendering + scroll_ratio = ImSaturate(window->ScrollY / scroll_max); + grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height; + + // Update distance to grab now that we have seeked and saturated + if (seek_absolute) + g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f; + } + + // Render + const ImU32 grab_col = window->Color(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); + window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm) + grab_h_pixels), grab_col); +} + // Moving window to front of display (which happens to be back of our sorted list) static void FocusWindow(ImGuiWindow* window) { ImGuiState& g = *GImGui; + + // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing. g.FocusedWindow = window; + // And move its root window to the top of the pile + if (window->RootWindow) + window = window->RootWindow; + if (g.Windows.back() == window) return; @@ -3109,8 +3284,6 @@ static void SetFont(ImFont* font) g.Font = font; g.FontSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel; - g.Font->FallbackGlyph = NULL; - g.Font->FallbackGlyph = g.Font->FindGlyph(g.Font->FallbackChar); } void ImGui::PushFont(ImFont* font) @@ -3228,7 +3401,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) ImGuiState& g = *GImGui; ImVec2* pvar = GetStyleVarVec2Addr(idx); - IM_ASSERT(pvar != NULL); // Called function with wrong-type? Varialble is not a ImVec2. + IM_ASSERT(pvar != NULL); // Called function with wrong-type? Variable is not a ImVec2. ImGuiStyleMod backup; backup.Var = idx; backup.PreviousValue = *pvar; @@ -3320,14 +3493,12 @@ ImVec2 ImGui::GetWindowPos() return window->Pos; } -void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCondition cond) +static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond) { - ImGuiWindow* window = GetCurrentWindow(); - // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowPosAllowFlags & cond) == 0) return; - window->SetWindowPosAllowFlags &= ~(ImGuiSetCondition_FirstUseThisSession | ImGuiSetCondition_FirstUseEver); + window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver); // Set const ImVec2 old_pos = window->Pos; @@ -3336,20 +3507,31 @@ void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCondition cond) window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor } -ImVec2 ImGui::GetWindowSize() +void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond) { ImGuiWindow* window = GetCurrentWindow(); - return window->Size; + SetWindowPos(window, pos, cond); } -void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCondition cond) +void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowPos(window, pos, cond); +} + +ImVec2 ImGui::GetWindowSize() { ImGuiWindow* window = GetCurrentWindow(); + return window->Size; +} +static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond) +{ // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) return; - window->SetWindowSizeAllowFlags &= ~(ImGuiSetCondition_FirstUseThisSession | ImGuiSetCondition_FirstUseEver); + window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver); // Set if (ImLengthSqr(size) > 0.00001f) @@ -3365,53 +3547,103 @@ void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCondition cond) } } -void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCondition cond) +void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond) { ImGuiWindow* window = GetCurrentWindow(); + SetWindowSize(window, size, cond); +} +void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowSize(window, size, cond); +} + +static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond) +{ // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) return; - window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCondition_FirstUseThisSession | ImGuiSetCondition_FirstUseEver); + window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver); // Set window->Collapsed = collapsed; } -void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCondition cond) +void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond) +{ + ImGuiWindow* window = GetCurrentWindow(); + SetWindowCollapsed(window, collapsed, cond); +} + +bool ImGui::GetWindowCollapsed() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->Collapsed; +} + +void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowCollapsed(window, collapsed, cond); +} + +void ImGui::SetWindowFocus() +{ + ImGuiWindow* window = GetCurrentWindow(); + FocusWindow(window); +} + +void ImGui::SetWindowFocus(const char* name) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + FocusWindow(window); +} + +void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond) { ImGuiState& g = *GImGui; g.SetNextWindowPosVal = pos; - g.SetNextWindowPosCond = cond ? cond : ImGuiSetCondition_Always; + g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always; } -void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCondition cond) +void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond) { ImGuiState& g = *GImGui; g.SetNextWindowSizeVal = size; - g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCondition_Always; + g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always; } -void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCondition cond) +void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond) { ImGuiState& g = *GImGui; g.SetNextWindowCollapsedVal = collapsed; - g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCondition_Always; + g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowFocus() +{ + ImGuiState& g = *GImGui; + g.SetNextWindowFocus = true; } ImVec2 ImGui::GetContentRegionMax() { ImGuiWindow* window = GetCurrentWindow(); - ImVec2 mx = window->Size - window->WindowPadding(); + ImVec2 window_padding = window->WindowPadding(); + ImVec2 mx = window->Size - window_padding; if (window->DC.ColumnsCount != 1) { mx.x = ImGui::GetColumnOffset(window->DC.ColumnsCurrent + 1); - mx.x -= GImGui->Style.WindowPadding.x; + mx.x -= window_padding.x; } else { if (window->ScrollbarY) - mx.x -= GImGui->Style.ScrollBarWidth; + mx.x -= GImGui->Style.ScrollbarWidth; } return mx; } @@ -3427,7 +3659,7 @@ ImVec2 ImGui::GetWindowContentRegionMax() ImGuiWindow* window = GetCurrentWindow(); ImVec2 m = window->Size - window->WindowPadding(); if (window->ScrollbarY) - m.x -= GImGui->Style.ScrollBarWidth; + m.x -= GImGui->Style.ScrollbarWidth; return m; } @@ -3490,21 +3722,21 @@ void ImGui::SetCursorPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = window->Pos + pos; - window->SizeContentsFit = ImMax(window->SizeContentsFit, pos + ImVec2(0.0f, window->ScrollY)); + window->SizeContentsCurrent = ImMax(window->SizeContentsCurrent, pos + ImVec2(0.0f, window->ScrollY)); } void ImGui::SetCursorPosX(float x) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.x = window->Pos.x + x; - window->SizeContentsFit.x = ImMax(window->SizeContentsFit.x, x); + window->SizeContentsCurrent.x = ImMax(window->SizeContentsCurrent.x, x); } void ImGui::SetCursorPosY(float y) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.y = window->Pos.y + y; - window->SizeContentsFit.y = ImMax(window->SizeContentsFit.y, y + window->ScrollY); + window->SizeContentsCurrent.y = ImMax(window->SizeContentsCurrent.y, y + window->ScrollY); } ImVec2 ImGui::GetCursorScreenPos() @@ -3519,6 +3751,18 @@ void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) window->DC.CursorPos = screen_pos; } +float ImGui::GetScrollPosY() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->ScrollY; +} + +float ImGui::GetScrollMaxY() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->SizeContents.y - window->SizeFull.y; +} + void ImGui::SetScrollPosHere() { ImGuiWindow* window = GetCurrentWindow(); @@ -3720,9 +3964,9 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) const char* value_text_begin = &buf[0]; const char* value_text_end = value_text_begin + ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); - const ImVec2 text_size = CalcTextSize(label, NULL, true); - const ImGuiAabb value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2, text_size.y)); - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2 + (text_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), 0.0f) + text_size); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImGuiAabb value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2, label_size.y)); + const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + style.FramePadding.x*2 + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), 0.0f) + label_size); ItemSize(bb); if (!ItemAdd(value_bb, NULL)) return; @@ -3746,8 +3990,11 @@ static bool IsHovered(const ImGuiAabb& bb, const ImGuiID& id) if (g.HoveredId == 0) { ImGuiWindow* window = GetCurrentWindow(); - const bool hovered = (g.HoveredRootWindow == window->RootWindow) && (g.ActiveId == 0 || g.ActiveId == id) && IsMouseHoveringBox(bb); - return hovered; + if (g.HoveredRootWindow == window->RootWindow) + { + bool hovered = (g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdIsFocusedOnly) && IsMouseHoveringBox(bb); + return hovered; + } } return false; } @@ -3814,9 +4061,9 @@ bool ImGui::Button(const char* label, const ImVec2& size_arg, bool repeat_when_h const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); - const ImVec2 text_size = CalcTextSize(label, NULL, true); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : text_size.x, size_arg.y != 0.0f ? size_arg.y : text_size.y); + const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + size + style.FramePadding*2.0f); ItemSize(bb); if (!ItemAdd(bb, &id)) @@ -3829,8 +4076,8 @@ bool ImGui::Button(const char* label, const ImVec2& size_arg, bool repeat_when_h const ImU32 col = window->Color((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - const ImVec2 off = ImVec2(ImMax(0.0f, size.x - text_size.x) * 0.5f, ImMax(0.0f, size.y - text_size.y) * 0.5f); // Center (only applies if we explicitly gave a size bigger than the text size, which isn't the common path) - RenderTextClipped(bb.Min + style.FramePadding + off, label, NULL, &text_size, bb.Max); // Render clip (only applies if we explicitly gave a size smaller than the text size, which isn't the commmon path) + const ImVec2 off = ImVec2(ImMax(0.0f, size.x - label_size.x) * 0.5f, ImMax(0.0f, size.y - label_size.y) * 0.5f); // Center (only applies if we explicitly gave a size bigger than the text size, which isn't the common path) + RenderTextClipped(bb.Min + style.FramePadding + off, label, NULL, &label_size, bb.Max); // Render clip (only applies if we explicitly gave a size smaller than the text size, which isn't the commmon path) return pressed; } @@ -3845,9 +4092,9 @@ bool ImGui::SmallButton(const char* label) const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); - const ImVec2 text_size = CalcTextSize(label, NULL, true); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + text_size + ImVec2(style.FramePadding.x*2,0)); + const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + label_size + ImVec2(style.FramePadding.x*2,0)); ItemSize(bb); if (!ItemAdd(bb, &id)) return false; @@ -3888,7 +4135,7 @@ static bool CloseWindowButton(bool* p_opened) { ImGuiWindow* window = GetCurrentWindow(); - const ImGuiID id = window->GetID("##CLOSE"); + const ImGuiID id = window->GetID("#CLOSE"); const float size = window->TitleBarHeight() - 4.0f; const ImGuiAabb bb(window->Aabb().GetTR() + ImVec2(-3.0f-size,2.0f), window->Aabb().GetTR() + ImVec2(-3.0f,2.0f+size)); @@ -4082,7 +4329,7 @@ void ImGui::LogButtons() LogToClipboard(g.LogAutoExpandMaxDepth); } -bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool display_frame, const bool default_open) +bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display_frame, bool default_open) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -4098,14 +4345,31 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool d label = str_id; const ImGuiID id = window->GetID(str_id); - // We only write to the tree storage if the user clicks + // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions) ImGuiStorage* storage = window->DC.StateStorage; bool opened; - if (window->DC.OpenNextNode != -1) + if (g.SetNextTreeNodeOpenedCond != 0) { - opened = window->DC.OpenNextNode > 0; - storage->SetInt(id, opened); - window->DC.OpenNextNode = -1; + if (g.SetNextTreeNodeOpenedCond & ImGuiSetCond_Always) + { + opened = g.SetNextTreeNodeOpenedVal; + storage->SetInt(id, opened); + } + else + { + // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently. + const int stored_value = storage->GetInt(id, -1); + if (stored_value == -1) + { + opened = g.SetNextTreeNodeOpenedVal; + storage->SetInt(id, opened); + } + else + { + opened = stored_value != 0; + } + } + g.SetNextTreeNodeOpenedCond = 0; } else { @@ -4114,10 +4378,10 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool d // Framed header expand a little outside the default padding const ImVec2 window_padding = window->WindowPadding(); - const ImVec2 text_size = CalcTextSize(label, NULL, true); + const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 pos_min = window->DC.CursorPos; const ImVec2 pos_max = window->Pos + GetContentRegionMax(); - ImGuiAabb bb = ImGuiAabb(pos_min, ImVec2(pos_max.x, pos_min.y + text_size.y)); + ImGuiAabb bb = ImGuiAabb(pos_min, ImVec2(pos_max.x, pos_min.y + label_size.y)); if (display_frame) { bb.Min.x -= window_padding.x*0.5f - 1; @@ -4125,14 +4389,13 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool d bb.Max.y += style.FramePadding.y * 2; } - const ImGuiAabb text_bb(bb.Min, bb.Min + ImVec2(window->FontSize() + style.FramePadding.x*2*2,0) + text_size); + const ImGuiAabb text_bb(bb.Min, bb.Min + ImVec2(window->FontSize() + style.FramePadding.x*2*2,0) + label_size); ItemSize(ImVec2(text_bb.GetSize().x, bb.GetSize().y)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit // When logging is enabled, if automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behaviour). // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (!display_frame) - if (g.LogEnabled && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) - opened = true; + if (g.LogEnabled && !display_frame && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + opened = true; if (!ItemAdd(bb, &id)) return opened; @@ -4179,8 +4442,7 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, const bool d return opened; } -// Text with a little bullet aligned to the typical tree node. -void ImGui::BulletTextV(const char* fmt, va_list args) +void ImGui::Bullet() { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -4188,14 +4450,36 @@ void ImGui::BulletTextV(const char* fmt, va_list args) return; const ImGuiStyle& style = g.Style; + const float line_height = window->FontSize(); + const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(line_height, line_height)); + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + // Render + const float bullet_size = line_height*0.15f; + window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + line_height*0.5f, line_height*0.5f), bullet_size, window->Color(ImGuiCol_Text)); + + // Stay on same line + ImGui::SameLine(0, -1); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; static char buf[1024]; const char* text_begin = buf; const char* text_end = text_begin + ImFormatStringV(buf, IM_ARRAYSIZE(buf), fmt, args); + const ImGuiStyle& style = g.Style; const float line_height = window->FontSize(); - const ImVec2 text_size = CalcTextSize(text_begin, text_end, true); - const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(line_height + (text_size.x > 0.0f ? (style.FramePadding.x*2) : 0.0f),0) + text_size); // Empty text doesn't add padding + const ImVec2 label_size = CalcTextSize(text_begin, text_end, true); + const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(line_height + (label_size.x > 0.0f ? (style.FramePadding.x*2) : 0.0f),0) + label_size); // Empty text doesn't add padding ItemSize(bb); if (!ItemAdd(bb, NULL)) return; @@ -4275,10 +4559,11 @@ bool ImGui::TreeNode(const char* str_label_id) return TreeNode(str_label_id, "%s", str_label_id); } -void ImGui::OpenNextNode(bool open) +void ImGui::SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond) { - ImGuiWindow* window = GetCurrentWindow(); - window->DC.OpenNextNode = open ? 1 : 0; + ImGuiState& g = *GImGui; + g.SetNextTreeNodeOpenedVal = opened; + g.SetNextTreeNodeOpenedCond = cond ? cond : ImGuiSetCond_Always; } void ImGui::PushID(const char* str_id) @@ -4322,7 +4607,7 @@ ImGuiID ImGui::GetID(const void* ptr_id) // NB: only call right after InputText because we are using its InitialValue storage static void ApplyNumericalTextInput(const char* buf, float *v) { - while (*buf == ' ' || *buf == '\t') + while (ImCharIsSpace(*buf)) buf++; // We don't support '-' op because it would conflict with inputing negative value. @@ -4331,7 +4616,7 @@ static void ApplyNumericalTextInput(const char* buf, float *v) if (op == '+' || op == '*' || op == '/') { buf++; - while (*buf == ' ' || *buf == '\t') + while (ImCharIsSpace(*buf)) buf++; } else @@ -4395,10 +4680,10 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c } } - const ImVec2 text_size = CalcTextSize(label, NULL, true); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); const ImGuiAabb slider_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(text_size.x > 0.0f ? style.ItemInnerSpacing.x + text_size.x : 0.0f, 0.0f)); + const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); // NB- we don't call ItemSize() yet becausae we may turn into a text edit box later in the function if (!ItemAdd(slider_bb, &id)) @@ -4414,9 +4699,9 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c const float grab_size_in_units = 1.0f; // In 'v' units. Probably needs to be parametrized, based on a 'v_step' value? decimal precision? float grab_size_in_pixels; if (decimal_precision > 0 || is_unbound) - grab_size_in_pixels = 10.0f; + grab_size_in_pixels = style.GrabMinSize; else - grab_size_in_pixels = ImMax(grab_size_in_units * (w / (v_max-v_min+1.0f)), 8.0f); // Integer sliders + grab_size_in_pixels = ImMax(grab_size_in_units * (w / (v_max-v_min+1.0f)), style.GrabMinSize); // Integer sliders const float slider_effective_w = slider_bb.GetWidth() - grab_size_in_pixels; const float slider_effective_x1 = slider_bb.Min.x + grab_size_in_pixels*0.5f; const float slider_effective_x2 = slider_bb.Max.x - grab_size_in_pixels*0.5f; @@ -4471,20 +4756,14 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c if (g.SliderAsInputTextId == 0) { // First frame - IM_ASSERT(g.ActiveId == id); // InputText ID should match the Slider ID (else we'd need to store them both which is also possible) + IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible) g.SliderAsInputTextId = g.ActiveId; - SetActiveId(id); g.HoveredId = id; } - else + else if (g.ActiveId != g.SliderAsInputTextId) { - if (g.ActiveId == g.SliderAsInputTextId) - SetActiveId(id); - else - { - SetActiveId(0); - g.SliderAsInputTextId = 0; - } + // Release + g.SliderAsInputTextId = 0; } if (value_changed) { @@ -4579,8 +4858,9 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c // Draw value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; - ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); - RenderText(ImVec2(slider_bb.GetCenter().x-CalcTextSize(value_buf, NULL, true).x*0.5f, frame_bb.Min.y + style.FramePadding.y), value_buf); + char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + const ImVec2 value_text_size = CalcTextSize(value_buf, value_buf_end, true); + RenderTextClipped(ImVec2(ImMax(frame_bb.Min.x + style.FramePadding.x, slider_bb.GetCenter().x - value_text_size.x*0.5f), frame_bb.Min.y + style.FramePadding.y), value_buf, value_buf_end, &value_text_size, frame_bb.Max); RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, slider_bb.Min.y), label); @@ -4721,15 +5001,15 @@ static void Plot(ImGuiPlotType plot_type, const char* label, float (*values_gett const ImGuiStyle& style = g.Style; - const ImVec2 text_size = ImGui::CalcTextSize(label, NULL, true); + const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); if (graph_size.x == 0.0f) graph_size.x = ImGui::CalcItemWidth(); if (graph_size.y == 0.0f) - graph_size.y = text_size.y; + graph_size.y = label_size.y; const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y) + style.FramePadding*2.0f); const ImGuiAabb graph_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(text_size.x > 0.0f ? style.ItemInnerSpacing.x + text_size.x : 0.0f, 0)); + const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ItemSize(bb); if (!ItemAdd(bb, NULL)) return; @@ -4854,16 +5134,16 @@ bool ImGui::Checkbox(const char* label, bool* v) const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); - const ImVec2 text_size = CalcTextSize(label, NULL, true); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImGuiAabb check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(text_size.y + style.FramePadding.y*2, text_size.y + style.FramePadding.y*2)); + const ImGuiAabb check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); ItemSize(check_bb); ImGuiAabb total_bb = check_bb; - if (text_size.x > 0) + if (label_size.x > 0) SameLine(0, (int)style.ItemInnerSpacing.x); - const ImGuiAabb text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + text_size); - if (text_size.x > 0) + const ImGuiAabb text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); + if (label_size.x > 0) { ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight())); total_bb = ImGuiAabb(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); @@ -4912,17 +5192,16 @@ bool ImGui::RadioButton(const char* label, bool active) const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImVec2 text_size = CalcTextSize(label, NULL, true); - - const ImGuiAabb check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(text_size.y + style.FramePadding.y*2-1, text_size.y + style.FramePadding.y*2-1)); + const ImGuiAabb check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); ItemSize(check_bb); ImGuiAabb total_bb = check_bb; - if (text_size.x > 0) + if (label_size.x > 0) SameLine(0, (int)style.ItemInnerSpacing.x); - const ImGuiAabb text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + text_size); - if (text_size.x > 0) + const ImGuiAabb text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); + if (label_size.x > 0) { ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight())); total_bb.Add(text_bb); @@ -4988,20 +5267,40 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob r->num_chars = (int)(text_remaining - (obj->Text + line_start_idx)); } -static bool is_white(unsigned int c) { return c==0 || c==' ' || c=='\t' || c=='\r' || c=='\n'; } static bool is_separator(unsigned int c) { return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -#define STB_TEXTEDIT_IS_SPACE(CH) ( is_white((unsigned int)CH) || is_separator((unsigned int)CH) ) -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) { ImWchar* dst = obj->Text+pos; const ImWchar* src = obj->Text+pos+n; while (ImWchar c = *src++) *dst++ = c; *dst = '\0'; } +#define STB_TEXTEDIT_IS_SPACE(CH) ( ImCharIsSpace((unsigned int)CH) || is_separator((unsigned int)CH) ) +static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +{ + ImWchar* dst = obj->Text + pos; + + // We maintain our buffer length in both UTF-8 and wchar formats + obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); + obj->CurLenW -= n; + + // Offset remaining text + const ImWchar* src = obj->Text + pos + n; + while (ImWchar c = *src++) + *dst++ = c; + *dst = '\0'; +} + static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) { - const size_t text_len = ImStrlenW(obj->Text); - if ((size_t)new_text_len + text_len + 1 >= obj->BufSize) + const size_t text_len = obj->CurLenW; + if ((size_t)new_text_len + text_len + 1 > IM_ARRAYSIZE(obj->Text)) + return false; + + const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); + if ((size_t)new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) return false; if (pos != (int)text_len) memmove(obj->Text + (size_t)pos + new_text_len, obj->Text + (size_t)pos, (text_len - (size_t)pos) * sizeof(ImWchar)); memcpy(obj->Text + (size_t)pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); - obj->Text[text_len + (size_t)new_text_len] = '\0'; + + obj->CurLenW += new_text_len; + obj->CurLenA += new_text_len_utf8; + obj->Text[obj->CurLenW] = '\0'; return true; } @@ -5096,11 +5395,15 @@ void ImGuiTextEditState::RenderTextScrolledClipped(ImFont* font, float font_size const char* text_start = GetTextPointerClippedA(font, font_size, buf, scroll_x, NULL); const char* text_end = GetTextPointerClippedA(font, font_size, text_start, width, &text_size); + // We need to test for the possibility of malformed UTF-8 (instead of just text_end[0] != 0) + unsigned int text_end_char = 0; + ImTextCharFromUtf8(&text_end_char, text_end, NULL); + // Draw a little clip symbol if we've got text on either left or right of the box const char symbol_c = '~'; const float symbol_w = font_size*0.40f; // FIXME: compute correct width const float clip_begin = (text_start > buf && text_start < text_end) ? symbol_w : 0.0f; - const float clip_end = (text_end[0] != '\0' && text_end > text_start) ? symbol_w : 0.0f; + const float clip_end = (text_end_char != 0 && text_end > text_start) ? symbol_w : 0.0f; // Draw text RenderText(pos+ImVec2(clip_begin,0), text_start+(clip_begin>0.0f?1:0), text_end-(clip_end>0.0f?1:0), false); @@ -5122,8 +5425,8 @@ bool ImGui::InputFloat(const char* label, float *v, float step, float step_fast, const ImGuiStyle& style = g.Style; const float w = ImGui::CalcItemWidth(); - const ImVec2 text_size = CalcTextSize(label, NULL, true); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); ImGui::PushID(label); const float button_sz = window->FontSize(); @@ -5163,10 +5466,10 @@ bool ImGui::InputFloat(const char* label, float *v, float step, float step_fast, ImGui::PopID(); - if (text_size.x > 0) + if (label_size.x > 0) { ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); - ItemSize(text_size); + ItemSize(label_size); RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); } @@ -5222,27 +5525,56 @@ void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const SelectionStart = SelectionEnd = CursorPos; } -static bool InputTextFilterCharacter(ImWchar c, ImGuiInputTextFlags flags) +// Return false to discard a character. +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) { + unsigned int c = *p_char; + if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) - return true; + return false; if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. - return true; + return false; - if (flags & ImGuiInputTextFlags_CharsDecimal) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) - return true; + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank)) + { + if (flags & ImGuiInputTextFlags_CharsDecimal) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + return false; - if (flags & ImGuiInputTextFlags_CharsHexadecimal) - if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) - return true; + if (flags & ImGuiInputTextFlags_CharsHexadecimal) + if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) + return false; - return false; + if (flags & ImGuiInputTextFlags_CharsUppercase) + if (c >= 'a' && c <= 'z') + *p_char = (c += (unsigned int)('A'-'a')); + + if (flags & ImGuiInputTextFlags_CharsNoBlank) + if (ImCharIsSpace(c)) + return false; + } + + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiTextEditCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + if (callback(&callback_data) != 0) + return false; + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; } // Edit a string of text -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, void (*callback)(ImGuiTextEditCallbackData*), void* user_data) +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -5255,9 +5587,9 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT const ImGuiID id = window->GetID(label); const float w = ImGui::CalcItemWidth(); - const ImVec2 text_size = CalcTextSize(label, NULL, true); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f); - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(text_size.x > 0.0f ? (style.ItemInnerSpacing.x + text_size.x) : 0.0f, 0.0f)); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); ItemSize(bb); if (!ItemAdd(frame_bb, &id)) return false; @@ -5267,29 +5599,48 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT const bool is_ctrl_down = io.KeyCtrl; const bool is_shift_down = io.KeyShift; - const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id, (flags & ImGuiInputTextFlags_CallbackCompletion) == 0); // Using completion callback disable keyboard tabbing - //const bool align_center = (bool)(flags & ImGuiInputTextFlags_AlignCenter); // FIXME: Unsupported + const bool focus_requested = window->FocusItemRegister(g.ActiveId == id, (flags & ImGuiInputTextFlags_CallbackCompletion) == 0); // Using completion callback disable keyboard tabbing + const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); + const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; const bool hovered = IsHovered(frame_bb, id); if (hovered) g.HoveredId = id; + const bool user_clicked = hovered && io.MouseClicked[0]; bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0; - if (tab_focus_requested || (hovered && io.MouseClicked[0])) + if (focus_requested || user_clicked) { if (g.ActiveId != id) { // Start edition // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' ImFormatString(edit_state.InitialText, IM_ARRAYSIZE(edit_state.InitialText), "%s", buf); - ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), buf, NULL); - edit_state.ScrollX = 0.0f; + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), buf, NULL, &buf_end); + edit_state.CurLenA = buf_end - buf; // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. edit_state.Width = w; - stb_textedit_initialize_state(&edit_state.StbState, true); + edit_state.InputCursorScreenPos = ImVec2(-1.f,-1.f); edit_state.CursorAnimReset(); - edit_state.LastCursorPos = ImVec2(-1.f,-1.f); - if (tab_focus_requested || is_ctrl_down) + if (edit_state.Id != id) + { + edit_state.Id = id; + edit_state.ScrollX = 0.0f; + stb_textedit_initialize_state(&edit_state.StbState, true); + if (focus_requested_by_code) + select_all = true; + } + else + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + edit_state.StbState.cursor = ImMin(edit_state.StbState.cursor, edit_state.CurLenW); + edit_state.StbState.select_start = ImMin(edit_state.StbState.select_start, edit_state.CurLenW); + edit_state.StbState.select_end = ImMin(edit_state.StbState.select_end, edit_state.CurLenW); + } + if (focus_requested_by_tab || (user_clicked && is_ctrl_down)) select_all = true; } SetActiveId(id); @@ -5304,6 +5655,11 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT } } + // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. + // Down the line we should have a cleaner concept of focused vs active in the library. + if (g.ActiveId == id) + g.ActiveIdIsFocusedOnly = !io.MouseDown[0]; + bool value_changed = false; bool cancel_edit = false; bool enter_pressed = false; @@ -5311,7 +5667,7 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT if (g.ActiveId == id) { // Edit in progress - edit_state.BufSize = buf_size < IM_ARRAYSIZE(edit_state.Text) ? buf_size : IM_ARRAYSIZE(edit_state.Text); + edit_state.BufSizeA = buf_size; edit_state.Font = window->Font(); edit_state.FontSize = window->FontSize(); @@ -5343,13 +5699,13 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT // Process text input (before we check for Return because using some IME will effectively send a Return?) for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++) { - const ImWchar c = g.IO.InputCharacters[n]; + unsigned int c = (unsigned int)g.IO.InputCharacters[n]; if (c) { // Insert character if they pass filtering - if (InputTextFilterCharacter(c, flags)) + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) continue; - edit_state.OnKeyPressed(c); + edit_state.OnKeyPressed((int)c); } } @@ -5379,7 +5735,7 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT if (g.IO.SetClipboardTextFn) { const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; - const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : (int)ImStrlenW(edit_state.Text); + const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; ImTextStrToUtf8(text_tmp_utf8, IM_ARRAYSIZE(text_tmp_utf8), edit_state.Text+ib, edit_state.Text+ie); g.IO.SetClipboardTextFn(text_tmp_utf8); } @@ -5395,19 +5751,18 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT if (const char* clipboard = g.IO.GetClipboardTextFn()) { // Remove new-line from pasted buffer - size_t clipboard_len = strlen(clipboard); + const size_t clipboard_len = strlen(clipboard); ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); int clipboard_filtered_len = 0; for (const char* s = clipboard; *s; ) { unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, NULL); - if (bytes_count <= 0) + s += ImTextCharFromUtf8(&c, s, NULL); + if (c == 0) break; - s += bytes_count; if (c >= 0x10000) continue; - if (InputTextFilterCharacter((ImWchar)c, flags)) + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } @@ -5442,35 +5797,46 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT IM_ASSERT(callback != NULL); // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; ImGuiKey event_key = ImGuiKey_COUNT; if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; event_key = ImGuiKey_Tab; + } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_UpArrow; + } else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_DownArrow; + } if (event_key != ImGuiKey_COUNT || (flags & ImGuiInputTextFlags_CallbackAlways) != 0) { ImGuiTextEditCallbackData callback_data; + callback_data.EventFlag = event_flag; callback_data.EventKey = event_key; callback_data.Buf = text_tmp_utf8; - callback_data.BufSize = edit_state.BufSize; + callback_data.BufSize = edit_state.BufSizeA; callback_data.BufDirty = false; callback_data.Flags = flags; callback_data.UserData = user_data; // We have to convert from position from wchar to UTF-8 positions - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromWchar(edit_state.Text, edit_state.Text + edit_state.StbState.select_end); + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_end); // Call user code callback(&callback_data); // Read back what user may have modified IM_ASSERT(callback_data.Buf == text_tmp_utf8); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufSize); + IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); IM_ASSERT(callback_data.Flags == flags); if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); @@ -5509,7 +5875,6 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT } } - // FIXME: 'align_center' unsupported ImGuiTextEditState::RenderTextScrolledClipped(window->Font(), window->FontSize(), buf, frame_bb.Min + style.FramePadding, w, (g.ActiveId == id) ? edit_state.ScrollX : 0.0f); if (g.ActiveId == id) @@ -5520,14 +5885,15 @@ bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputT if (g.InputTextState.CursorIsVisible()) window->DrawList->AddRect(cursor_pos - font_off_up + ImVec2(0,2), cursor_pos + font_off_dn - ImVec2(0,3), window->Color(ImGuiCol_Text)); - // Notify OS of text input position - if (io.ImeSetInputScreenPosFn && ImLengthSqr(edit_state.LastCursorPos - cursor_pos) > 0.0001f) + // Notify OS of text input position for advanced IME + if (io.ImeSetInputScreenPosFn && ImLengthSqr(edit_state.InputCursorScreenPos - cursor_pos) > 0.0001f) io.ImeSetInputScreenPosFn((int)cursor_pos.x - 1, (int)(cursor_pos.y - window->FontSize())); // -1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety. - edit_state.LastCursorPos = cursor_pos; + edit_state.InputCursorScreenPos = cursor_pos; } - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return enter_pressed; @@ -5646,13 +6012,13 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi const ImGuiID id = window->GetID(label); const float w = ImGui::CalcItemWidth(); - const ImVec2 text_size = CalcTextSize(label, NULL, true); - const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f); - ItemSize(frame_bb); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + label_size.x,0)); + ItemSize(bb); if (!ItemAdd(frame_bb, &id)) return false; - const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(style.ItemInnerSpacing.x + text_size.x,0)); const float arrow_size = (window->FontSize() + style.FramePadding.x * 2.0f); const bool hovered = IsHovered(frame_bb, id); @@ -5669,12 +6035,8 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi RenderTextClipped(frame_bb.Min + style.FramePadding, item_text, NULL, NULL, value_bb.Max); } - // Empty text doesn't add padding - if (text_size.x > 0) - { - ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); - ImGui::TextUnformatted(label, FindTextDisplayEnd(label)); - } + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); ImGui::PushID((int)id); bool menu_toggled = false; @@ -5698,7 +6060,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi const ImVec2 backup_pos = ImGui::GetCursorPos(); const float popup_off_x = 0.0f;//style.ItemInnerSpacing.x; - const float popup_height = (text_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + style.WindowPadding.y; + const float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + style.WindowPadding.y; const ImGuiAabb popup_aabb(ImVec2(frame_bb.Min.x+popup_off_x, frame_bb.Max.y), ImVec2(frame_bb.Max.x+popup_off_x, frame_bb.Max.y + popup_height)); ImGui::SetCursorPos(popup_aabb.Min - window->Pos); @@ -5752,10 +6114,10 @@ bool ImGui::Selectable(const char* label, bool selected, const ImVec2& size_arg) const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); - const ImVec2 text_size = CalcTextSize(label, NULL, true); + const ImVec2 label_size = CalcTextSize(label, NULL, true); const float w = window->Pos.x + ImGui::GetContentRegionMax().x - window->DC.CursorPos.x; - const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : w, size_arg.y != 0.0f ? size_arg.y : text_size.y); + const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : w, size_arg.y != 0.0f ? size_arg.y : label_size.y); const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + size); ItemSize(bb); @@ -5783,7 +6145,7 @@ bool ImGui::Selectable(const char* label, bool selected, const ImVec2& size_arg) } //const ImVec2 off = ImVec2(ImMax(0.0f, size.x - text_size.x) * 0.5f, ImMax(0.0f, size.y - text_size.y) * 0.5f); - RenderTextClipped(bb.Min, label, NULL, &text_size, bb_with_spacing.Max); + RenderTextClipped(bb.Min, label, NULL, &label_size, bb_with_spacing.Max); return pressed; } @@ -5803,8 +6165,6 @@ bool ImGui::Selectable(const char* label, bool* p_selected, const ImVec2& size_a bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) { ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; const ImGuiStyle& style = ImGui::GetStyle(); const ImGuiID id = ImGui::GetID(label); @@ -5816,6 +6176,8 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) size.y = (size_arg.y != 0.0f) ? size_arg.y : ImGui::GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y; const ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + const ImGuiAabb bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + window->DC.LastItemAabb = bb; if (label_size.x > 0) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); @@ -5842,7 +6204,13 @@ bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_item void ImGui::ListBoxFooter() { + ImGuiWindow* parent_window = GetParentWindow(); + const ImGuiAabb bb = parent_window->DC.LastItemAabb; + ImGui::EndChildFrame(); + + parent_window->DC.CursorPos = bb.Min; + ItemSize(bb, NULL); } bool ImGui::ListBox(const char* label, int* current_item, const char** items, int items_count, int height_items) @@ -5853,6 +6221,10 @@ bool ImGui::ListBox(const char* label, int* current_item, const char** items, in bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) { + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + if (!ImGui::ListBoxHeader(label, items_count, height_in_items)) return false; @@ -5886,7 +6258,7 @@ bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_borde return false; const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID("##colorbutton"); + const ImGuiID id = window->GetID("#colorbutton"); const float square_size = window->FontSize(); const ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.x*2, square_size + (small_height ? 0 : style.FramePadding.y*2))); ItemSize(bb); @@ -6006,7 +6378,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) value_changed |= ImGui::InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal); ImGui::PopItemWidth(); char* p = buf; - while (*p == '#' || *p == ' ' || *p == '\t') + while (*p == '#' || ImCharIsSpace(*p)) p++; // Treat at unsigned (%X is unsigned) @@ -6094,7 +6466,10 @@ void ImGui::Separator() window->DrawList->AddLine(bb.Min, bb.Max, window->Color(ImGuiCol_Border)); if (window->DC.ColumnsCount > 1) + { PushColumnClipRect(); + window->DC.ColumnsCellMinY = window->DC.CursorPos.y; + } } // A little vertical spacing. @@ -6123,15 +6498,15 @@ static void ItemSize(ImVec2 size, ImVec2* adjust_vertical_offset) window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); - window->SizeContentsFit = ImMax(window->SizeContentsFit, ImVec2(window->DC.CursorPosPrevLine.x, window->DC.CursorPos.y) - window->Pos + ImVec2(0.0f, window->ScrollY)); + window->SizeContentsCurrent = ImMax(window->SizeContentsCurrent, ImVec2(window->DC.CursorPosPrevLine.x - window->Pos.x, window->DC.CursorPos.y + window->ScrollY - window->Pos.y)); window->DC.PrevLineHeight = line_height; window->DC.CurrentLineHeight = 0.0f; } -static void ItemSize(const ImGuiAabb& aabb, ImVec2* adjust_start_offset) +static void ItemSize(const ImGuiAabb& bb, ImVec2* adjust_start_offset) { - ItemSize(aabb.GetSize(), adjust_start_offset); + ItemSize(bb.GetSize(), adjust_start_offset); } static bool IsClipped(const ImGuiAabb& bb) @@ -6160,7 +6535,11 @@ static bool ItemAdd(const ImGuiAabb& bb, const ImGuiID* id) window->DC.LastItemHovered = false; return false; } - window->DC.LastItemHovered = IsMouseHoveringBox(bb); // this is a sensible default but widgets are free to override it after calling ItemAdd() + + // This is a sensible default, but widgets are free to override it after calling ItemAdd() + const bool hovered = IsMouseHoveringBox(bb); + //const bool hovered = (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdIsFocusedOnly) && IsMouseHoveringBox(bb); // matching the behaviour of IsHovered(), not always what the user wants? + window->DC.LastItemHovered = hovered; return true; } @@ -6225,7 +6604,18 @@ void ImGui::NextColumn() } } -// FIMXE-OPT: This is called too often. We need to cache offset for active columns set. +int ImGui::GetColumnIndex() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->DC.ColumnsCurrent; +} + +int ImGui::GetColumnsCount() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->DC.ColumnsCount; +} + float ImGui::GetColumnOffset(int column_index) { ImGuiState& g = *GImGui; @@ -6233,12 +6623,13 @@ float ImGui::GetColumnOffset(int column_index) if (column_index < 0) column_index = window->DC.ColumnsCurrent; - const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); - RegisterAliveId(column_id); - const float default_t = column_index / (float)window->DC.ColumnsCount; - const float t = window->StateStorage.GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store an union into the map?) + // Read from cache + IM_ASSERT(column_index < (int)window->DC.ColumnsOffsetsT.size()); + const float t = window->DC.ColumnsOffsetsT[column_index]; - const float offset = window->DC.ColumnsStartX + t * (window->Size.x - g.Style.ScrollBarWidth - window->DC.ColumnsStartX); + const float min_x = window->DC.ColumnsStartX; + const float max_x = window->Size.x - (g.Style.ScrollbarWidth);// - window->WindowPadding().x; + const float offset = min_x + t * (max_x - min_x); return offset; } @@ -6249,9 +6640,14 @@ void ImGui::SetColumnOffset(int column_index, float offset) if (column_index < 0) column_index = window->DC.ColumnsCurrent; + IM_ASSERT(column_index < (int)window->DC.ColumnsOffsetsT.size()); const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); - const float t = (offset - window->DC.ColumnsStartX) / (window->Size.x - g.Style.ScrollBarWidth - window->DC.ColumnsStartX); - window->StateStorage.SetFloat(column_id, t); + + const float min_x = window->DC.ColumnsStartX; + const float max_x = window->Size.x - (g.Style.ScrollbarWidth);// - window->WindowPadding().x; + const float t = (offset - min_x) / (max_x - min_x); + window->DC.StateStorage->SetFloat(column_id, t); + window->DC.ColumnsOffsetsT[column_index] = t; } float ImGui::GetColumnWidth(int column_index) @@ -6279,8 +6675,6 @@ void ImGui::Columns(int columns_count, const char* id, bool border) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; if (window->DC.ColumnsCount != 1) { @@ -6294,7 +6688,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) } // Draw columns borders and handle resize at the time of "closing" a columns set - if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders) + if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems) { const float y1 = window->DC.ColumnsStartPos.y; const float y2 = window->DC.CursorPos.y; @@ -6338,9 +6732,26 @@ void ImGui::Columns(int columns_count, const char* id, bool border) if (window->DC.ColumnsCount != 1) { + // Cache column offsets + window->DC.ColumnsOffsetsT.resize((size_t)columns_count + 1); + for (int column_index = 0; column_index < columns_count + 1; column_index++) + { + const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); + RegisterAliveId(column_id); + const float default_t = column_index / (float)window->DC.ColumnsCount; + const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store an union into the map?) + window->DC.ColumnsOffsetsT[column_index] = t; + } + PushColumnClipRect(); ImGui::PushItemWidth(ImGui::GetColumnWidth() * 0.65f); } + else + { + window->DC.ColumnsOffsetsT.resize(2); + window->DC.ColumnsOffsetsT[0] = 0.0f; + window->DC.ColumnsOffsetsT[1] = 1.0f; + } } void ImGui::TreePush(const char* str_id) @@ -7146,7 +7557,7 @@ void ImFontAtlas::RenderCustomTexData() TexUvWhitePixel = ImVec2((TexExtraDataPos.x + 0.5f) / TexWidth, (TexExtraDataPos.y + 0.5f) / TexHeight); // Draw a mouse cursor into texture - // Because our font uses an alpha texture, we have to spread the cursor in 2 parts (black/white) which will be rendered separately. + // Because our font uses a single color channel, we have to spread the cursor in 2 layers (black/white) which will be rendered separately. const char cursor_pixels[] = { "X " @@ -7195,8 +7606,10 @@ void ImFont::Clear() DisplayOffset = ImVec2(-0.5f, 0.5f); ContainerAtlas = NULL; Glyphs.clear(); - IndexLookup.clear(); FallbackGlyph = NULL; + FallbackXAdvance = 0.0f; + IndexXAdvance.clear(); + IndexLookup.clear(); } // Retrieve list of range (2 int per range, values are inclusive) @@ -7215,7 +7628,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChinese() static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3040, 0x309F, // Hiragana, Katakana + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters 0x4e00, 0x9FAF, // CJK Ideograms 0, @@ -7263,18 +7677,18 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 109,2,18,23,0,0,9,61,3,0,28,41,77,27,19,17,81,5,2,14,5,83,57,252,14,154,263,14,20,8,13,6,57,39,38, }; static int ranges_unpacked = false; - static ImWchar ranges[6 + 1 + IM_ARRAYSIZE(offsets_from_0x4E00)*2] = + static ImWchar ranges[8 + IM_ARRAYSIZE(offsets_from_0x4E00)*2 + 1] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3040, 0x309F, // Hiragana, Katakana + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters - 0, }; if (!ranges_unpacked) { // Unpack int codepoint = 0x4e00; - ImWchar* dst = &ranges[6]; + ImWchar* dst = &ranges[8]; for (int n = 0; n < IM_ARRAYSIZE(offsets_from_0x4E00); n++, dst += 2) dst[0] = dst[1] = (ImWchar)(codepoint += (offsets_from_0x4E00[n] + 1)); dst[0] = 0; @@ -7289,24 +7703,48 @@ void ImFont::BuildLookupTable() for (size_t i = 0; i != Glyphs.size(); i++) max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); + IndexXAdvance.clear(); + IndexXAdvance.resize((size_t)max_codepoint + 1); IndexLookup.clear(); IndexLookup.resize((size_t)max_codepoint + 1); - for (size_t i = 0; i < IndexLookup.size(); i++) + for (size_t i = 0; i < (size_t)max_codepoint + 1; i++) + { + IndexXAdvance[i] = -1.0f; IndexLookup[i] = -1; + } for (size_t i = 0; i < Glyphs.size(); i++) - IndexLookup[(int)Glyphs[i].Codepoint] = (int)i; + { + const size_t codepoint = (int)Glyphs[i].Codepoint; + IndexXAdvance[codepoint] = Glyphs[i].XAdvance; + IndexLookup[codepoint] = (int)i; + } // Create a glyph to handle TAB // FIXME: Needs proper TAB handling but it needs to be contextualized (can arbitrary say that each string starts at "column 0" - if (const ImFont::Glyph* space_glyph = FindGlyph((unsigned short)' ')) + if (FindGlyph((unsigned short)' ')) { - Glyphs.resize(Glyphs.size() + 1); + if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times + Glyphs.resize(Glyphs.size() + 1); ImFont::Glyph& tab_glyph = Glyphs.back(); - tab_glyph = *space_glyph; + tab_glyph = *FindGlyph((unsigned short)' '); tab_glyph.Codepoint = '\t'; tab_glyph.XAdvance *= 4; - IndexLookup[(int)tab_glyph.Codepoint] = (int)(Glyphs.size()-1); + IndexXAdvance[(size_t)tab_glyph.Codepoint] = (float)tab_glyph.XAdvance; + IndexLookup[(size_t)tab_glyph.Codepoint] = (int)(Glyphs.size()-1); } + + FallbackGlyph = NULL; + FallbackGlyph = FindGlyph(FallbackChar); + FallbackXAdvance = FallbackGlyph ? FallbackGlyph->XAdvance : 0.0f; + for (size_t i = 0; i < (size_t)max_codepoint + 1; i++) + if (IndexXAdvance[i] < 0.0f) + IndexXAdvance[i] = FallbackXAdvance; +} + +void ImFont::SetFallbackChar(ImWchar c) +{ + FallbackChar = c; + BuildLookupTable(); } const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const @@ -7322,65 +7760,66 @@ const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const // Convert UTF-8 to 32-bits character, process single character input. // Based on stb_from_utf8() from github.com/nothings/stb/ +// We handle UTF-8 decoding error by skipping forward. static int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) { - if (*in_text != 0) - { - unsigned int c = (unsigned int)-1; - const unsigned char* str = (const unsigned char*)in_text; - if (!(*str & 0x80)) - { - c = (unsigned int)(*str++); - *out_char = c; - return 1; - } - if ((*str & 0xe0) == 0xc0) - { - if (in_text_end && in_text_end - (const char*)str < 2) return -1; - if (*str < 0xc2) return -1; - c = (unsigned int)((*str++ & 0x1f) << 6); - if ((*str & 0xc0) != 0x80) return -1; - c += (*str++ & 0x3f); - *out_char = c; - return 2; - } - if ((*str & 0xf0) == 0xe0) - { - if (in_text_end && in_text_end - (const char*)str < 3) return -1; - if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return -1; - if (*str == 0xed && str[1] > 0x9f) return -1; // str[1] < 0x80 is checked below - c = (unsigned int)((*str++ & 0x0f) << 12); - if ((*str & 0xc0) != 0x80) return -1; - c += (unsigned int)((*str++ & 0x3f) << 6); - if ((*str & 0xc0) != 0x80) return -1; - c += (*str++ & 0x3f); - *out_char = c; - return 3; - } - if ((*str & 0xf8) == 0xf0) - { - if (in_text_end && in_text_end - (const char*)str < 4) return -1; - if (*str > 0xf4) return -1; - if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return -1; - if (*str == 0xf4 && str[1] > 0x8f) return -1; // str[1] < 0x80 is checked below - c = (unsigned int)((*str++ & 0x07) << 18); - if ((*str & 0xc0) != 0x80) return -1; - c += (unsigned int)((*str++ & 0x3f) << 12); - if ((*str & 0xc0) != 0x80) return -1; - c += (unsigned int)((*str++ & 0x3f) << 6); - if ((*str & 0xc0) != 0x80) return -1; - c += (*str++ & 0x3f); - // utf-8 encodings of values used in surrogate pairs are invalid - if ((c & 0xFFFFF800) == 0xD800) return -1; - *out_char = c; - return 4; - } + unsigned int c = (unsigned int)-1; + const unsigned char* str = (const unsigned char*)in_text; + if (!(*str & 0x80)) + { + c = (unsigned int)(*str++); + *out_char = c; + return 1; + } + if ((*str & 0xe0) == 0xc0) + { + *out_char = 0; + if (in_text_end && in_text_end - (const char*)str < 2) return 0; + if (*str < 0xc2) return 0; + c = (unsigned int)((*str++ & 0x1f) << 6); + if ((*str & 0xc0) != 0x80) return 0; + c += (*str++ & 0x3f); + *out_char = c; + return 2; + } + if ((*str & 0xf0) == 0xe0) + { + *out_char = 0; + if (in_text_end && in_text_end - (const char*)str < 3) return 0; + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 0; + if (*str == 0xed && str[1] > 0x9f) return 0; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x0f) << 12); + if ((*str & 0xc0) != 0x80) return 0; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 0; + c += (*str++ & 0x3f); + *out_char = c; + return 3; + } + if ((*str & 0xf8) == 0xf0) + { + *out_char = 0; + if (in_text_end && in_text_end - (const char*)str < 4) return 0; + if (*str > 0xf4) return 0; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 0; + if (*str == 0xf4 && str[1] > 0x8f) return 0; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x07) << 18); + if ((*str & 0xc0) != 0x80) return 0; + c += (unsigned int)((*str++ & 0x3f) << 12); + if ((*str & 0xc0) != 0x80) return 0; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 0; + c += (*str++ & 0x3f); + // utf-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xFFFFF800) == 0xD800) return 0; + *out_char = c; + return 4; } *out_char = 0; return 0; } -static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end) +static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) { ImWchar* buf_out = buf; ImWchar* buf_end = buf + buf_size; @@ -7388,10 +7827,14 @@ static ptrdiff_t ImTextStrFromUtf8(ImWchar* buf, size_t buf_size, const char* in { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes *buf_out++ = (ImWchar)c; } *buf_out = 0; + if (in_text_remaining) + *in_text_remaining = in_text; return buf_out - buf; } @@ -7402,6 +7845,8 @@ static int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; if (c < 0x10000) char_count++; } @@ -7466,7 +7911,7 @@ static ptrdiff_t ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_t return buf_out - buf; } -static int ImTextCountUtf8BytesFromWchar(const ImWchar* in_text, const ImWchar* in_text_end) +static int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) { int bytes_count = 0; while ((!in_text_end || in_text < in_text_end) && *in_text) @@ -7506,9 +7951,14 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c const char* s = text; while (s < text_end) { - unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, text_end); - const char* next_s = s + (bytes_count > 0 ? bytes_count : 1); + unsigned int c = (unsigned int)*s; + const char* next_s; + if (c < 0x80) + next_s = s + 1; + else + next_s = s + ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; if (c == '\n') { @@ -7518,11 +7968,8 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c continue; } - float char_width = 0.0f; - if (const Glyph* glyph = FindGlyph((unsigned short)c)) - char_width = glyph->XAdvance * scale; - - if (c == ' ' || c == '\t') + const float char_width = ((size_t)c < IndexXAdvance.size()) ? IndexXAdvance[(size_t)c] * scale : FallbackXAdvance; + if (ImCharIsSpace(c)) { if (inside_word) { @@ -7604,16 +8051,24 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons while (s < text_end) { const char c = *s; - if (c == ' ' || c == '\t') { s++; } else if (c == '\n') { s++; break; } else { break; } + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } } continue; } } // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte) - unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, text_end); - s += bytes_count > 0 ? bytes_count : 1; + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + } if (c == '\n') { @@ -7623,10 +8078,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons continue; } - float char_width = 0.0f; - if (const Glyph* glyph = FindGlyph((unsigned short)c)) - char_width = glyph->XAdvance * scale; - + const float char_width = ((size_t)c < IndexXAdvance.size()) ? IndexXAdvance[(size_t)c] * scale : FallbackXAdvance; if (line_width + char_width >= max_width) break; @@ -7670,10 +8122,7 @@ ImVec2 ImFont::CalcTextSizeW(float size, float max_width, const ImWchar* text_be continue; } - float char_width = 0.0f; - if (const Glyph* glyph = FindGlyph((unsigned short)c)) - char_width = glyph->XAdvance * scale; - + const float char_width = ((size_t)c < IndexXAdvance.size()) ? IndexXAdvance[(size_t)c] * scale : FallbackXAdvance; if (line_width + char_width >= max_width) break; @@ -7740,16 +8189,24 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re while (s < text_end) { const char c = *s; - if (c == ' ' || c == '\t') { s++; } else if (c == '\n') { s++; break; } else { break; } + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } } continue; } } // Decode and advance source (handle unlikely UTF-8 decoding failure by skipping to the next byte) - unsigned int c; - const int bytes_count = ImTextCharFromUtf8(&c, s, text_end); - s += bytes_count > 0 ? bytes_count : 1; + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + } if (c == '\n') { @@ -7831,11 +8288,13 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re #if defined(_MSC_VER) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) +#ifndef _WINDOWS_ #define WIN32_LEAN_AND_MEAN #include +#endif // Win32 API clipboard implementation -static const char* GetClipboardTextFn_DefaultImpl() +static const char* GetClipboardTextFn_DefaultImpl() { static char* buf_local = NULL; if (buf_local) @@ -7845,12 +8304,16 @@ static const char* GetClipboardTextFn_DefaultImpl() } if (!OpenClipboard(NULL)) return NULL; - HANDLE buf_handle = GetClipboardData(CF_TEXT); - if (buf_handle == NULL) + HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT); + if (wbuf_handle == NULL) return NULL; - if (char* buf_global = (char*)GlobalLock(buf_handle)) - buf_local = ImStrdup(buf_global); - GlobalUnlock(buf_handle); + if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle)) + { + int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1; + buf_local = (char*)ImGui::MemAlloc(buf_len * sizeof(char)); + ImTextStrToUtf8(buf_local, buf_len, wbuf_global, NULL); + } + GlobalUnlock(wbuf_handle); CloseClipboard(); return buf_local; } @@ -7860,24 +8323,23 @@ static void SetClipboardTextFn_DefaultImpl(const char* text) { if (!OpenClipboard(NULL)) return; - const char* text_end = text + strlen(text); - const int buf_length = (int)(text_end - text) + 1; - HGLOBAL buf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)buf_length * sizeof(char)); - if (buf_handle == NULL) + + const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; + HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); + if (wbuf_handle == NULL) return; - char* buf_global = (char *)GlobalLock(buf_handle); - memcpy(buf_global, text, (size_t)(text_end - text)); - buf_global[text_end - text] = 0; - GlobalUnlock(buf_handle); + ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); + ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL); + GlobalUnlock(wbuf_handle); EmptyClipboard(); - SetClipboardData(CF_TEXT, buf_handle); + SetClipboardData(CF_UNICODETEXT, wbuf_handle); CloseClipboard(); } #else // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers -static const char* GetClipboardTextFn_DefaultImpl() +static const char* GetClipboardTextFn_DefaultImpl() { return GImGui->PrivateClipboard; } @@ -7899,6 +8361,37 @@ static void SetClipboardTextFn_DefaultImpl(const char* text) #endif +#if defined(_MSC_VER) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS) + +#ifndef _WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#pragma comment(lib, "imm32") + +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +{ + // Notify OS Input Method Editor of text input position + if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) + if (HIMC himc = ImmGetContext(hwnd)) + { + COMPOSITIONFORM cf; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y; + cf.dwStyle = CFS_FORCE_POSITION; + ImmSetCompositionWindow(himc, &cf); + } +} + +#else + +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +{ +} + +#endif + //----------------------------------------------------------------------------- // HELP //----------------------------------------------------------------------------- @@ -7957,7 +8450,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat("TreeNodeSpacing", &style.TreeNodeSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("ScrollBarWidth", &style.ScrollBarWidth, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("ScrollBarWidth", &style.ScrollbarWidth, 1.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); ImGui::TreePop(); } @@ -8028,18 +8522,39 @@ static void ShowExampleAppConsole(bool* opened); static void ShowExampleAppLongText(bool* opened); static void ShowExampleAppAutoResize(bool* opened); static void ShowExampleAppFixedOverlay(bool* opened); +static void ShowExampleAppManipulatingWindowTitle(bool* opened); static void ShowExampleAppCustomRendering(bool* opened); // Demonstrate ImGui features (unfortunately this makes this function a little bloated!) void ImGui::ShowTestWindow(bool* opened) { + // Examples apps + static bool show_app_console = false; + static bool show_app_long_text = false; + static bool show_app_auto_resize = false; + static bool show_app_fixed_overlay = false; + static bool show_app_custom_rendering = false; + static bool show_app_manipulating_window_title = false; + if (show_app_console) + ShowExampleAppConsole(&show_app_console); + if (show_app_long_text) + ShowExampleAppLongText(&show_app_long_text); + if (show_app_auto_resize) + ShowExampleAppAutoResize(&show_app_auto_resize); + if (show_app_fixed_overlay) + ShowExampleAppFixedOverlay(&show_app_fixed_overlay); + if (show_app_manipulating_window_title) + ShowExampleAppManipulatingWindowTitle(&show_app_manipulating_window_title); + if (show_app_custom_rendering) + ShowExampleAppCustomRendering(&show_app_custom_rendering); + static bool no_titlebar = false; static bool no_border = true; static bool no_resize = false; static bool no_move = false; static bool no_scrollbar = false; static bool no_collapse = false; - static float fill_alpha = 0.65f; + static float bg_alpha = 0.65f; // Demonstrate the various window flags. Typically you would just use the default. ImGuiWindowFlags window_flags = 0; @@ -8049,13 +8564,15 @@ void ImGui::ShowTestWindow(bool* opened) if (no_move) window_flags |= ImGuiWindowFlags_NoMove; if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; - if (!ImGui::Begin("ImGui Test", opened, ImVec2(550,680), fill_alpha, window_flags)) + if (!ImGui::Begin("ImGui Test", opened, ImVec2(550,680), bg_alpha, window_flags)) { // Early out if the window is collapsed, as an optimization. ImGui::End(); return; } - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); + + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // 2/3 of the space for widget and 1/3 for labels + //ImGui::PushItemWidth(-140); // Right align, keep 140 pixels for labels ImGui::Text("ImGui says hello."); //ImGui::Text("MousePos (%g, %g)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); @@ -8078,7 +8595,7 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::Checkbox("no move", &no_move); ImGui::SameLine(150); ImGui::Checkbox("no scrollbar", &no_scrollbar); ImGui::SameLine(300); ImGui::Checkbox("no collapse", &no_collapse); - ImGui::SliderFloat("fill alpha", &fill_alpha, 0.0f, 1.0f); + ImGui::SliderFloat("bg alpha", &bg_alpha, 0.0f, 1.0f); if (ImGui::TreeNode("Style")) { @@ -8149,7 +8666,9 @@ void ImGui::ShowTestWindow(bool* opened) { ImGui::BulletText("Bullet point 1"); ImGui::BulletText("Bullet point 2\nOn multiple lines"); - ImGui::BulletText("Bullet point 3"); + ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); + ImGui::Bullet(); ImGui::SmallButton("Button 1"); + ImGui::Bullet(); ImGui::SmallButton("Button 2"); ImGui::TreePop(); } @@ -8291,6 +8810,18 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::TreePop(); } + if (ImGui::TreeNode("Filtered Text Input")) + { + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); + static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); + struct TextFilters { static int FilterImGuiLetters(ImGuiTextEditCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + ImGui::TreePop(); + } + static bool check = true; ImGui::Checkbox("checkbox", &check); @@ -8392,6 +8923,7 @@ void ImGui::ShowTestWindow(bool* opened) static int listbox_item_current = 1; ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4); + //static int listbox_item_current2 = 2; //ImGui::PushItemWidth(-1); //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); //ImGui::PopItemWidth(); @@ -8455,14 +8987,32 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::SameLine(); ImGui::Checkbox("Rich", &c4); - // SliderFloat + // Various static float f0=1.0f, f1=2.0f, f2=3.0f; ImGui::PushItemWidth(80); + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; + static int item = -1; + ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); + ImGui::SameLine(); ImGui::SliderFloat("X", &f0, 0.0f,5.0f); ImGui::SameLine(); ImGui::SliderFloat("Y", &f1, 0.0f,5.0f); ImGui::SameLine(); ImGui::SliderFloat("Z", &f2, 0.0f,5.0f); + ImGui::PopItemWidth(); + + ImGui::PushItemWidth(80); + ImGui::Text("Lists:"); + static int selection[4] = { 0, 1, 2, 3 }; + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::PopID(); + //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + } + ImGui::PopItemWidth(); } if (ImGui::CollapsingHeader("Child regions")) @@ -8505,34 +9055,59 @@ void ImGui::ShowTestWindow(bool* opened) if (ImGui::CollapsingHeader("Columns")) { - ImGui::Columns(4, "data", true); + // Basic columns + ImGui::Text("Basic:"); + ImGui::Columns(4, "mycolumns"); ImGui::Text("ID"); ImGui::NextColumn(); ImGui::Text("Name"); ImGui::NextColumn(); ImGui::Text("Path"); ImGui::NextColumn(); ImGui::Text("Flags"); ImGui::NextColumn(); ImGui::Separator(); + const char* names[3] = { "Robert", "Stephanie", "C64" }; + const char* paths[3] = { "/path/robert", "/path/stephanie", "/path/computer" }; + for (int i = 0; i < 3; i++) + { + ImGui::Text("%04d", i); ImGui::NextColumn(); + ImGui::Text(names[i]); ImGui::NextColumn(); + ImGui::Text(paths[i]); ImGui::NextColumn(); + ImGui::Text("...."); ImGui::NextColumn(); + } + ImGui::Columns(1); - ImGui::Text("0000"); ImGui::NextColumn(); - ImGui::Text("Robert"); ImGui::NextColumn(); - ImGui::Text("/path/robert"); ImGui::NextColumn(); - ImGui::Text("...."); ImGui::NextColumn(); - - ImGui::Text("0001"); ImGui::NextColumn(); - ImGui::Text("Stephanie"); ImGui::NextColumn(); - ImGui::Text("/path/stephanie"); ImGui::NextColumn(); - ImGui::Text("line 1"); ImGui::Text("line 2"); ImGui::NextColumn(); // two lines, two items + ImGui::Separator(); + ImGui::Spacing(); - ImGui::Text("0002"); ImGui::NextColumn(); - ImGui::Text("C64"); ImGui::NextColumn(); - ImGui::Text("/path/computer"); ImGui::NextColumn(); - ImGui::Text("...."); ImGui::NextColumn(); + // Scrolling columns + /* + ImGui::Text("Scrolling:"); + ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); + ImGui::Columns(3); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); ImGui::Columns(1); + ImGui::Separator(); + ImGui::EndChild(); + ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); + ImGui::Columns(3); + for (int i = 0; i < 10; i++) + { + ImGui::Text("%04d", i); ImGui::NextColumn(); + ImGui::Text("Foobar"); ImGui::NextColumn(); + ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::EndChild(); ImGui::Separator(); + ImGui::Spacing(); + */ + // Create multiple items in a same cell before switching to next column + ImGui::Text("Mixed items:"); ImGui::Columns(3, "mixed"); + ImGui::Separator(); - // Create multiple items in a same cell because switching to next column static int e = 0; ImGui::Text("Hello"); ImGui::Button("Banana"); @@ -8542,38 +9117,41 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::Text("ImGui"); ImGui::Button("Apple"); ImGui::RadioButton("radio b", &e, 1); + static float foo = 1.0f; + ImGui::InputFloat("red", &foo, 0.05f, 0, 3); ImGui::Text("An extra line here."); ImGui::NextColumn(); ImGui::Text("World!"); ImGui::Button("Corniflower"); ImGui::RadioButton("radio c", &e, 2); + static float bar = 1.0f; + ImGui::InputFloat("blue", &bar, 0.05f, 0, 3); ImGui::NextColumn(); if (ImGui::CollapsingHeader("Category A")) ImGui::Text("Blah blah blah"); ImGui::NextColumn(); if (ImGui::CollapsingHeader("Category B")) ImGui::Text("Blah blah blah"); ImGui::NextColumn(); if (ImGui::CollapsingHeader("Category C")) ImGui::Text("Blah blah blah"); ImGui::NextColumn(); - - ImGui::Columns(1); - - ImGui::Separator(); - - ImGui::Columns(2, "multiple components"); - static float foo = 1.0f; - ImGui::InputFloat("red", &foo, 0.05f, 0, 3); ImGui::NextColumn(); - static float bar = 1.0f; - ImGui::InputFloat("blue", &bar, 0.05f, 0, 3); ImGui::NextColumn(); ImGui::Columns(1); ImGui::Separator(); + ImGui::Spacing(); + // Tree items + ImGui::Text("Tree items:"); ImGui::Columns(2, "tree items"); + ImGui::Separator(); if (ImGui::TreeNode("Hello")) { ImGui::BulletText("World"); ImGui::TreePop(); } ImGui::NextColumn(); - if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Monde"); ImGui::TreePop(); } + if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Monde"); ImGui::TreePop(); } ImGui::NextColumn(); ImGui::Columns(1); + ImGui::Separator(); + ImGui::Spacing(); - ImGui::Columns(2, "word wrapping"); + // Word-wrapping + ImGui::Text("Word-wrapping:"); + ImGui::Columns(2, "word-wrapping"); + ImGui::Separator(); ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); ImGui::Text("Hello Left"); ImGui::NextColumn(); @@ -8582,34 +9160,29 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::Columns(1); ImGui::Separator(); + ImGui::Spacing(); if (ImGui::TreeNode("Inside a tree..")) { if (ImGui::TreeNode("node 1 (with borders)")) { ImGui::Columns(4); - ImGui::Text("aaa"); ImGui::NextColumn(); - ImGui::Text("bbb"); ImGui::NextColumn(); - ImGui::Text("ccc"); ImGui::NextColumn(); - ImGui::Text("ddd"); ImGui::NextColumn(); - ImGui::Text("eee"); ImGui::NextColumn(); - ImGui::Text("fff"); ImGui::NextColumn(); - ImGui::Text("ggg"); ImGui::NextColumn(); - ImGui::Text("hhh"); ImGui::NextColumn(); + for (int i = 0; i < 8; i++) + { + ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); + ImGui::NextColumn(); + } ImGui::Columns(1); ImGui::TreePop(); } if (ImGui::TreeNode("node 2 (without borders)")) { ImGui::Columns(4, NULL, false); - ImGui::Text("aaa"); ImGui::NextColumn(); - ImGui::Text("bbb"); ImGui::NextColumn(); - ImGui::Text("ccc"); ImGui::NextColumn(); - ImGui::Text("ddd"); ImGui::NextColumn(); - ImGui::Text("eee"); ImGui::NextColumn(); - ImGui::Text("fff"); ImGui::NextColumn(); - ImGui::Text("ggg"); ImGui::NextColumn(); - ImGui::Text("hhh"); ImGui::NextColumn(); + for (int i = 0; i < 8; i++) + { + ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); + ImGui::NextColumn(); + } ImGui::Columns(1); ImGui::TreePop(); } @@ -8674,33 +9247,20 @@ void ImGui::ShowTestWindow(bool* opened) ImGui::Text("Item with focus: %d", has_focus); else ImGui::Text("Item with focus: "); + ImGui::TextWrapped("Cursor & selection are preserved when refocusing last used item in code."); ImGui::TreePop(); } } - static bool show_app_console = false; - static bool show_app_long_text = false; - static bool show_app_auto_resize = false; - static bool show_app_fixed_overlay = false; - static bool show_app_custom_rendering = false; if (ImGui::CollapsingHeader("App Examples")) { ImGui::Checkbox("Console", &show_app_console); ImGui::Checkbox("Long text display", &show_app_long_text); ImGui::Checkbox("Auto-resizing window", &show_app_auto_resize); ImGui::Checkbox("Simple overlay", &show_app_fixed_overlay); + ImGui::Checkbox("Manipulating window title", &show_app_manipulating_window_title); ImGui::Checkbox("Custom rendering", &show_app_custom_rendering); } - if (show_app_console) - ShowExampleAppConsole(&show_app_console); - if (show_app_long_text) - ShowExampleAppLongText(&show_app_long_text); - if (show_app_auto_resize) - ShowExampleAppAutoResize(&show_app_auto_resize); - if (show_app_fixed_overlay) - ShowExampleAppFixedOverlay(&show_app_fixed_overlay); - if (show_app_custom_rendering) - ShowExampleAppCustomRendering(&show_app_custom_rendering); ImGui::End(); } @@ -8738,8 +9298,34 @@ static void ShowExampleAppFixedOverlay(bool* opened) ImGui::End(); } +static void ShowExampleAppManipulatingWindowTitle(bool* /*opened*/) +{ + // By default, Windows are uniquely identified by their title. + // You can use the "##" and "###" markers to manipulate the display/ID. Read FAQ at the top of this file! + + // Using "##" to display same title but have unique identifier. + ImGui::SetNextWindowPos(ImVec2(100,100), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Same title as another window##1"); + ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); + ImGui::End(); + + ImGui::SetNextWindowPos(ImVec2(100,200), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Same title as another window##2"); + ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); + ImGui::End(); + + // Using "###" to display a changing title but keep a static identifier "MyWindow" + char buf[128]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "Animated title %c %d###MyWindow", "|/-\\"[(int)(ImGui::GetTime()/0.25f)&3], rand()); + ImGui::SetNextWindowPos(ImVec2(100,300), ImGuiSetCond_FirstUseEver); + ImGui::Begin(buf); + ImGui::Text("This window has a changing title."); + ImGui::End(); +} + static void ShowExampleAppCustomRendering(bool* opened) { + ImGui::SetNextWindowSize(ImVec2(300,350), ImGuiSetCond_FirstUseEver); if (!ImGui::Begin("Example: Custom Rendering", opened)) { ImGui::End(); @@ -8754,7 +9340,7 @@ static void ShowExampleAppCustomRendering(bool* opened) static ImVector points; static bool adding_line = false; if (ImGui::Button("Clear")) points.clear(); - if (points.size() > 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) points.pop_back(); } + if (points.size() >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } ImGui::Text("Left-click and drag to add lines"); ImGui::Text("Right-click to undo"); @@ -8860,6 +9446,8 @@ struct ExampleAppConsole if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.size()); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); if (ImGui::SmallButton("Add Dummy Error")) AddLog("[error] something went wrong"); ImGui::SameLine(); if (ImGui::SmallButton("Clear")) ClearLog(); + //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } + ImGui::Separator(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); @@ -8945,18 +9533,18 @@ struct ExampleAppConsole } } - static void TextEditCallbackStub(ImGuiTextEditCallbackData* data) + static int TextEditCallbackStub(ImGuiTextEditCallbackData* data) { ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; - console->TextEditCallback(data); + return console->TextEditCallback(data); } - void TextEditCallback(ImGuiTextEditCallbackData* data) + int TextEditCallback(ImGuiTextEditCallbackData* data) { //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); - switch (data->EventKey) + switch (data->EventFlag) { - case ImGuiKey_Tab: + case ImGuiInputTextFlags_CallbackCompletion: { // Example of TEXT COMPLETION @@ -8966,7 +9554,7 @@ struct ExampleAppConsole while (word_start > data->Buf) { const char c = word_start[-1]; - if (c == ' ' || c == '\t' || c == ',' || c == ';') + if (ImCharIsSpace(c) || c == ',' || c == ';') break; word_start--; } @@ -9021,8 +9609,7 @@ struct ExampleAppConsole break; } - case ImGuiKey_UpArrow: - case ImGuiKey_DownArrow: + case ImGuiInputTextFlags_CallbackHistory: { // Example of HISTORY const int prev_history_pos = HistoryPos; @@ -9049,6 +9636,7 @@ struct ExampleAppConsole } } } + return 0; } }; diff --git a/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.h b/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.h index cd94ff36129..f4f36f1833e 100644 --- a/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.h +++ b/3rdparty/bgfx/3rdparty/ocornut-imgui/imgui.h @@ -1,4 +1,4 @@ -// ImGui library v1.32 wip +// ImGui library v1.35 // See .cpp file for documentation. // See ImGui::ShowTestWindow() for sample code. // Read 'Programmer guide' in .cpp for notes on how to setup ImGui in your codebase. @@ -6,32 +6,33 @@ #pragma once -struct ImDrawCmd; -struct ImDrawList; -struct ImFont; -struct ImFontAtlas; -struct ImGuiAabb; -struct ImGuiIO; -struct ImGuiStorage; -struct ImGuiStyle; -struct ImGuiWindow; - -#include "imconfig.h" +#include "imconfig.h" // User-editable configuration file #include // FLT_MAX #include // va_list #include // ptrdiff_t #include // NULL, malloc #include // memset, memmove +// Define assertion handler. #ifndef IM_ASSERT #include #define IM_ASSERT(_EXPR) assert(_EXPR) #endif +// Define attributes of all API symbols declarations, e.g. for DLL under Windows. #ifndef IMGUI_API #define IMGUI_API #endif +// Forward declarations +struct ImDrawCmd; +struct ImDrawList; +struct ImFont; +struct ImFontAtlas; +struct ImGuiIO; +struct ImGuiStorage; +struct ImGuiStyle; + typedef unsigned int ImU32; typedef unsigned short ImWchar; // character for display typedef void* ImTextureID; // user data to refer to a texture (e.g. store your texture handle/id) @@ -41,9 +42,10 @@ typedef int ImGuiStyleVar; // enum ImGuiStyleVar_ typedef int ImGuiKey; // enum ImGuiKey_ typedef int ImGuiColorEditMode; // enum ImGuiColorEditMode_ typedef int ImGuiWindowFlags; // enum ImGuiWindowFlags_ -typedef int ImGuiSetCondition; // enum ImGuiSetCondition_ +typedef int ImGuiSetCond; // enum ImGuiSetCondition_ typedef int ImGuiInputTextFlags; // enum ImGuiInputTextFlags_ -struct ImGuiTextEditCallbackData; // for advanced uses of InputText() +struct ImGuiTextEditCallbackData; // for advanced uses of InputText() +typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); struct ImVec2 { @@ -74,7 +76,7 @@ namespace ImGui IMGUI_API void MemFree(void* ptr); } -// std::vector<> like class to avoid dragging dependencies (also: windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). +// std::vector<> like class to avoid dragging dependencies (also: windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). // Use '#define ImVector std::vector' if you want to use the STL type or your own type. // Our implementation does NOT call c++ constructors! because the data types we use don't need them (but that could be added as well). Only provide the minimum functionalities we need. #ifndef ImVector @@ -115,14 +117,14 @@ public: inline void swap(ImVector& rhs) { const size_t rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; const size_t rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } inline void resize(size_t new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; } - inline void reserve(size_t new_capacity) - { + inline void reserve(size_t new_capacity) + { if (new_capacity <= Capacity) return; T* new_data = (value_type*)ImGui::MemAlloc(new_capacity * sizeof(value_type)); memcpy(new_data, Data, Size * sizeof(value_type)); ImGui::MemFree(Data); Data = new_data; - Capacity = new_capacity; + Capacity = new_capacity; } inline void push_back(const value_type& v) { if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); Data[Size++] = v; } @@ -141,7 +143,7 @@ public: // - struct ImDrawList // Draw command list // - struct ImFont // TTF font loader, bake glyphs into bitmap -// ImGui End-user API +// ImGui end-user API // In a namespace so that user can add extra functions (e.g. Value() helpers for your vector or common types) namespace ImGui { @@ -156,10 +158,11 @@ namespace ImGui IMGUI_API void ShowTestWindow(bool* open = NULL); // Window - IMGUI_API bool Begin(const char* name = "Debug", bool* p_opened = NULL, ImVec2 size = ImVec2(0,0), float fill_alpha = -1.0f, ImGuiWindowFlags flags = 0);// return false when window is collapsed, so you can early out in your code. passing 'bool* p_opened' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. + // See implementation in .cpp for details + IMGUI_API bool Begin(const char* name = "Debug", bool* p_opened = NULL, const ImVec2& initial_size = ImVec2(0,0), float bg_alpha = -1.0f, ImGuiWindowFlags flags = 0); // return false when window is collapsed, so you can early out in your code. passing 'bool* p_opened' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. IMGUI_API void End(); - IMGUI_API void BeginChild(const char* str_id, ImVec2 size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). on each axis. - IMGUI_API void BeginChild(ImGuiID id, ImVec2 size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // " + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). on each axis. + IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // " IMGUI_API void EndChild(); IMGUI_API bool GetWindowIsFocused(); IMGUI_API ImVec2 GetContentRegionMax(); // window or current column boundaries, in windows coordinates @@ -173,13 +176,22 @@ namespace ImGui IMGUI_API ImVec2 GetWindowSize(); // get current window position. IMGUI_API float GetWindowWidth(); IMGUI_API bool GetWindowCollapsed(); - IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCondition cond = 0); // set next window position - call before Begin(). - IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiSetCondition cond = 0); // set next window size. set to ImVec2(0,0) to force an auto-fit. - IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiSetCondition cond = 0); // set next window collapsed state. - IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiSetCondition cond = 0); // set current window position - call within Begin()/End(). may incur tearing. - IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiSetCondition cond = 0); // set current window size. set to ImVec2(0,0) to force an auto-fit. may incur tearing. - IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiSetCondition cond = 0); // set current window collapsed state. + IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set next window position - call before Begin(). + IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // set next window size. set to ImVec2(0,0) to force an auto-fit. + IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // set next window collapsed state. + IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most + IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set current window position - call within Begin()/End(). may incur tearing. + IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // set current window size. set to ImVec2(0,0) to force an auto-fit. may incur tearing. + IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // set current window collapsed state. + IMGUI_API void SetWindowFocus(); // set current window to be focused / front-most + IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond = 0); // set named window position - call within Begin()/End(). may incur tearing. + IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond = 0); // set named window size. set to ImVec2(0,0) to force an auto-fit. may incur tearing. + IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond = 0); // set named window collapsed state. + IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / front-most + + IMGUI_API float GetScrollPosY(); // get scrolling position (0..GetScrollMaxY()) + IMGUI_API float GetScrollMaxY(); // get maximum scrolling position == ContentSize.Y - WindowSize.Y IMGUI_API void SetScrollPosHere(); // adjust scrolling position to center into the current cursor position. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. IMGUI_API void SetStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it). @@ -215,9 +227,11 @@ namespace ImGui IMGUI_API void Spacing(); IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border=true); // setup number of columns IMGUI_API void NextColumn(); // next column - IMGUI_API float GetColumnOffset(int column_index = -1); - IMGUI_API void SetColumnOffset(int column_index, float offset); - IMGUI_API float GetColumnWidth(int column_index = -1); + IMGUI_API int GetColumnIndex(); // get current column index + IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetcolumnsCount() inclusive. column 0 is usually 0.0f and not resizable unless you call this. + IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column. + IMGUI_API float GetColumnWidth(int column_index = -1); // column width (== GetColumnOffset(GetColumnIndex()+1) - GetColumnOffset(GetColumnOffset()) + IMGUI_API int GetColumnsCount(); // number of columns (what was passed to Columns()) IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position IMGUI_API float GetCursorPosX(); // " IMGUI_API float GetCursorPosY(); // " @@ -231,8 +245,8 @@ namespace ImGui IMGUI_API float GetTextLineHeightWithSpacing(); // spacing (in pixels) between 2 consecutive lines of text == GetWindowFontSize() + GetStyle().ItemSpacing.y // ID scopes - // If you are creating repeated widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them. - // You can also use ## within your widget name to distinguish them from each others (see 'Programmer Guide') + // If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them. + // You can also use "##extra" within your widget name to distinguish them from each others (see 'Programmer Guide') IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the *entire* stack! IMGUI_API void PushID(const void* ptr_id); IMGUI_API void PushID(const int int_id); @@ -248,8 +262,9 @@ namespace ImGui IMGUI_API void TextWrapped(const char* fmt, ...); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos(); IMGUI_API void TextWrappedV(const char* fmt, va_list args); IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // doesn't require null terminated string if 'text_end' is specified. no copy done to any bounded stack buffer, recommended for long chunks of text. - IMGUI_API void LabelText(const char* label, const char* fmt, ...); // display text+label aligned the same way as value+label widgets + IMGUI_API void LabelText(const char* label, const char* fmt, ...); // display text+label aligned the same way as value+label widgets IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args); + IMGUI_API void Bullet(); IMGUI_API void BulletText(const char* fmt, ...); IMGUI_API void BulletTextV(const char* fmt, va_list args); IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0), bool repeat_when_held = false); @@ -257,7 +272,7 @@ namespace ImGui IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); 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(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,1), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no paddnig. - IMGUI_API bool CollapsingHeader(const char* label, const char* str_id = NULL, const bool display_frame = true, const bool default_open = false); + IMGUI_API bool CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false); IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders. IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); @@ -275,7 +290,7 @@ namespace ImGui IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); IMGUI_API bool RadioButton(const char* label, bool active); IMGUI_API bool RadioButton(const char* label, int* v, int v_button); - IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, void (*callback)(ImGuiTextEditCallbackData*) = NULL, void* user_data = NULL); + IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1); IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1); @@ -288,6 +303,8 @@ namespace ImGui IMGUI_API bool ColorEdit3(const char* label, float col[3]); IMGUI_API bool ColorEdit4(const char* label, float col[4], bool show_alpha = true); IMGUI_API void ColorEditMode(ImGuiColorEditMode mode); + + // Trees IMGUI_API bool TreeNode(const char* str_label_id); // if returning 'true' the node is open and the user is responsible for calling TreePop IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...); // " IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...); // " @@ -296,7 +313,7 @@ namespace ImGui IMGUI_API void TreePush(const char* str_id = NULL); // already called by TreeNode(), but you can call Push/Pop yourself for layouting purpose IMGUI_API void TreePush(const void* ptr_id = NULL); // " IMGUI_API void TreePop(); - IMGUI_API void OpenNextNode(bool open); // force open/close the next TreeNode or CollapsingHeader + IMGUI_API void SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond = 0); // set next tree node to be opened. // Selectable / Lists IMGUI_API bool Selectable(const char* label, bool selected, const ImVec2& size = ImVec2(0,0)); @@ -326,6 +343,7 @@ namespace ImGui // Utilities IMGUI_API bool IsItemHovered(); // was the last item hovered by mouse? IMGUI_API bool IsItemActive(); // was the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) + IMGUI_API bool IsAnyItemActive(); // IMGUI_API ImVec2 GetItemBoxMin(); // get bounding box of last item IMGUI_API ImVec2 GetItemBoxMax(); // get bounding box of last item IMGUI_API bool IsClipped(const ImVec2& item_size); // to perform coarse clipping on user's side (as an optimization) @@ -357,6 +375,7 @@ namespace ImGui // Obsolete (will be removed) IMGUI_API void GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const void** png_data, unsigned int* png_size); + static inline void OpenNextNode(bool open) { ImGui::SetNextTreeNodeOpened(open, 0); } } // namespace ImGui @@ -367,7 +386,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window - ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scroll bar (window can still scroll with mouse or programatically) + ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbar (window can still scroll with mouse or programatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user scrolling with mouse wheel ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame @@ -387,12 +406,14 @@ enum ImGuiInputTextFlags_ // Default: 0 ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef - ImGuiInputTextFlags_AutoSelectAll = 1 << 2, // Select entire text when first taking focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 3, // Return 'true' when Enter is pressed (as opposed to when the value was modified) - ImGuiInputTextFlags_CallbackCompletion = 1 << 4, // Call user function on pressing TAB (for completion handling) - ImGuiInputTextFlags_CallbackHistory = 1 << 5, // Call user function on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 6 // Call user function every time - //ImGuiInputTextFlags_AlignCenter = 1 << 6, + ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z + ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs + ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) + ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) + ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) + ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time + ImGuiInputTextFlags_CallbackCharFilter = 1 << 9 // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. }; // User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array @@ -488,13 +509,13 @@ enum ImGuiColorEditMode_ ImGuiColorEditMode_HEX = 2 }; -// Condition flags for ImGui::SetWindow***() and SetNextWindow***() functions -// Those functions treat 0 as a shortcut to ImGuiSetCondition_Always -enum ImGuiSetCondition_ +// Condition flags for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions +// All those functions treat 0 as a shortcut to ImGuiSetCond_Always +enum ImGuiSetCond_ { - ImGuiSetCondition_Always = 1 << 0, // Set the variable - ImGuiSetCondition_FirstUseThisSession = 1 << 1, // Only set the variable on the first call for this window (once per session) - ImGuiSetCondition_FirstUseEver = 1 << 2 // Only set the variable if the window doesn't exist in the .ini file + ImGuiSetCond_Always = 1 << 0, // Set the variable + ImGuiSetCond_Once = 1 << 1, // Only set the variable on the first call per runtime session + ImGuiSetCond_FirstUseEver = 1 << 2 // Only set the variable if the window doesn't exist in the .ini file }; struct ImGuiStyle @@ -513,7 +534,8 @@ struct ImGuiStyle float WindowFillAlphaDefault; // Default alpha of window background, if not specified in ImGui::Begin() float TreeNodeSpacing; // Horizontal spacing when entering a tree node float ColumnsMinSpacing; // Minimum horizontal spacing between two columns - float ScrollBarWidth; // Width of the vertical scroll bar + float ScrollbarWidth; // Width of the vertical scrollbar + float GrabMinSize; // Minimum width/height of a slider or scrollbar grab ImVec4 Colors[ImGuiCol_COUNT]; IMGUI_API ImGuiStyle(); @@ -529,7 +551,7 @@ struct ImGuiIO ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. - float IniSavingRate; // = 5.0f // Maximum time between saving .ini file, in seconds. + float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. @@ -547,33 +569,36 @@ struct ImGuiIO // User Functions //------------------------------------------------------------------ - // REQUIRED: rendering function. + // REQUIRED: rendering function. // See example code if you are unsure of how to implement this. - void (*RenderDrawListsFn)(ImDrawList** const draw_lists, int count); + void (*RenderDrawListsFn)(ImDrawList** const draw_lists, int count); - // Optional: access OS clipboard (default to use native Win32 clipboard on Windows, otherwise use a ImGui private clipboard) - // Override to access OS clipboard on other architectures. + // Optional: access OS clipboard + // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) const char* (*GetClipboardTextFn)(); void (*SetClipboardTextFn)(const char* text); - // Optional: override memory allocations (default to posix malloc/free). MemFreeFn() may be called with a NULL pointer. + // Optional: override memory allocations. MemFreeFn() may be called with a NULL pointer. + // (default to posix malloc/free) void* (*MemAllocFn)(size_t sz); void (*MemFreeFn)(void* ptr); - // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese inputs in Windows) + // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) + // (default to use native imm32 api on Windows) void (*ImeSetInputScreenPosFn)(int x, int y); + void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. //------------------------------------------------------------------ // Input - Fill before calling NewFrame() //------------------------------------------------------------------ ImVec2 MousePos; // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) - bool MouseDown[5]; // Mouse buttons. ImGui itself only uses button 0 (left button) but you can use others as storage for convenience. - float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. + bool MouseDown[5]; // Mouse buttons. ImGui itself only uses button 0 (left button). Others buttons allows to track if mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift - bool KeysDown[512]; // Keyboard keys that are pressed (in whatever order user naturally has access to keyboard data) + bool KeysDown[512]; // Keyboard keys that are pressed (in whatever storage order you naturally have access to keyboard data) ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. // Function @@ -585,12 +610,13 @@ struct ImGuiIO bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) + float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames //------------------------------------------------------------------ // [Internal] ImGui will maintain those fields for you //------------------------------------------------------------------ - ImVec2 MousePosPrev; // + ImVec2 MousePosPrev; // Previous mouse position ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are negative to allow mouse enabling/disabling. bool MouseClicked[5]; // Mouse button went from !Down to Down ImVec2 MouseClickedPos[5]; // Position at time of clicking @@ -646,7 +672,7 @@ struct ImGuiTextFilter ImVector Filters; int CountGrep; - ImGuiTextFilter(); + ImGuiTextFilter(const char* default_filter = ""); void Clear() { InputBuf[0] = 0; Build(); } void Draw(const char* label = "Filter (inc,-exc)", float width = -1.0f); // Helper calling InputText+Build bool PassFilter(const char* val) const; @@ -680,13 +706,13 @@ struct ImGuiTextBuffer // - You want to store custom debug data easily without adding or editing structures in your code. struct ImGuiStorage { - struct Pair - { - ImGuiID key; - union { int val_i; float val_f; void* val_p; }; - Pair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } - Pair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } - Pair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } + struct Pair + { + ImGuiID key; + union { int val_i; float val_f; void* val_p; }; + Pair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } + Pair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } + Pair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } }; ImVector Data; @@ -701,7 +727,7 @@ struct ImGuiStorage IMGUI_API void* GetVoidPtr(ImGuiID key) const; // default_val is NULL IMGUI_API void SetVoidPtr(ImGuiID key, void* val); - // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. + // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. // - A typical use case where this is convenient: // float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar; @@ -716,15 +742,21 @@ struct ImGuiStorage // Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used. struct ImGuiTextEditCallbackData { - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only - char* Buf; // Current text // Read-write (pointed data only) - size_t BufSize; // // Read-only - bool BufDirty; // Set if you modify Buf directly // Write - ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only - int CursorPos; // // Read-write - int SelectionStart; // // Read-write (== to SelectionEnd when no selection) - int SelectionEnd; // // Read-write - void* UserData; // What user passed to InputText() + ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only + void* UserData; // What user passed to InputText() // Read-only + + // CharFilter event: + ImWchar EventChar; // Character input // Read-write (replace character or set to zero) + + // Completion,History,Always events: + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only + char* Buf; // Current text // Read-write (pointed data only) + size_t BufSize; // // Read-only + bool BufDirty; // Set if you modify Buf directly // Write + int CursorPos; // // Read-write + int SelectionStart; // // Read-write (== to SelectionEnd when no selection) + int SelectionEnd; // // Read-write // NB: calling those function loses selection. void DeleteChars(int pos, int bytes_count); @@ -740,7 +772,6 @@ struct ImColor ImColor(int r, int g, int b, int a = 255) { Value.x = (float)r / 255.0f; Value.y = (float)g / 255.0f; Value.z = (float)b / 255.0f; Value.w = (float)a / 255.0f; } ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; } ImColor(const ImVec4& col) { Value = col; } - operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } operator ImVec4() const { return Value; } @@ -763,7 +794,7 @@ struct ImColor // It is up to you to decide if your rendering loop or the callback should be responsible for backup/restoring rendering state. typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); -// Typically, 1 command = 1 gpu draw call +// Typically, 1 command = 1 gpu draw call (unless command is a callback) struct ImDrawCmd { unsigned int vtx_count; // Number of vertices (multiple of 3) to be drawn as triangles. The vertices are stored in the callee ImDrawList's vtx_buffer[] array. @@ -802,7 +833,7 @@ struct ImDrawList // [Internal to ImGui] ImVector clip_rect_stack; // [Internal] - ImVector texture_id_stack; // [Internal] + ImVector texture_id_stack; // [Internal] ImDrawVert* vtx_write; // [Internal] point within vtx_buffer after each add command (to avoid using the ImVector<> operators too much) ImDrawList() { Clear(); } @@ -812,7 +843,7 @@ struct ImDrawList IMGUI_API void PushTextureID(const ImTextureID& texture_id); IMGUI_API void PopTextureID(); - // Primitives + // Primitives IMGUI_API void AddLine(const ImVec2& a, const ImVec2& b, ImU32 col); IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners=0x0F); IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners=0x0F); @@ -897,7 +928,7 @@ struct ImFont float FontSize; // // Height of characters, set during loading (don't change after loading) float Scale; // = 1.0f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() ImVec2 DisplayOffset; // = (0.0f,0.0f) // Offset font rendering by xx pixels - ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. + ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() // Members: Runtime data struct Glyph @@ -910,8 +941,10 @@ struct ImFont }; ImFontAtlas* ContainerAtlas; // What we has been loaded into ImVector Glyphs; - ImVector IndexLookup; // Index glyphs by Unicode code-point const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + float FallbackXAdvance; // + ImVector IndexXAdvance; // Glyphs->XAdvance directly indexable (for CalcTextSize functions which are often bottleneck in large UI) + ImVector IndexLookup; // Index glyphs by Unicode code-point // Methods IMGUI_API ImFont(); @@ -919,6 +952,7 @@ struct ImFont IMGUI_API void Clear(); IMGUI_API void BuildLookupTable(); IMGUI_API const Glyph* FindGlyph(unsigned short c) const; + IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API bool IsLoaded() const { return ContainerAtlas != NULL; } // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. diff --git a/3rdparty/bgfx/README.md b/3rdparty/bgfx/README.md index 30df03b0008..bdad511f2cb 100644 --- a/3rdparty/bgfx/README.md +++ b/3rdparty/bgfx/README.md @@ -73,6 +73,14 @@ http://www.dogbytegames.com/ Dogbyte Games is an indie mobile developer studio focusing on racing games. ![ios](http://www.dogbytegames.com/bgfx/offroadlegends2_bgfx_ipad2.jpg) +https://github.com/andr3wmac/Torque6 Torque 6 is an MIT licensed 3D engine +loosely based on Torque2D. Being neither Torque2D or Torque3D it is the 6th +derivative of the original Torque Engine. + +https://github.com/cgbystrom/twinkle GPU-accelerated UI framework powered by +JavaScript for desktop/mobile apps. Idea is to combine the fast workflow and +deployment model of web with the performance of native code and GPU acceleration. + Examples -------- diff --git a/3rdparty/bgfx/examples/common/entry/entry.cpp b/3rdparty/bgfx/examples/common/entry/entry.cpp index 947d4204666..1007787a0bd 100644 --- a/3rdparty/bgfx/examples/common/entry/entry.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry.cpp @@ -34,6 +34,43 @@ namespace entry } #endif // ENTRY_CONFIG_IMPLEMENT_DEFAULT_ALLOCATOR + char keyToAscii(Key::Enum _key, uint8_t _modifiers) + { + const bool isAscii = (Key::Key0 <= _key && _key <= Key::KeyZ) + || (Key::Esc <= _key && _key <= Key::Minus); + if (!isAscii) + { + return '\0'; + } + + const bool isNumber = (Key::Key0 <= _key && _key <= Key::Key9); + if (isNumber) + { + return '0' + (_key - Key::Key0); + } + + const bool isChar = (Key::KeyA <= _key && _key <= Key::KeyZ); + if (isChar) + { + enum { ShiftMask = Modifier::LeftShift|Modifier::RightShift }; + + const bool shift = !!(_modifiers&ShiftMask); + return (shift ? 'A' : 'a') + (_key - Key::KeyA); + } + + switch (_key) + { + case Key::Esc: { return 0x1b; } break; + case Key::Return: { return 0x0d; } break; + case Key::Tab: { return 0x09; } break; + case Key::Space: { return 0xa0; } break; + case Key::Backspace: { return 0x08; } break; + case Key::Plus: { return 0x2b; } break; + case Key::Minus: { return 0x2d; } break; + default: { return '\0'; } break; + } + } + bool setOrToggle(uint32_t& _flags, const char* _name, uint32_t _bit, int _first, int _argc, char const* const* _argv) { if (0 == strcmp(_argv[_first], _name) ) diff --git a/3rdparty/bgfx/examples/common/entry/entry.h b/3rdparty/bgfx/examples/common/entry/entry.h index 7f6524b8a28..be1d35d75a2 100644 --- a/3rdparty/bgfx/examples/common/entry/entry.h +++ b/3rdparty/bgfx/examples/common/entry/entry.h @@ -211,6 +211,7 @@ namespace entry void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height); void setWindowTitle(WindowHandle _handle, const char* _title); void toggleWindowFrame(WindowHandle _handle); + void toggleFullscreen(WindowHandle _handle); void setMouseLock(WindowHandle _handle, bool _lock); struct WindowState diff --git a/3rdparty/bgfx/examples/common/entry/entry_android.cpp b/3rdparty/bgfx/examples/common/entry/entry_android.cpp index 5ad35d1c13c..2d7c852c86c 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_android.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_android.cpp @@ -440,6 +440,11 @@ namespace entry BX_UNUSED(_handle); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_asmjs.cpp b/3rdparty/bgfx/examples/common/entry/entry_asmjs.cpp index 6c221246e30..3ac5497b1d0 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_asmjs.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_asmjs.cpp @@ -59,6 +59,11 @@ namespace entry BX_UNUSED(_handle); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_ios.mm b/3rdparty/bgfx/examples/common/entry/entry_ios.mm index 9a9a2d6aa4b..8fc67ffe8c7 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_ios.mm +++ b/3rdparty/bgfx/examples/common/entry/entry_ios.mm @@ -126,6 +126,11 @@ namespace entry BX_UNUSED(_handle); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_nacl.cpp b/3rdparty/bgfx/examples/common/entry/entry_nacl.cpp index fca37180cc9..8d6fac7baff 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_nacl.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_nacl.cpp @@ -94,6 +94,11 @@ namespace entry BX_UNUSED(_handle); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_osx.mm b/3rdparty/bgfx/examples/common/entry/entry_osx.mm index 51e9d86be00..b46fbdb4b9f 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_osx.mm +++ b/3rdparty/bgfx/examples/common/entry/entry_osx.mm @@ -81,7 +81,9 @@ namespace entry , m_mx(0) , m_my(0) , m_scroll(0) + , m_style(0) , m_exit(false) + , m_fullscreen(false) { s_translateKey[27] = Key::Esc; s_translateKey[13] = Key::Return; @@ -138,7 +140,7 @@ namespace entry NSWindow* window = m_window[handle.idx]; NSRect originalFrame = [window frame]; NSPoint location = [window mouseLocationOutsideOfEventStream]; - NSRect adjustFrame = [NSWindow contentRectForFrameRect: originalFrame styleMask: NSTitledWindowMask]; + NSRect adjustFrame = [window contentRectForFrameRect: originalFrame]; int x = location.x; int y = (int)adjustFrame.size.height - (int)location.y; @@ -246,9 +248,9 @@ namespace entry case NSLeftMouseDown: { // TODO: remove! - // Shift + Left Mouse Button acts as middle! This just a temporary solution! + // Command + Left Mouse Button acts as middle! This just a temporary solution! // This is becase the average OSX user doesn't have middle mouse click. - MouseButton::Enum mb = ([event modifierFlags] & NSShiftKeyMask) ? MouseButton::Middle : MouseButton::Left; + MouseButton::Enum mb = ([event modifierFlags] & NSCommandKeyMask) ? MouseButton::Middle : MouseButton::Left; m_eventQueue.postMouseEvent(s_defaultWindow, m_mx, m_my, m_scroll, mb, true); break; } @@ -306,16 +308,22 @@ namespace entry { m_eventQueue.postExitEvent(); } - else if ( (Key::Key0 <= key && key <= Key::KeyZ) - || (Key::Esc <= key && key <= Key::Minus) ) - { - m_eventQueue.postCharEvent(s_defaultWindow, 1, pressedChar); - return false; - } else { - m_eventQueue.postKeyEvent(s_defaultWindow, key, modifiers, true); - return false; + enum { ShiftMask = Modifier::LeftShift|Modifier::RightShift }; + const bool nonShiftModifiers = (0 != (modifiers&(~ShiftMask) ) ); + const bool isCharPressed = (Key::Key0 <= key && key <= Key::KeyZ) || (Key::Esc <= key && key <= Key::Minus); + const bool isText = isCharPressed && !nonShiftModifiers; + if (isText) + { + m_eventQueue.postCharEvent(s_defaultWindow, 1, pressedChar); + return false; + } + else + { + m_eventQueue.postKeyEvent(s_defaultWindow, key, modifiers, true); + return false; + } } } @@ -354,7 +362,7 @@ namespace entry WindowHandle handle = { 0 }; NSWindow* window = m_window[handle.idx]; NSRect originalFrame = [window frame]; - NSRect rect = [NSWindow contentRectForFrameRect: originalFrame styleMask: NSTitledWindowMask]; + NSRect rect = [window contentRectForFrameRect: originalFrame]; uint32_t width = uint32_t(rect.size.width); uint32_t height = uint32_t(rect.size.height); m_eventQueue.postSizeEvent(handle, width, height); @@ -398,26 +406,33 @@ namespace entry [menubar addItem:appMenuItem]; [NSApp setMainMenu:menubar]; + m_style = 0 + | NSTitledWindowMask + | NSClosableWindowMask + | NSMiniaturizableWindowMask + | NSResizableWindowMask + ; + + NSRect screenRect = [[NSScreen mainScreen] frame]; + const float centerX = (screenRect.size.width - (float)ENTRY_DEFAULT_WIDTH )*0.5f; + const float centerY = (screenRect.size.height - (float)ENTRY_DEFAULT_HEIGHT)*0.5f; + m_windowAlloc.alloc(); - NSRect rect = NSMakeRect(0, 0, ENTRY_DEFAULT_WIDTH, ENTRY_DEFAULT_HEIGHT); + NSRect rect = NSMakeRect(centerX, centerY, (float)ENTRY_DEFAULT_WIDTH, (float)ENTRY_DEFAULT_HEIGHT); NSWindow* window = [[NSWindow alloc] initWithContentRect:rect - styleMask:0 - |NSTitledWindowMask - |NSClosableWindowMask - |NSMiniaturizableWindowMask - |NSResizableWindowMask + styleMask:m_style backing:NSBackingStoreBuffered defer:NO ]; NSString* appName = [[NSProcessInfo processInfo] processName]; [window setTitle:appName]; - [window cascadeTopLeftFromPoint:NSMakePoint(20,20)]; [window makeKeyAndOrderFront:window]; [window setAcceptsMouseMovedEvents:YES]; [window setBackgroundColor:[NSColor blackColor]]; [[Window sharedDelegate] windowCreated:window]; m_window[0] = window; + m_windowFrame = [window frame]; bgfx::osxSetNSWindow(window); @@ -448,16 +463,24 @@ namespace entry return 0; } + bool isValid(WindowHandle _handle) + { + return m_windowAlloc.isValid(_handle.idx); + } + EventQueue m_eventQueue; bx::HandleAllocT m_windowAlloc; NSWindow* m_window[ENTRY_CONFIG_MAX_WINDOWS]; + NSRect m_windowFrame; float m_scrollf; int32_t m_mx; int32_t m_my; int32_t m_scroll; + int32_t m_style; bool m_exit; + bool m_fullscreen; }; static Context s_ctx; @@ -486,27 +509,104 @@ namespace entry void destroyWindow(WindowHandle _handle) { - BX_UNUSED(_handle); + if (s_ctx.isValid(_handle) ) + { + dispatch_async(dispatch_get_main_queue() + , ^{ + [s_ctx.m_window[_handle.idx] performClose: nil]; + }); + } } void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y) { - BX_UNUSED(_handle, _x, _y); + if (s_ctx.isValid(_handle) ) + { + NSWindow* window = s_ctx.m_window[_handle.idx]; + NSScreen* screen = [window screen]; + + NSRect screenRect = [screen frame]; + CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; + + NSPoint position = { float(_x), screenRect.size.height - menuBarHeight - float(_y) }; + + dispatch_async(dispatch_get_main_queue() + , ^{ + [window setFrameTopLeftPoint: position]; + }); + } } void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height) { - BX_UNUSED(_handle, _width, _height); + if (s_ctx.isValid(_handle) ) + { + NSSize size = { float(_width), float(_height) }; + dispatch_async(dispatch_get_main_queue() + , ^{ + [s_ctx.m_window[_handle.idx] setContentSize: size]; + }); + } } void setWindowTitle(WindowHandle _handle, const char* _title) { - BX_UNUSED(_handle, _title); + if (s_ctx.isValid(_handle) ) + { + NSString* title = [[NSString alloc] initWithCString:_title encoding:1]; + dispatch_async(dispatch_get_main_queue() + , ^{ + [s_ctx.m_window[_handle.idx] setTitle: title]; + }); + [title release]; + } } void toggleWindowFrame(WindowHandle _handle) { - BX_UNUSED(_handle); + if (s_ctx.isValid(_handle) ) + { + s_ctx.m_style ^= NSTitledWindowMask; + dispatch_async(dispatch_get_main_queue() + , ^{ + [s_ctx.m_window[_handle.idx] setStyleMask: s_ctx.m_style]; + }); + } + } + + void toggleFullscreen(WindowHandle _handle) + { + if (s_ctx.isValid(_handle) ) + { + NSWindow* window = s_ctx.m_window[_handle.idx]; + NSScreen* screen = [window screen]; + NSRect screenRect = [screen frame]; + + if (!s_ctx.m_fullscreen) + { + [NSMenu setMenuBarVisible: false]; + s_ctx.m_style &= ~NSTitledWindowMask; + dispatch_async(dispatch_get_main_queue() + , ^{ + [window setStyleMask: s_ctx.m_style]; + [window setFrame:screenRect display:YES]; + }); + + s_ctx.m_fullscreen = true; + } + else + { + [NSMenu setMenuBarVisible: true]; + s_ctx.m_style |= NSTitledWindowMask; + dispatch_async(dispatch_get_main_queue() + , ^{ + [window setStyleMask: s_ctx.m_style]; + [window setFrame:s_ctx.m_windowFrame display:YES]; + }); + + s_ctx.m_fullscreen = false; + } + } } void setMouseLock(WindowHandle _handle, bool _lock) @@ -610,6 +710,7 @@ namespace entry using namespace entry; s_ctx.windowDidResize(); } + @end int main(int _argc, char** _argv) diff --git a/3rdparty/bgfx/examples/common/entry/entry_p.h b/3rdparty/bgfx/examples/common/entry/entry_p.h index 39a289ed737..74d4756b5cb 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_p.h +++ b/3rdparty/bgfx/examples/common/entry/entry_p.h @@ -56,6 +56,8 @@ namespace entry int main(int _argc, char** _argv); + char keyToAscii(Key::Enum _key, uint8_t _modifiers); + struct Event { enum Enum diff --git a/3rdparty/bgfx/examples/common/entry/entry_qnx.cpp b/3rdparty/bgfx/examples/common/entry/entry_qnx.cpp index 0ec4da6e1c8..c99393bb3ca 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_qnx.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_qnx.cpp @@ -59,6 +59,11 @@ namespace entry BX_UNUSED(_handle); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_sdl.cpp b/3rdparty/bgfx/examples/common/entry/entry_sdl.cpp index e44ebcefddb..c279f0fd2a6 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_sdl.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_sdl.cpp @@ -179,6 +179,7 @@ namespace entry SDL_USER_WINDOW_SET_POS, SDL_USER_WINDOW_SET_SIZE, SDL_USER_WINDOW_TOGGLE_FRAME, + SDL_USER_WINDOW_TOGGLE_FULL_SCREEN, SDL_USER_WINDOW_MOUSE_LOCK, }; @@ -191,7 +192,7 @@ namespace entry union { void* p; WindowHandle h; } cast; cast.h = _handle; uev.data1 = cast.p; - + uev.data2 = _msg; uev.code = _code; SDL_PushEvent(&event); @@ -211,6 +212,7 @@ namespace entry , m_height(ENTRY_DEFAULT_HEIGHT) , m_aspectRatio(16.0f/9.0f) , m_mouseLock(false) + , m_fullscreen(false) { memset(s_translateKey, 0, sizeof(s_translateKey) ); initTranslateKey(SDL_SCANCODE_ESCAPE, Key::Esc); @@ -410,6 +412,32 @@ namespace entry break; case SDL_KEYDOWN: + { + const SDL_KeyboardEvent& kev = event.key; + WindowHandle handle = findHandle(kev.windowID); + if (isValid(handle) ) + { + uint8_t modifiers = translateKeyModifiers(kev.keysym.mod); + Key::Enum key = translateKey(kev.keysym.scancode); + + const uint8_t shiftMask = Modifier::LeftShift|Modifier::RightShift; + const bool nonShiftModifiers = (0 != (modifiers&(~shiftMask) ) ); + const bool isCharPressed = (Key::Key0 <= key && key <= Key::KeyZ) || (Key::Esc <= key && key <= Key::Minus); + const bool isText = isCharPressed && !nonShiftModifiers; + + if (isText) + { + uint8_t pressedChar[4]; + pressedChar[0] = keyToAscii(key, modifiers); + m_eventQueue.postCharEvent(handle, 1, pressedChar); + } + else + { + m_eventQueue.postKeyEvent(handle, key, modifiers, kev.state == SDL_PRESSED); + } + } + } + break; case SDL_KEYUP: { const SDL_KeyboardEvent& kev = event.key; @@ -615,6 +643,14 @@ namespace entry } break; + case SDL_USER_WINDOW_TOGGLE_FULL_SCREEN: + { + WindowHandle handle = getWindowHandle(uev); + m_fullscreen = !m_fullscreen; + SDL_SetWindowFullscreen(m_window[handle.idx], m_fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + } + break; + case SDL_USER_WINDOW_MOUSE_LOCK: { SDL_SetRelativeMouseMode(!!uev.code ? SDL_TRUE : SDL_FALSE); @@ -669,19 +705,6 @@ namespace entry m_width = _width; m_height = _height; - if (m_width < m_height) - { - float aspectRatio = 1.0f/m_aspectRatio; - m_width = bx::uint32_max(ENTRY_DEFAULT_WIDTH/4, m_width); - m_height = uint32_t(float(m_width)*aspectRatio); - } - else - { - float aspectRatio = m_aspectRatio; - m_height = bx::uint32_max(ENTRY_DEFAULT_HEIGHT/4, m_height); - m_width = uint32_t(float(m_height)*aspectRatio); - } - SDL_SetWindowSize(m_window[_handle.idx], m_width, m_height); m_eventQueue.postSizeEvent(_handle, m_width, m_height); } @@ -723,6 +746,7 @@ namespace entry int32_t m_mx; int32_t m_my; bool m_mouseLock; + bool m_fullscreen; }; static Context s_ctx; @@ -805,6 +829,11 @@ namespace entry sdlPostEvent(SDL_USER_WINDOW_TOGGLE_FRAME, _handle); } + void toggleFullscreen(WindowHandle _handle) + { + sdlPostEvent(SDL_USER_WINDOW_TOGGLE_FULL_SCREEN, _handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { sdlPostEvent(SDL_USER_WINDOW_MOUSE_LOCK, _handle, NULL, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_windows.cpp b/3rdparty/bgfx/examples/common/entry/entry_windows.cpp index 335d5cb45b6..65fd6013c6b 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_windows.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_windows.cpp @@ -1067,6 +1067,11 @@ namespace entry PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_TOGGLE_FRAME, _handle.idx, 0); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_MOUSE_LOCK, _handle.idx, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_winrt.cpp b/3rdparty/bgfx/examples/common/entry/entry_winrt.cpp index f50b58a3960..e7cf8214de6 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_winrt.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_winrt.cpp @@ -175,6 +175,11 @@ namespace entry BX_UNUSED(_handle); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); diff --git a/3rdparty/bgfx/examples/common/entry/entry_x11.cpp b/3rdparty/bgfx/examples/common/entry/entry_x11.cpp index 627263faa23..249b8a0483c 100644 --- a/3rdparty/bgfx/examples/common/entry/entry_x11.cpp +++ b/3rdparty/bgfx/examples/common/entry/entry_x11.cpp @@ -664,6 +664,11 @@ namespace entry BX_UNUSED(_handle); } + void toggleFullscreen(WindowHandle _handle) + { + BX_UNUSED(_handle); + } + void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); diff --git a/3rdparty/bgfx/examples/common/imgui/ocornut_imgui.cpp b/3rdparty/bgfx/examples/common/imgui/ocornut_imgui.cpp index e77b58a59e7..62cb1c87a79 100644 --- a/3rdparty/bgfx/examples/common/imgui/ocornut_imgui.cpp +++ b/3rdparty/bgfx/examples/common/imgui/ocornut_imgui.cpp @@ -165,7 +165,7 @@ struct OcornutImguiContext ImGui::NewFrame(); -// ImGui::ShowTestWindow(); + ImGui::ShowTestWindow(); } void endFrame() diff --git a/3rdparty/bgfx/scripts/bgfx.lua b/3rdparty/bgfx/scripts/bgfx.lua index 8b67642d903..30646cfd1ac 100644 --- a/3rdparty/bgfx/scripts/bgfx.lua +++ b/3rdparty/bgfx/scripts/bgfx.lua @@ -46,6 +46,14 @@ function bgfxProject(_name, _kind, _defines) } end + if (_OPTIONS["vs"] == "vs2012-xp") + or (_OPTIONS["vs"] == "vs2013-xp") then + configuration { "vs201*" } + includedirs { + "$(DXSDK_DIR)/include", + } + end + configuration { "Debug" } defines { "BGFX_CONFIG_DEBUG=1", @@ -62,13 +70,6 @@ function bgfxProject(_name, _kind, _defines) "$(DXSDK_DIR)/include", } - if (_OPTIONS["vs"] == "vs2012-xp") or (_OPTIONS["vs"] == "vs2013-xp") then - configuration { "vs201*" } - includedirs { - "$(DXSDK_DIR)/include", - } - end - configuration { "winphone8*"} linkoptions { "/ignore:4264" -- LNK4264: archiving object file compiled with /ZW into a static library; note that when authoring Windows Runtime types it is not recommended to link with a static library that contains Windows Runtime metadata diff --git a/3rdparty/bx/include/bx/handlealloc.h b/3rdparty/bx/include/bx/handlealloc.h index 050590fdc97..25e250ec135 100644 --- a/3rdparty/bx/include/bx/handlealloc.h +++ b/3rdparty/bx/include/bx/handlealloc.h @@ -66,6 +66,14 @@ namespace bx return invalid; } + bool isValid(uint16_t _handle) + { + uint16_t* sparse = &m_handles[MaxHandlesT]; + uint16_t index = sparse[_handle]; + + return (index < m_numHandles && m_handles[index] == _handle); + } + void free(uint16_t _handle) { BX_CHECK(0 < m_numHandles, "Freeing invalid handle %d.", _handle); @@ -139,6 +147,14 @@ namespace bx return invalid; } + bool isValid(uint16_t _handle) + { + uint16_t* sparse = &m_handles[m_maxHandles]; + uint16_t index = sparse[_handle]; + + return (index < m_numHandles && m_handles[index] == _handle); + } + void free(uint16_t _handle) { uint16_t* sparse = &m_handles[m_maxHandles]; diff --git a/3rdparty/genie/README.md b/3rdparty/genie/README.md index 5f26ab8a49d..0fc9c18128d 100644 --- a/3rdparty/genie/README.md +++ b/3rdparty/genie/README.md @@ -14,7 +14,7 @@ Supported project generators: Download (stable) ----------------- - version 197 (commit 2b34cf0f0831d265b274d8e11a24bf6320d73d6e) + version 206 (commit e65d8143b1186496b9da03c6461a25402a2ee873) Linux: https://github.com/bkaradzic/bx/raw/master/tools/bin/linux/genie @@ -96,7 +96,7 @@ This long period between releases where multiple versions are in flight cause confusion for users who are not familiar with Premake, and they just need to use Premake to generate project files for project they are interested in. -I've been using Premake for [a while](https://carbongames.com/2011/08/Premake/), +I've been using Premake for [a while](https://web.archive.org/web/20120119020903/http://carbongames.com/2011/08/Premake), I really like it's simplicity, and that it does one thing really well. I was considering replacing Premake with other build systems that also could diff --git a/3rdparty/genie/src/actions/make/make_cpp.lua b/3rdparty/genie/src/actions/make/make_cpp.lua index ce8279ee0fa..d4f5c3b351a 100644 --- a/3rdparty/genie/src/actions/make/make_cpp.lua +++ b/3rdparty/genie/src/actions/make/make_cpp.lua @@ -5,6 +5,7 @@ -- premake.make.cpp = { } + premake.make.undefine = { } local cpp = premake.make.cpp local make = premake.make @@ -173,7 +174,10 @@ _p(' config=%s', _MAKE.esc(premake.getconfigname(prj.solution.configurations[1], platforms[1], true))) _p('endif') _p('') - + for _, variable in ipairs(premake.make.undefine) do + _p('override undefine '.. variable) + end + _p('') _p('ifndef verbose') _p(' SILENT = @') _p('endif') diff --git a/3rdparty/genie/src/host/premake.c b/3rdparty/genie/src/host/premake.c index 31bdb506f25..6a67371f901 100644 --- a/3rdparty/genie/src/host/premake.c +++ b/3rdparty/genie/src/host/premake.c @@ -359,9 +359,15 @@ int load_builtin_scripts(lua_State* L) } } + /* set error handler to get tracebacks in built-in scripts */ + lua_getglobal(L, "debug"); + lua_getfield(L, -1, "traceback"); + lua_remove(L, -2); + int ehpos = lua_gettop(L); + /* hand off control to the scripts */ lua_getglobal(L, "_premake_main"); - if (lua_pcall(L, 0, 1, 0) != OKAY) + if (lua_pcall(L, 0, 1, ehpos) != OKAY) { printf(ERROR_MESSAGE, lua_tostring(L, -1)); return !OKAY; diff --git a/3rdparty/genie/src/host/scripts.c b/3rdparty/genie/src/host/scripts.c index d2108413138..ab706827655 100644 --- a/3rdparty/genie/src/host/scripts.c +++ b/3rdparty/genie/src/host/scripts.c @@ -179,16 +179,17 @@ const char* builtin_scripts[] = { "ative(sln.location, prj.location)), _MAKE.esc(_MAKE.getmakefilename(prj, true)))\n_p('')\nend\n_p('clean:')\nfor _ ,prj in ipairs(sln.projects) do\n_p('\\t@${MAKE} --no-print-directory -C %s -f %s clean', _MAKE.esc(path.getrelative(sln.location, prj.location)), _MAKE.esc(_MAKE.getmakefilename(prj, true)))\nend\n_p('')\n_p('help:')\n_p(1,'@echo \"Usage: make [config=name] [target]\"')\n_p(1,'@echo \"\"')\n_p(1,'@echo \"CONFIGURATIONS:\"')\nlocal cfgpairs = { }\nfor _, platform in ipairs(platforms) do\nfor _, cfgname in ipairs(sln.configurations) do\n_p(1,'@echo \" %s\"', premake.getconfigname(cfgname, platform, true))\nend\nend\n_p(1,'@echo \"\"')\n_p(1,'@echo \"TARGETS:\"')\n_p(1,'@echo \" all (default)\"')\n_p(1,'@echo \" clean\"')\nfor _, prj in ipairs(sln.projects) do\n_p(1,'@echo \" %s\"', prj.name)\nend\n_p(1,'@echo \"\"')\n_p(1,'@echo \"For more information, see http://industriousone.com/premake/quick-start\"')\nend\n", /* actions/make/make_cpp.lua */ - "premake.make.cpp = { }\nlocal cpp = premake.make.cpp\nlocal make = premake.make\nfunction premake.make_cpp(prj)\nlocal cc = premake.gettool(prj)\nlocal platforms = premake.filterplatforms(prj.solution, cc.platforms, \"Native\")\npremake.gmake_cpp_header(prj, cc, platforms)\nfor _, platform in ipairs(platforms) do\nfor cfg in premake.eachconfig(prj, platform) do\npremake.gmake_cpp_config(prj, cfg, cc)\nend\nend\nlocal objdirs = {}\nfor _, file in ipairs(prj.files) do\nif path.iscppfile(file) then\nobjdirs[_MAKE.esc(path.getdirectory(path.trimdots(file)))] = 1\nend\nend\n_p('OBJDIRS := \\\\')\n_p('\\t$(OBJDIR) \\\\')\nfor dir, _ in pairs(objdirs) do\n_p('\\t$(OBJDIR)/%s \\\\', dir)\nend\n_p('')\n_p('RESOURCES := \\\\')\nfor _, file in ipairs(prj.files) do\nif path.isresourcefile(file) then\n_p('\\t$(OBJDIR)/%s.res \\\\', _MAKE.esc(path.getbasename(file)))\nend\nend\n_p('')\n_p('.PHONY: clean prebuild prelink')\n_p('')\nif os.is(\"MacOSX\") and prj.kind == \"WindowedApp\" then\n_p('all: $(TARGETDIR) $(OBJDIRS) pr" - "ebuild prelink $(TARGET) $(dir $(TARGETDIR))PkgInfo $(dir $(TARGETDIR))Info.plist')\nelse\n_p('all: $(TARGETDIR) $(OBJDIRS) prebuild prelink $(TARGET)')\nend\n_p('\\t@:')\n_p('')\nif (prj.kind == \"StaticLib\" and prj.options.ArchiveSplit) then\n_p('define max_args')\n_p('\\t$(eval _args:=)')\n_p('\\t$(foreach obj,$3,$(eval _args+=$(obj))$(if $(word $2,$(_args)),$1$(_args)$(EOL)$(eval _args:=)))')\n_p('\\t$(if $(_args),$1$(_args))')\n_p('endef')\n_p('')\n_p('define EOL')\n_p('')\n_p('')\n_p('endef')\n_p('')\nend\n_p('$(TARGET): $(GCH) $(OBJECTS) $(LDDEPS) $(RESOURCES)')\nif prj.kind == \"StaticLib\" then\nif prj.msgarchiving then\n_p('\\t@echo ' .. prj.msgarchiving)\nelse\n_p('\\t@echo Archiving %s', prj.name)\nend\nif (not prj.archivesplit_size) then \nprj.archivesplit_size=200\nend\nif (not prj.options.ArchiveSplit) then\n_p('\\t$(SILENT) $(LINKCMD) $(OBJECTS)')\nelse\n_p('\\t@$(call max_args,$(LINKCMD),'.. prj.archivesplit_size ..',$(OBJECTS))')\nend\nelse\nif prj.msglinking then\n_p('\\t@echo ' .. prj.msgl" - "inking)\nelse\n_p('\\t@echo Linking %s', prj.name)\nend\n_p('\\t$(SILENT) $(LINKCMD)')\nend\n_p('\\t$(POSTBUILDCMDS)')\n_p('')\n_p('$(TARGETDIR):')\npremake.make_mkdirrule(\"$(TARGETDIR)\")\n_p('$(OBJDIRS):')\nif (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, \"SkipCreatingMessage\")) then\n_p('\\t@echo Creating $(OBJDIR)')\nend\n_p('\\t-$(call MKDIR,$(OBJDIR))')\nfor dir, _ in pairs(objdirs) do\n_p('\\t-$(call MKDIR,$(OBJDIR)/%s)', dir)\nend\n_p('')\nif os.is(\"MacOSX\") and prj.kind == \"WindowedApp\" then\n_p('$(dir $(TARGETDIR))PkgInfo:')\n_p('$(dir $(TARGETDIR))Info.plist:')\n_p('')\nend\n_p('clean:')\nif (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, \"SkipCleaningMessage\")) then\n_p('\\t@echo Cleaning %s', prj.name)\nend\n_p('ifeq (posix,$(SHELLTYPE))')\n_p('\\t$(SILENT) rm -f $(TARGET)')\n_p('\\t$(SILENT) rm -rf $(OBJDIR)')\n_p('else')\n_p('\\t$(SILENT) if exist $(subst /,\\\\\\\\,$(TARGET)) del $(subst /,\\\\\\\\,$(TARGET))')\n_p('\\t" - "$(SILENT) if exist $(subst /,\\\\\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\\\\\,$(OBJDIR))')\n_p('endif')\n_p('')\n_p('prebuild:')\n_p('\\t$(PREBUILDCMDS)')\n_p('')\n_p('prelink:')\n_p('\\t$(PRELINKCMDS)')\n_p('')\ncpp.pchrules(prj)\ncpp.fileRules(prj)\n_p('-include $(OBJECTS:%%.o=%%.d)')\n_p('ifneq (,$(PCH))')\n_p(' -include $(OBJDIR)/$(notdir $(PCH)).d')\n_p('endif')\nend\nfunction premake.gmake_cpp_header(prj, cc, platforms)\n_p('# %s project makefile autogenerated by GENie', premake.action.current().shortname)\n_p('ifndef config')\n_p(' config=%s', _MAKE.esc(premake.getconfigname(prj.solution.configurations[1], platforms[1], true)))\n_p('endif')\n_p('')\n_p('ifndef verbose')\n_p(' SILENT = @')\n_p('endif')\n_p('')\n_p('SHELLTYPE := msdos')\n_p('ifeq (,$(ComSpec)$(COMSPEC))')\n_p(' SHELLTYPE := posix')\n_p('endif')\n_p('ifeq (/bin,$(findstring /bin,$(SHELL)))')\n_p(' SHELLTYPE := posix')\n_p('endif')\n_p('')\n_p('ifeq (posix,$(SHELLTYPE))')\n_p(' MKDIR = $(SILENT) mkdir -p \"$(1)\"')\n_p(' COPY = $(" - "SILENT) cp -fR \"$(1)\" \"$(2)\"')\n_p('else')\n_p(' MKDIR = $(SILENT) mkdir \"$(subst /,\\\\\\\\,$(1))\" 2> nul || exit 0')\n_p(' COPY = $(SILENT) copy /Y \"$(subst /,\\\\\\\\,$(1))\" \"$(subst /,\\\\\\\\,$(2))\"')\n_p('endif')\n_p('')\n_p('CC = %s', cc.cc)\n_p('CXX = %s', cc.cxx)\n_p('AR = %s', cc.ar)\n_p('')\n_p('ifndef RESCOMP')\n_p(' ifdef WINDRES')\n_p(' RESCOMP = $(WINDRES)')\n_p(' else')\n_p(' RESCOMP = windres')\n_p(' endif')\n_p('endif')\n_p('')\nend\nfunction premake.gmake_cpp_config(prj, cfg, cc)\n_p('ifeq ($(config),%s)', _MAKE.esc(cfg.shortname))\ncpp.platformtools(cfg, cc)\n_p(' OBJDIR = %s', _MAKE.esc(cfg.objectsdir))\n_p(' TARGETDIR = %s', _MAKE.esc(cfg.buildtarget.directory))\n_p(' TARGET = $(TARGETDIR)/%s', _MAKE.esc(cfg.buildtarget.name))\n_p(' DEFINES +=%s', make.list(cc.getdefines(cfg.defines)))\n_p(' INCLUDES +=%s', make.list(cc.getincludedirs(cfg.includedirs)))\ncpp.pchconfig(cfg)\ncpp.flags(cfg, cc)\ncpp.linker(cfg, cc)\n_p(' OBJECTS := \\\\')\nfor _, fi" - "le in ipairs(prj.files) do\nif path.iscppfile(file) then\nlocal excluded = false\nfor _, exclude in ipairs(cfg.excludes) do\nexcluded = (exclude == file)\nif (excluded) then break end\nend\nif excluded == false then\n_p('\\t$(OBJDIR)/%s.o \\\\'\n, _MAKE.esc(path.trimdots(path.removeext(file)))\n)\nend\nend\nend\n_p('')\n_p(' define PREBUILDCMDS')\nif #cfg.prebuildcommands > 0 then\n_p('\\t@echo Running pre-build commands')\n_p('\\t%s', table.implode(cfg.prebuildcommands, \"\", \"\", \"\\n\\t\"))\nend\n_p(' endef')\n_p(' define PRELINKCMDS')\nif #cfg.prelinkcommands > 0 then\n_p('\\t@echo Running pre-link commands')\n_p('\\t%s', table.implode(cfg.prelinkcommands, \"\", \"\", \"\\n\\t\"))\nend\n_p(' endef')\n_p(' define POSTBUILDCMDS')\nif #cfg.postbuildcommands > 0 then\n_p('\\t@echo Running post-build commands')\n_p('\\t%s', table.implode(cfg.postbuildcommands, \"\", \"\", \"\\n\\t\"))\nend\n_p(' endef')\nmake.settings(cfg, cc)\n_p('endif')\n_p('')\nend\nfunction cpp.platformtools(cfg, cc)\nlocal platfor" - "m = cc.platforms[cfg.platform]\nif platform.cc then\n_p(' CC = %s', platform.cc)\nend\nif platform.cxx then\n_p(' CXX = %s', platform.cxx)\nend\nif platform.ar then\n_p(' AR = %s', platform.ar)\nend\nend\nfunction cpp.flags(cfg, cc)\nif cfg.pchheader and not cfg.flags.NoPCH then\n_p(' FORCE_INCLUDE += -include $(OBJDIR)/$(notdir $(PCH))')\nend\nif #cfg.forcedincludes > 0 then\n_p(' FORCE_INCLUDE += -include %s'\n,premake.esc(table.concat(cfg.forcedincludes, \";\")))\nend\n_p(' ALL_CPPFLAGS += $(CPPFLAGS) %s $(DEFINES) $(INCLUDES)', table.concat(cc.getcppflags(cfg), \" \"))\n_p(' ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcflags(cfg), cfg.buildoptions, cfg.buildoptions_c)))\n_p(' ALL_CXXFLAGS += $(CXXFLAGS) $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcxxflags(cfg), cfg.buildoptions, cfg.buildoptions_cpp)))\n_p(' ALL_OBJCFLAGS += $(CXXFLAGS) $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcxxflags(cfg)," - " cfg.buildoptions, cfg.buildoptions_objc)))\n_p(' ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)%s',\n make.list(table.join(cc.getdefines(cfg.resdefines),\n cc.getincludedirs(cfg.resincludedirs), cfg.resoptions)))\nend\nfunction cpp.linker(cfg, cc)\n_p(' ALL_LDFLAGS += $(LDFLAGS)%s', make.list(table.join(cc.getlibdirflags(cfg), cc.getldflags(cfg), cfg.linkoptions)))\n_p(' LDDEPS +=%s', make.list(_MAKE.esc(premake.getlinks(cfg, \"siblings\", \"fullpath\"))))\n_p(' LIBS += $(LDDEPS)%s', make.list(cc.getlinkflags(cfg)))\nif cfg.kind == \"StaticLib\" then\nif cfg.platform:startswith(\"Universal\") then\n_p(' LINKCMD = libtool -o $(TARGET)')\nelse\nif cc.llvm then\n_p(' LINKCMD = $(AR) rcs $(TARGET)')\nelse\n_p(' LINKCMD = $(AR) -rcs $(TARGET)')\nend\nend\nelse\nlocal tool = iif(cfg.language == \"C\", \"CC\", \"CXX\")\n_p(' LINKCMD = $(%s) -o $(TARGET) $(OBJECTS) $(RESOURCES) $(ARCH) $(ALL_LDFLAGS) $(LIBS)', tool)\nend\nend\nfunction cpp.pchco" - "nfig(cfg)\nif not cfg.pchheader or cfg.flags.NoPCH then\nreturn\nend\nlocal pch = cfg.pchheader\nfor _, incdir in ipairs(cfg.includedirs) do\nlocal abspath = path.getabsolute(path.join(cfg.project.location, incdir))\nlocal testname = path.join(abspath, pch)\nif os.isfile(testname) then\npch = path.getrelative(cfg.location, testname)\nbreak\nend\nend\n_p(' PCH = %s', _MAKE.esc(pch))\n_p(' GCH = $(OBJDIR)/$(notdir $(PCH)).gch')\nend\nfunction cpp.pchrules(prj)\n_p('ifneq (,$(PCH))')\n_p('$(GCH): $(PCH)')\n_p('\\t@echo $(notdir $<)')\nlocal cmd = iif(prj.language == \"C\", \"$(CC) -x c-header $(ALL_CFLAGS)\", \"$(CXX) -x c++-header $(ALL_CXXFLAGS)\")\n_p('\\t$(SILENT) %s -MMD -MP $(DEFINES) $(INCLUDES) -o \"$@\" -MF \"$(@:%%.gch=%%.d)\" -c \"$<\"', cmd)\n_p('endif')\n_p('')\nend\nfunction cpp.fileRules(prj)\nfor _, file in ipairs(prj.files or {}) do\nif path.iscppfile(file) then\n_p('$(OBJDIR)/%s.o: %s'\n, _MAKE.esc(path.trimdots(path.removeext(file)))\n, _MAKE.esc(file)\n)\nif (path.isobjcfile(fi" - "le) and prj.msgcompile_objc) then\n_p('\\t@echo ' .. prj.msgcompile_objc)\nelseif prj.msgcompile then\n_p('\\t@echo ' .. prj.msgcompile)\nelse\n_p('\\t@echo $(notdir $<)')\nend\nif (path.isobjcfile(file)) then\n_p('\\t$(SILENT) $(CXX) $(ALL_OBJCFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF $(@:%%.o=%%.d) -c \"$<\"')\nelse\ncpp.buildcommand(path.iscfile(file) and not prj.options.ForceCPP, \"o\")\nend\n_p('')\nelseif (path.getextension(file) == \".rc\") then\n_p('$(OBJDIR)/%s.res: %s', _MAKE.esc(path.getbasename(file)), _MAKE.esc(file))\nif prj.msgresource then\n_p('\\t@echo ' .. prj.msgresource)\nelse\n_p('\\t@echo $(notdir $<)')\nend\n_p('\\t$(SILENT) $(RESCOMP) $< -O coff -o \"$@\" $(ALL_RESFLAGS)')\n_p('')\nend\nend\nend\nfunction cpp.buildcommand(iscfile, objext)\nlocal flags = iif(iscfile, '$(CC) $(ALL_CFLAGS)', '$(CXX) $(ALL_CXXFLAGS)')\n_p('\\t$(SILENT) %s $(FORCE_INCLUDE) -o \"$@\" -MF $(@:%%.%s=%%.d) -c \"$<\"', flags, objext)\nend\n", + "premake.make.cpp = { }\npremake.make.undefine = { }\nlocal cpp = premake.make.cpp\nlocal make = premake.make\nfunction premake.make_cpp(prj)\nlocal cc = premake.gettool(prj)\nlocal platforms = premake.filterplatforms(prj.solution, cc.platforms, \"Native\")\npremake.gmake_cpp_header(prj, cc, platforms)\nfor _, platform in ipairs(platforms) do\nfor cfg in premake.eachconfig(prj, platform) do\npremake.gmake_cpp_config(prj, cfg, cc)\nend\nend\nlocal objdirs = {}\nfor _, file in ipairs(prj.files) do\nif path.iscppfile(file) then\nobjdirs[_MAKE.esc(path.getdirectory(path.trimdots(file)))] = 1\nend\nend\n_p('OBJDIRS := \\\\')\n_p('\\t$(OBJDIR) \\\\')\nfor dir, _ in pairs(objdirs) do\n_p('\\t$(OBJDIR)/%s \\\\', dir)\nend\n_p('')\n_p('RESOURCES := \\\\')\nfor _, file in ipairs(prj.files) do\nif path.isresourcefile(file) then\n_p('\\t$(OBJDIR)/%s.res \\\\', _MAKE.esc(path.getbasename(file)))\nend\nend\n_p('')\n_p('.PHONY: clean prebuild prelink')\n_p('')\nif os.is(\"MacOSX\") and prj.kind == \"WindowedApp\" then\n_p('al" + "l: $(TARGETDIR) $(OBJDIRS) prebuild prelink $(TARGET) $(dir $(TARGETDIR))PkgInfo $(dir $(TARGETDIR))Info.plist')\nelse\n_p('all: $(TARGETDIR) $(OBJDIRS) prebuild prelink $(TARGET)')\nend\n_p('\\t@:')\n_p('')\nif (prj.kind == \"StaticLib\" and prj.options.ArchiveSplit) then\n_p('define max_args')\n_p('\\t$(eval _args:=)')\n_p('\\t$(foreach obj,$3,$(eval _args+=$(obj))$(if $(word $2,$(_args)),$1$(_args)$(EOL)$(eval _args:=)))')\n_p('\\t$(if $(_args),$1$(_args))')\n_p('endef')\n_p('')\n_p('define EOL')\n_p('')\n_p('')\n_p('endef')\n_p('')\nend\n_p('$(TARGET): $(GCH) $(OBJECTS) $(LDDEPS) $(RESOURCES)')\nif prj.kind == \"StaticLib\" then\nif prj.msgarchiving then\n_p('\\t@echo ' .. prj.msgarchiving)\nelse\n_p('\\t@echo Archiving %s', prj.name)\nend\nif (not prj.archivesplit_size) then \nprj.archivesplit_size=200\nend\nif (not prj.options.ArchiveSplit) then\n_p('\\t$(SILENT) $(LINKCMD) $(OBJECTS)')\nelse\n_p('\\t@$(call max_args,$(LINKCMD),'.. prj.archivesplit_size ..',$(OBJECTS))')\nend\nelse\nif prj.msglinking the" + "n\n_p('\\t@echo ' .. prj.msglinking)\nelse\n_p('\\t@echo Linking %s', prj.name)\nend\n_p('\\t$(SILENT) $(LINKCMD)')\nend\n_p('\\t$(POSTBUILDCMDS)')\n_p('')\n_p('$(TARGETDIR):')\npremake.make_mkdirrule(\"$(TARGETDIR)\")\n_p('$(OBJDIRS):')\nif (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, \"SkipCreatingMessage\")) then\n_p('\\t@echo Creating $(OBJDIR)')\nend\n_p('\\t-$(call MKDIR,$(OBJDIR))')\nfor dir, _ in pairs(objdirs) do\n_p('\\t-$(call MKDIR,$(OBJDIR)/%s)', dir)\nend\n_p('')\nif os.is(\"MacOSX\") and prj.kind == \"WindowedApp\" then\n_p('$(dir $(TARGETDIR))PkgInfo:')\n_p('$(dir $(TARGETDIR))Info.plist:')\n_p('')\nend\n_p('clean:')\nif (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, \"SkipCleaningMessage\")) then\n_p('\\t@echo Cleaning %s', prj.name)\nend\n_p('ifeq (posix,$(SHELLTYPE))')\n_p('\\t$(SILENT) rm -f $(TARGET)')\n_p('\\t$(SILENT) rm -rf $(OBJDIR)')\n_p('else')\n_p('\\t$(SILENT) if exist $(subst /,\\\\\\\\,$(TARGET)) del $(subst /," + "\\\\\\\\,$(TARGET))')\n_p('\\t$(SILENT) if exist $(subst /,\\\\\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\\\\\,$(OBJDIR))')\n_p('endif')\n_p('')\n_p('prebuild:')\n_p('\\t$(PREBUILDCMDS)')\n_p('')\n_p('prelink:')\n_p('\\t$(PRELINKCMDS)')\n_p('')\ncpp.pchrules(prj)\ncpp.fileRules(prj)\n_p('-include $(OBJECTS:%%.o=%%.d)')\n_p('ifneq (,$(PCH))')\n_p(' -include $(OBJDIR)/$(notdir $(PCH)).d')\n_p('endif')\nend\nfunction premake.gmake_cpp_header(prj, cc, platforms)\n_p('# %s project makefile autogenerated by GENie', premake.action.current().shortname)\n_p('ifndef config')\n_p(' config=%s', _MAKE.esc(premake.getconfigname(prj.solution.configurations[1], platforms[1], true)))\n_p('endif')\n_p('')\nfor _, variable in ipairs(premake.make.undefine) do\n_p('override undefine '.. variable)\nend\n_p('')\n_p('ifndef verbose')\n_p(' SILENT = @')\n_p('endif')\n_p('')\n_p('SHELLTYPE := msdos')\n_p('ifeq (,$(ComSpec)$(COMSPEC))')\n_p(' SHELLTYPE := posix')\n_p('endif')\n_p('ifeq (/bin,$(findstring /bin,$(SHELL)))')\n_p(' SHEL" + "LTYPE := posix')\n_p('endif')\n_p('')\n_p('ifeq (posix,$(SHELLTYPE))')\n_p(' MKDIR = $(SILENT) mkdir -p \"$(1)\"')\n_p(' COPY = $(SILENT) cp -fR \"$(1)\" \"$(2)\"')\n_p('else')\n_p(' MKDIR = $(SILENT) mkdir \"$(subst /,\\\\\\\\,$(1))\" 2> nul || exit 0')\n_p(' COPY = $(SILENT) copy /Y \"$(subst /,\\\\\\\\,$(1))\" \"$(subst /,\\\\\\\\,$(2))\"')\n_p('endif')\n_p('')\n_p('CC = %s', cc.cc)\n_p('CXX = %s', cc.cxx)\n_p('AR = %s', cc.ar)\n_p('')\n_p('ifndef RESCOMP')\n_p(' ifdef WINDRES')\n_p(' RESCOMP = $(WINDRES)')\n_p(' else')\n_p(' RESCOMP = windres')\n_p(' endif')\n_p('endif')\n_p('')\nend\nfunction premake.gmake_cpp_config(prj, cfg, cc)\n_p('ifeq ($(config),%s)', _MAKE.esc(cfg.shortname))\ncpp.platformtools(cfg, cc)\n_p(' OBJDIR = %s', _MAKE.esc(cfg.objectsdir))\n_p(' TARGETDIR = %s', _MAKE.esc(cfg.buildtarget.directory))\n_p(' TARGET = $(TARGETDIR)/%s', _MAKE.esc(cfg.buildtarget.name))\n_p(' DEFINES +=%s', make.list(cc.getdefines(cfg.defines)))\n_p(' INCLUDES +=%s', make.list(" + "cc.getincludedirs(cfg.includedirs)))\ncpp.pchconfig(cfg)\ncpp.flags(cfg, cc)\ncpp.linker(cfg, cc)\n_p(' OBJECTS := \\\\')\nfor _, file in ipairs(prj.files) do\nif path.iscppfile(file) then\nlocal excluded = false\nfor _, exclude in ipairs(cfg.excludes) do\nexcluded = (exclude == file)\nif (excluded) then break end\nend\nif excluded == false then\n_p('\\t$(OBJDIR)/%s.o \\\\'\n, _MAKE.esc(path.trimdots(path.removeext(file)))\n)\nend\nend\nend\n_p('')\n_p(' define PREBUILDCMDS')\nif #cfg.prebuildcommands > 0 then\n_p('\\t@echo Running pre-build commands')\n_p('\\t%s', table.implode(cfg.prebuildcommands, \"\", \"\", \"\\n\\t\"))\nend\n_p(' endef')\n_p(' define PRELINKCMDS')\nif #cfg.prelinkcommands > 0 then\n_p('\\t@echo Running pre-link commands')\n_p('\\t%s', table.implode(cfg.prelinkcommands, \"\", \"\", \"\\n\\t\"))\nend\n_p(' endef')\n_p(' define POSTBUILDCMDS')\nif #cfg.postbuildcommands > 0 then\n_p('\\t@echo Running post-build commands')\n_p('\\t%s', table.implode(cfg.postbuildcommands, \"\", \"\", " + "\"\\n\\t\"))\nend\n_p(' endef')\nmake.settings(cfg, cc)\n_p('endif')\n_p('')\nend\nfunction cpp.platformtools(cfg, cc)\nlocal platform = cc.platforms[cfg.platform]\nif platform.cc then\n_p(' CC = %s', platform.cc)\nend\nif platform.cxx then\n_p(' CXX = %s', platform.cxx)\nend\nif platform.ar then\n_p(' AR = %s', platform.ar)\nend\nend\nfunction cpp.flags(cfg, cc)\nif cfg.pchheader and not cfg.flags.NoPCH then\n_p(' FORCE_INCLUDE += -include $(OBJDIR)/$(notdir $(PCH))')\nend\nif #cfg.forcedincludes > 0 then\n_p(' FORCE_INCLUDE += -include %s'\n,premake.esc(table.concat(cfg.forcedincludes, \";\")))\nend\n_p(' ALL_CPPFLAGS += $(CPPFLAGS) %s $(DEFINES) $(INCLUDES)', table.concat(cc.getcppflags(cfg), \" \"))\n_p(' ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcflags(cfg), cfg.buildoptions, cfg.buildoptions_c)))\n_p(' ALL_CXXFLAGS += $(CXXFLAGS) $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcxxflags(cfg), cfg.buildoptions, cfg.b" + "uildoptions_cpp)))\n_p(' ALL_OBJCFLAGS += $(CXXFLAGS) $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcxxflags(cfg), cfg.buildoptions, cfg.buildoptions_objc)))\n_p(' ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)%s',\n make.list(table.join(cc.getdefines(cfg.resdefines),\n cc.getincludedirs(cfg.resincludedirs), cfg.resoptions)))\nend\nfunction cpp.linker(cfg, cc)\n_p(' ALL_LDFLAGS += $(LDFLAGS)%s', make.list(table.join(cc.getlibdirflags(cfg), cc.getldflags(cfg), cfg.linkoptions)))\n_p(' LDDEPS +=%s', make.list(_MAKE.esc(premake.getlinks(cfg, \"siblings\", \"fullpath\"))))\n_p(' LIBS += $(LDDEPS)%s', make.list(cc.getlinkflags(cfg)))\nif cfg.kind == \"StaticLib\" then\nif cfg.platform:startswith(\"Universal\") then\n_p(' LINKCMD = libtool -o $(TARGET)')\nelse\nif cc.llvm then\n_p(' LINKCMD = $(AR) rcs $(TARGET)')\nelse\n_p(' LINKCMD = $(AR) -rcs $(TARGET)')\nend\nend\nelse\nlocal tool = iif(cfg.language == \"C\", \"CC\", \"CXX" + "\")\n_p(' LINKCMD = $(%s) -o $(TARGET) $(OBJECTS) $(RESOURCES) $(ARCH) $(ALL_LDFLAGS) $(LIBS)', tool)\nend\nend\nfunction cpp.pchconfig(cfg)\nif not cfg.pchheader or cfg.flags.NoPCH then\nreturn\nend\nlocal pch = cfg.pchheader\nfor _, incdir in ipairs(cfg.includedirs) do\nlocal abspath = path.getabsolute(path.join(cfg.project.location, incdir))\nlocal testname = path.join(abspath, pch)\nif os.isfile(testname) then\npch = path.getrelative(cfg.location, testname)\nbreak\nend\nend\n_p(' PCH = %s', _MAKE.esc(pch))\n_p(' GCH = $(OBJDIR)/$(notdir $(PCH)).gch')\nend\nfunction cpp.pchrules(prj)\n_p('ifneq (,$(PCH))')\n_p('$(GCH): $(PCH)')\n_p('\\t@echo $(notdir $<)')\nlocal cmd = iif(prj.language == \"C\", \"$(CC) -x c-header $(ALL_CFLAGS)\", \"$(CXX) -x c++-header $(ALL_CXXFLAGS)\")\n_p('\\t$(SILENT) %s -MMD -MP $(DEFINES) $(INCLUDES) -o \"$@\" -MF \"$(@:%%.gch=%%.d)\" -c \"$<\"', cmd)\n_p('endif')\n_p('')\nend\nfunction cpp.fileRules(prj)\nfor _, file in ipairs(prj.files or {}) do\nif path.iscppf" + "ile(file) then\n_p('$(OBJDIR)/%s.o: %s'\n, _MAKE.esc(path.trimdots(path.removeext(file)))\n, _MAKE.esc(file)\n)\nif (path.isobjcfile(file) and prj.msgcompile_objc) then\n_p('\\t@echo ' .. prj.msgcompile_objc)\nelseif prj.msgcompile then\n_p('\\t@echo ' .. prj.msgcompile)\nelse\n_p('\\t@echo $(notdir $<)')\nend\nif (path.isobjcfile(file)) then\n_p('\\t$(SILENT) $(CXX) $(ALL_OBJCFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF $(@:%%.o=%%.d) -c \"$<\"')\nelse\ncpp.buildcommand(path.iscfile(file) and not prj.options.ForceCPP, \"o\")\nend\n_p('')\nelseif (path.getextension(file) == \".rc\") then\n_p('$(OBJDIR)/%s.res: %s', _MAKE.esc(path.getbasename(file)), _MAKE.esc(file))\nif prj.msgresource then\n_p('\\t@echo ' .. prj.msgresource)\nelse\n_p('\\t@echo $(notdir $<)')\nend\n_p('\\t$(SILENT) $(RESCOMP) $< -O coff -o \"$@\" $(ALL_RESFLAGS)')\n_p('')\nend\nend\nend\nfunction cpp.buildcommand(iscfile, objext)\nlocal flags = iif(iscfile, '$(CC) $(ALL_CFLAGS)', '$(CXX) $(ALL_CXXFLAGS)')\n_p('\\t$(SILENT) %s $(FORCE_INCLUDE) -o \"$" + "@\" -MF $(@:%%.%s=%%.d) -c \"$<\"', flags, objext)\nend\n", /* actions/make/make_csharp.lua */ "local function getresourcefilename(cfg, fname)\nif path.getextension(fname) == \".resx\" then\n local name = cfg.buildtarget.basename .. \".\"\n local dir = path.getdirectory(fname)\n if dir ~= \".\" then \nname = name .. path.translate(dir, \".\") .. \".\"\nend\nreturn \"$(OBJDIR)/\" .. _MAKE.esc(name .. path.getbasename(fname)) .. \".resources\"\nelse\nreturn fname\nend\nend\nfunction premake.make_csharp(prj)\nlocal csc = premake.dotnet\nlocal cfglibs = { }\nlocal cfgpairs = { }\nlocal anycfg\nfor cfg in premake.eachconfig(prj) do\nanycfg = cfg\ncfglibs[cfg] = premake.getlinks(cfg, \"siblings\", \"fullpath\")\ncfgpairs[cfg] = { }\nfor _, fname in ipairs(cfglibs[cfg]) do\nif path.getdirectory(fname) ~= cfg.buildtarget.directory then\ncfgpairs[cfg][\"$(TARGETDIR)/\" .. _MAKE.esc(path.getname(fname))] = _MAKE.esc(fname)\nend\nend\nend\nlocal sources = {}\nlocal embedded = { }\nlocal copypairs = { }\nfor fcfg in premake.project.eachfile(prj) do\nlocal action = csc.getbuildaction(fcfg)\nif action == \"Co" diff --git a/3rdparty/mongoose/docs/API.md b/3rdparty/mongoose/docs/API.md index 9549c4f9d63..faf93daaaae 100644 --- a/3rdparty/mongoose/docs/API.md +++ b/3rdparty/mongoose/docs/API.md @@ -183,11 +183,14 @@ points to a directory, Mongoose will show directory listing. If this function is used, no calls to `mg_send*` or `mg_printf*` functions must be made, and event handler must return `MG_MORE`. - int mg_websocket_write(struct mg_connection* conn, int opcode, - const char *data, size_t data_len); + size_t mg_websocket_write(struct mg_connection* conn, int opcode, + const char *data, size_t data_len); + size_t mg_websocket_printf(struct mg_connection* conn, int opcode, + const char *fmt, ...); -Similar to `mg_write()`, but wraps the data into a websocket frame with a -given websocket `opcode`. + +Similar to `mg_write()` and `mg_printf()`, but wraps the data into a +websocket frame with a given websocket `opcode`. const char *mg_get_header(const struct mg_connection *, const char *name); diff --git a/3rdparty/mongoose/examples/multi_threaded_server/Makefile b/3rdparty/mongoose/examples/multi_threaded_server/Makefile index b85aa97a8af..2b08db12c77 100644 --- a/3rdparty/mongoose/examples/multi_threaded_server/Makefile +++ b/3rdparty/mongoose/examples/multi_threaded_server/Makefile @@ -2,7 +2,7 @@ # All rights reserved PROG = multi_threaded_server -CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) +CFLAGS = -W -Wall -I../.. -pthread -g -O0 -DMONGOOSE_ENABLE_THREADS $(CFLAGS_EXTRA) SOURCES = $(PROG).c ../../mongoose.c $(PROG): $(SOURCES) diff --git a/3rdparty/mongoose/examples/proxy_server/Makefile b/3rdparty/mongoose/examples/proxy_server/Makefile index 214b8b6864e..7a5d2f7d41a 100644 --- a/3rdparty/mongoose/examples/proxy_server/Makefile +++ b/3rdparty/mongoose/examples/proxy_server/Makefile @@ -3,7 +3,7 @@ PROG = proxy_server FLAGS = -I../.. -DNS_ENABLE_SSL -CFLAGS = -W -Wall -g -O0 -pthread -lssl $(FLAGS) $(CFLAGS_EXTRA) +CFLAGS = -W -Wall -g -O0 -pthread -lssl -DMONGOOSE_ENABLE_THREADS $(FLAGS) $(CFLAGS_EXTRA) SOURCES = $(PROG).c ../../mongoose.c unix: $(SOURCES) diff --git a/3rdparty/mongoose/examples/web_server/Makefile b/3rdparty/mongoose/examples/web_server/Makefile index 888f2148d81..ee9bc1b0476 100644 --- a/3rdparty/mongoose/examples/web_server/Makefile +++ b/3rdparty/mongoose/examples/web_server/Makefile @@ -2,11 +2,27 @@ # All rights reserved PROG = web_server -CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) +CFLAGS = -W -Wall -I../.. -pthread -g -O0 -DMONGOOSE_ENABLE_THREADS $(CFLAGS_EXTRA) SOURCES = $(PROG).c ../../mongoose.c +OPENSSL_FLAGS = -DNS_ENABLE_SSL -lssl + +# PolarSSL paths and flags +POLARSSL_PATH = /usr/local +POLARSSLCOMPAT_PATH = ./../../../polar +SOURCES_POLAR = $(SOURCES) $(POLARSSLCOMPAT_PATH)/polarssl_compat.c +INCDIR_POLAR = -I$(POLARSSLCOMPAT_PATH) -I$(POLARSSL_PATH)/include +LDFLAGS_POLAR = -L$(POLARSSL_PATH)/lib -lmbedtls +CFLAGS_POLAR = $(CFLAGS) $(INCDIR_POLAR) -DNS_ENABLE_SSL +# $(PROG): $(SOURCES) $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) +openssl: + $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) $(OPENSSL_FLAGS) + +polarssl: + $(CC) -o $(PROG) $(SOURCES_POLAR) $(LDFLAGS_POLAR) $(CFLAGS_POLAR) + clean: rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib diff --git a/3rdparty/mongoose/examples/web_server/certs/cert.pem b/3rdparty/mongoose/examples/web_server/certs/cert.pem new file mode 100644 index 00000000000..f191e3c10a6 --- /dev/null +++ b/3rdparty/mongoose/examples/web_server/certs/cert.pem @@ -0,0 +1,46 @@ +-----BEGIN CERTIFICATE----- +MIIC+zCCAeOgAwIBAgIJAPhB8jbL+G82MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV +BAMMCTEyNy4wLjAuMTAeFw0xNTAzMDYxMjQzMzNaFw0yNTAzMDMxMjQzMzNaMBQx +EjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALi3b3daMgzUEROKob1Caf68i+//cTRkPdBJv2cOBak21CdQzY0Nvx73GLzf +5TKB347BCHNbYRKGJXDbYdmFp20/WeBHkY7RS3Ad2Q5lzyx66u9PxNx7hJIiqBgF +58VU+E3o/I+o8QNIoOT+wtCiq3Nwkp+zGBJmS32rzMEV9bcKxSzMrkfRhF+XAREd +DwM9vfPg6WRb/b+vv06uvVwcw390RprLautGfBdaRddVYkIAKJGRRTqZAvTRFW1J +FcIVOxlN+iA7qP7xjr3tUP78qMmlu0MXsHrUR2cgfveZK2sdUW5G804yHsU5sC8l +FbtLKMEOyLsk2bEIScOXgum7g2sCAwEAAaNQME4wHQYDVR0OBBYEFHtLzUqAsXkH +Il8S5sMhJuVhRJLdMB8GA1UdIwQYMBaAFHtLzUqAsXkHIl8S5sMhJuVhRJLdMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEzHc0AOr+qs0OFvWMfcSMi7 +O/aYlLS6f7Sos+lli69+61EcmCTJVarVeAVUsAoqmzBKDbeOpAK1hGX6/GGcXjR2 +BmuU0hUKyX9l1lwdMKU45BayH/riElwnvAyj2GxKoPpdIjlHns4SAITOCUx9NfpM +agd7kjolton0ZQ5DI/2a43PkqHv1lY4Dp60wJlxit9U68bsGOycCJ/BsAyrPROb2 +D1MkpMBIdfHc8uxRywM3/l9buFX8yrrMUGOYKgfjDwdzbj0iwIixoGpHL7IfeBtu +dvGO/g2rEhbtAP+xIgOR3GvzqjZh30er3no7zjDMn65tTME18Aq3tBQY7vPDKms= +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4t293WjIM1BET +iqG9Qmn+vIvv/3E0ZD3QSb9nDgWpNtQnUM2NDb8e9xi83+Uygd+OwQhzW2EShiVw +22HZhadtP1ngR5GO0UtwHdkOZc8seurvT8Tce4SSIqgYBefFVPhN6PyPqPEDSKDk +/sLQoqtzcJKfsxgSZkt9q8zBFfW3CsUszK5H0YRflwERHQ8DPb3z4OlkW/2/r79O +rr1cHMN/dEaay2rrRnwXWkXXVWJCACiRkUU6mQL00RVtSRXCFTsZTfogO6j+8Y69 +7VD+/KjJpbtDF7B61EdnIH73mStrHVFuRvNOMh7FObAvJRW7SyjBDsi7JNmxCEnD +l4Lpu4NrAgMBAAECggEAaFuqbAHXOQwuwZ2XFzgIblTTsrncmT7w9VZU/sIbTKif +X771AnX7vmDX5w2PjeN2DE7emV3NEAwd5w7qz1wFZWFfQ6jrgYaZWjRixxGZ5IVl +aeLlU7OtCGrwEPJ1KTWCO3IgDoHh+Hr1+6o7Imhk+QlmrTcfqHWGvO9s9MGVWt2S +RLAnSTFiOe5brdJnmlqq1sKZmnLmpydBaPUOYpZGAgRasrjdMZB+lZOazd1x23/5 +GAcm0rDREMnO9b2Jt+TNEZHT6d5KpVoExztZEZj8QCLXoic/SpFIqHGtpNlQXa+d +BVqgQbIYjO8ldldxZ8YIyJDVF+9e/uBBwu6jBIIsEQKBgQDspEHCyyuh4LG+7BbZ +eXlsfCxPTM6K9w31ZwHAwRtAuGqrOrE+pFJG9CEsFZbAI1aOGmZZdjexuSMcOlXl +TIVJTQHoFtoGEsanYEXO4O1t02Ab/DCYSpXusXUraRBRPpsTC77Sh5mxLUNd23d9 +NhnDBuwChAmC+IYexjkXeqPYFwKBgQDH08PEd+2PVo4MD8UVKUlEcgoyCr6ESiyp +HfYyhhfd5x3DbZLoKCkunDfBs/hakQk8DA2nn4tl4ZjfmzXmX0EBx+E5YTdYshW7 +ZcjN5x64B5PEOAR/NZA6agNlp3XGXXXgX+gnN6pgE49eVU22nZ4G+QBKD6NcCviB +LBPUxMbvzQKBgHgZYRqonGtaqzsXfP1AjmSFnMNeWtDiU95BOf2Gw/sT3WcrsXr2 +UJ+cFR3XkxvOk4YpVdp/igKT0ILqBGAMdvTdtWMB/gLpEpMt5B/7veRoS7XIRy1z +ZSawP6QZfWOOX4vKAT29/j2SmEcRNFKC245EfBFGy8EBuqfxuFX3MyJfAoGBAJ0y +tjsErVmpma1baosvI3g4zlR3p1CimWehLmCopHXorr1iocMIdP0535L+ZU258y3N +vaA0HpFTW9PsYgaMwLMJ7uAY3lVkIzx84e849i2HqHMgLkl0dbW+WFXL2xblxylv +yU2wuNNED/EB4lTawcpycAvTKYvrBXt4lVE4S9exAoGAGl6vZV3zyw4jpIw4uDfk +LTPYUrghFDDGKExyeOnC/W9pqR2veqzfBz02C3jqwhewoqgAcnNc2sg0rJmM+6Oz +Z2mmGZTHO9xR++7+W7e8AkQBbS6TB8a+7yNcM4USLP+b9sX5N+8gFhFs9tG7j/no +G44qLsJ/yve7/QsOA37uEMs= +-----END PRIVATE KEY----- diff --git a/3rdparty/mongoose/examples/web_server/web_server.c b/3rdparty/mongoose/examples/web_server/web_server.c index 8f8567af774..07fda84ed17 100644 --- a/3rdparty/mongoose/examples/web_server/web_server.c +++ b/3rdparty/mongoose/examples/web_server/web_server.c @@ -71,7 +71,12 @@ static char server_name[50]; // Set by init_server_name() static char s_config_file[PATH_MAX]; // Set by process_command_line_arguments static struct mg_server *server; // Set by start_mongoose() static const char *s_default_document_root = "."; +#ifndef NS_ENABLE_SSL static const char *s_default_listening_port = "8080"; +#else +static const char *s_default_listening_port = "ssl://8443:certs/cert.pem"; +#endif + static char **s_argv = { NULL }; static void set_options(char *argv[]); diff --git a/3rdparty/mongoose/examples/websocket_ssl_proxy/Makefile b/3rdparty/mongoose/examples/websocket_ssl_proxy/Makefile index bd3f6be1297..968f1aca066 100644 --- a/3rdparty/mongoose/examples/websocket_ssl_proxy/Makefile +++ b/3rdparty/mongoose/examples/websocket_ssl_proxy/Makefile @@ -2,14 +2,26 @@ # All rights reserved PROG = ws_ssl -CFLAGS = -W -Wall -I../.. -I. -pthread -g -O0 $(CFLAGS_EXTRA) +CFLAGS = -W -Wall -I../.. -I. -pthread -g -O0 -DMONGOOSE_ENABLE_THREADS -DNS_ENABLE_SSL -DSSL_WRAPPER_USE_AS_LIBRARY $(CFLAGS_EXTRA) +LDFLAGS = -lssl SOURCES = ws_ssl.c ../../mongoose.c ssl_wrapper.c +# PolarSSL paths and flags +POLARSSL_PATH = /usr/local +POLARSSLCOMPAT_PATH = ./../../../polar +SOURCES_POLAR = $(SOURCES) $(POLARSSLCOMPAT_PATH)/polarssl_compat.c +INCDIR_POLAR = -I$(POLARSSLCOMPAT_PATH) -I$(POLARSSL_PATH)/include +LDFLAGS_POLAR = -L$(POLARSSL_PATH)/lib -lmbedtls +CFLAGS_POLAR = $(CFLAGS) $(INCDIR_POLAR) +# + all: $(PROG) $(PROG): $(SOURCES) - $(CC) -o $(PROG) $(SOURCES) \ - -DNS_ENABLE_SSL -DSSL_WRAPPER_USE_AS_LIBRARY -lssl $(CFLAGS) + $(CC) -o $(PROG) $(SOURCES) $(LDFLAGS) $(CFLAGS) + +polarssl: $(SOURCES_POLAR) + $(CC) -o $(PROG) $(SOURCES_POLAR) $(LDFLAGS_POLAR) $(CFLAGS_POLAR) clean: rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib diff --git a/3rdparty/mongoose/mongoose.c b/3rdparty/mongoose/mongoose.c index f437fe9141d..f7f47cadadd 100644 --- a/3rdparty/mongoose/mongoose.c +++ b/3rdparty/mongoose/mongoose.c @@ -1486,7 +1486,7 @@ enum endpoint_type { }; #define MG_HEADERS_SENT NSF_USER_1 -#define MG_LONG_RUNNING NSF_USER_2 +#define MG_USING_CHUNKED_API NSF_USER_2 #define MG_CGI_CONN NSF_USER_3 #define MG_PROXY_CONN NSF_USER_4 #define MG_PROXY_DONT_PARSE NSF_USER_5 @@ -2716,18 +2716,22 @@ size_t mg_write(struct mg_connection *c, const void *buf, int len) { } void mg_send_status(struct mg_connection *c, int status) { + struct connection *conn = MG_CONN_2_CONN(c); if (c->status_code == 0) { c->status_code = status; mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status)); } + conn->ns_conn->flags |= MG_USING_CHUNKED_API; } void mg_send_header(struct mg_connection *c, const char *name, const char *v) { + struct connection *conn = MG_CONN_2_CONN(c); if (c->status_code == 0) { c->status_code = 200; mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200)); } mg_printf(c, "%s: %s\r\n", name, v); + conn->ns_conn->flags |= MG_USING_CHUNKED_API; } static void terminate_headers(struct mg_connection *c) { @@ -3109,7 +3113,8 @@ static int call_request_handler(struct connection *conn) { int result; conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf; if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) { - if (conn->ns_conn->flags & MG_HEADERS_SENT) { + if (conn->ns_conn->flags & MG_USING_CHUNKED_API) { + terminate_headers(&conn->mg_conn); write_terminating_chunk(conn); } close_local_endpoint(conn); @@ -4875,7 +4880,7 @@ static void close_local_endpoint(struct connection *conn) { conn->cl = conn->num_bytes_recv = conn->request_len = 0; conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA | NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY | - MG_HEADERS_SENT | MG_LONG_RUNNING); + MG_HEADERS_SENT | MG_USING_CHUNKED_API); // Do not memset() the whole structure, as some of the fields // (IP addresses & ports, server_param) must survive. Nullify the rest. -- cgit v1.2.3