diff --git a/README.md b/README.md index ce47a092f..101cafc35 100644 --- a/README.md +++ b/README.md @@ -162,13 +162,16 @@ Embeds [stb_textedit.h, stb_truetype.h, stb_rectpack.h](https://github.com/nothi Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. And everybody posting feedback, questions and patches on the GitHub. -ImGui development is financially supported on [**Patreon**](http://www.patreon.com/imgui). +Ongoing ImGui development is financially supported on [**Patreon**](http://www.patreon.com/imgui). -Special supporters: -- Jetha Chan, Wild Sheep Studio, Pastagames, Mārtiņš Možeiko, Daniel Collin, Stefano Cristiano. +Double-chocolate sponsors: +- Media Molecule, Mobigame -And: -- Michel Courtine, César Leblic, Dale Kim, Alex Evans, Rui Figueira, Paul Patrashcu, Jerome Lanquetot, Ctrl Alt Ninja, Paul Fleming, Neil Henning, Stephan Dilly, Neil Blakey-Milner, Aleksei, NeiloGD, Justin Paver, FiniteSol, Vincent Pancaldi, James Billot, Robin Hübner, furrtek, Eric, Simon Barratt, Game Atelier, Julian Bosch, Simon Lundmark, Vincent Hamm. +Salty caramel supporters: +- Jetha Chan, Wild Sheep Studio, Pastagames, Mārtiņš Možeiko, Daniel Collin, Stefano Cristiano, Chris Genova, ikrima + +Caramel supporters: +- Michel Courtine, César Leblic, Dale Kim, Alex Evans, Rui Figueira, Paul Patrashcu, Jerome Lanquetot, Ctrl Alt Ninja, Paul Fleming, Neil Henning, Stephan Dilly, Neil Blakey-Milner, Aleksei, NeiloGD, Justin Paver, FiniteSol, Vincent Pancaldi, James Billot, Robin Hübner, furrtek, Eric, Simon Barratt, Game Atelier, Julian Bosch, Simon Lundmark, Vincent Hamm, Farhan Wali, Jeff Roberts, Matt Reyer, Colin Riley. And other supporters; thanks! diff --git a/examples/README.txt b/examples/README.txt index b80866c11..f8e0c55ed 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -1,5 +1,10 @@ Those are standalone ready-to-build applications to demonstrate ImGui. -Binaries of some of those demos are available at http://www.miracleworld.net/imgui/binaries +Binaries of some of those demos: http://www.miracleworld.net/imgui/binaries + +Third party languages and frameworks bindings: https://github.com/ocornut/imgui/wiki/Links +(languages: C, .net, rust, D, Python, Lua..) +(frameworks: DX12, Vulkan, Cinder, OpenGLES, openFrameworks, Cocos2d-x, SFML, Flexium, NanoRT, Irrlicht..) +(extras: RemoteImGui, ImWindow, imgui_wm..) TL;DR; - Newcomers, read 'PROGRAMMER GUIDE' in imgui.cpp for notes on how to setup ImGui in your codebase. diff --git a/examples/allegro5_example/imgui_impl_a5.cpp b/examples/allegro5_example/imgui_impl_a5.cpp index 9af4ee0ee..c7ba11062 100644 --- a/examples/allegro5_example/imgui_impl_a5.cpp +++ b/examples/allegro5_example/imgui_impl_a5.cpp @@ -249,6 +249,7 @@ void ImGui_ImplA5_NewFrame() io.KeyCtrl = al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL); io.KeyShift = al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT); io.KeyAlt = al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR); + io.KeySuper = al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN); ALLEGRO_MOUSE_STATE mouse; if (keys.display == g_Display) diff --git a/examples/directx10_example/imgui_impl_dx10.cpp b/examples/directx10_example/imgui_impl_dx10.cpp index 73bd3abff..21f86f5d8 100644 --- a/examples/directx10_example/imgui_impl_dx10.cpp +++ b/examples/directx10_example/imgui_impl_dx10.cpp @@ -496,6 +496,7 @@ void ImGui_ImplDX10_NewFrame() io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; + io.KeySuper = false; // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events // io.MousePos : filled by WM_MOUSEMOVE events // io.MouseDown : filled by WM_*BUTTON* events diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/directx11_example/imgui_impl_dx11.cpp index 8dd528d4e..8451fb8eb 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/directx11_example/imgui_impl_dx11.cpp @@ -494,6 +494,7 @@ void ImGui_ImplDX11_NewFrame() io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; + io.KeySuper = false; // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events // io.MousePos : filled by WM_MOUSEMOVE events // io.MouseDown : filled by WM_*BUTTON* events diff --git a/examples/directx9_example/imgui_impl_dx9.cpp b/examples/directx9_example/imgui_impl_dx9.cpp index 21e8b5e36..17f4f078c 100644 --- a/examples/directx9_example/imgui_impl_dx9.cpp +++ b/examples/directx9_example/imgui_impl_dx9.cpp @@ -309,6 +309,7 @@ void ImGui_ImplDX9_NewFrame() io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; + io.KeySuper = false; // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events // io.MousePos : filled by WM_MOUSEMOVE events // io.MouseDown : filled by WM_*BUTTON* events diff --git a/examples/ios_example/imguiex/imgui_impl_ios.mm b/examples/ios_example/imguiex/imgui_impl_ios.mm index ab3fcd871..b286c4d4a 100644 --- a/examples/ios_example/imguiex/imgui_impl_ios.mm +++ b/examples/ios_example/imguiex/imgui_impl_ios.mm @@ -263,10 +263,10 @@ void ImGui_KeyboardCallback(uSynergyCookie cookie, uint16_t key, // printf("Synergy: keyboard callback: 0x%02X (%s)", scanCode, down?"true":"false"); ImGuiIO& io = ImGui::GetIO(); io.KeysDown[key] = down; - io.KeyShift = modifiers & USYNERGY_MODIFIER_SHIFT; - io.KeyCtrl = modifiers & USYNERGY_MODIFIER_CTRL; - io.KeyAlt = modifiers & USYNERGY_MODIFIER_ALT; - + io.KeyShift = (modifiers & USYNERGY_MODIFIER_SHIFT); + io.KeyCtrl = (modifiers & USYNERGY_MODIFIER_CTRL); + io.KeyAlt = (modifiers & USYNERGY_MODIFIER_ALT); + io.KeySuper = (modifiers & USYNERGY_MODIFIER_WIN); // Add this as keyboard input if ((down) && (key) && (scanCode<256) && !(modifiers & USYNERGY_MODIFIER_CTRL)) diff --git a/examples/marmalade_example/imgui_impl_marmalade.cpp b/examples/marmalade_example/imgui_impl_marmalade.cpp index f7cff9b17..12d5dfaae 100644 --- a/examples/marmalade_example/imgui_impl_marmalade.cpp +++ b/examples/marmalade_example/imgui_impl_marmalade.cpp @@ -153,6 +153,7 @@ int32 ImGui_Marmalade_KeyCallback(void* SystemData, void* userData) io.KeyCtrl = s3eKeyboardGetState(s3eKeyLeftControl) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightControl) == S3E_KEY_STATE_DOWN; io.KeyShift = s3eKeyboardGetState(s3eKeyLeftShift) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightShift) == S3E_KEY_STATE_DOWN; io.KeyAlt = s3eKeyboardGetState(s3eKeyLeftAlt) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightAlt) == S3E_KEY_STATE_DOWN; + io.KeySuper = s3eKeyboardGetState(s3eKeyLeftWindows) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightWindows) == S3E_KEY_STATE_DOWN; return 0; } diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index 7cad27b09..b1d2a40b4 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -157,6 +157,7 @@ void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow*, int key, int, int action, int mo io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; + io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; } void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow*, unsigned int c) diff --git a/examples/opengl_example/imgui_impl_glfw.cpp b/examples/opengl_example/imgui_impl_glfw.cpp index ae6f31157..fd9ce7206 100644 --- a/examples/opengl_example/imgui_impl_glfw.cpp +++ b/examples/opengl_example/imgui_impl_glfw.cpp @@ -139,6 +139,7 @@ void ImGui_ImplGlFw_KeyCallback(GLFWwindow*, int key, int, int action, int mods) io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; + io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; } void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 7afbf3435..5406f424b 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -134,36 +134,37 @@ bool ImGui_ImplSdlGL3_ProcessEvent(SDL_Event* event) switch (event->type) { case SDL_MOUSEWHEEL: - { - if (event->wheel.y > 0) - g_MouseWheel = 1; - if (event->wheel.y < 0) - g_MouseWheel = -1; - return true; - } + { + if (event->wheel.y > 0) + g_MouseWheel = 1; + if (event->wheel.y < 0) + g_MouseWheel = -1; + return true; + } case SDL_MOUSEBUTTONDOWN: - { - if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; - if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; - if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; - return true; - } + { + if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; + if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; + if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; + return true; + } case SDL_TEXTINPUT: - { - ImGuiIO& io = ImGui::GetIO(); - io.AddInputCharactersUTF8(event->text.text); - return true; - } + { + ImGuiIO& io = ImGui::GetIO(); + io.AddInputCharactersUTF8(event->text.text); + return true; + } case SDL_KEYDOWN: case SDL_KEYUP: - { - int key = event->key.keysym.sym & ~SDLK_SCANCODE_MASK; - io.KeysDown[key] = (event->type == SDL_KEYDOWN); - io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); - io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); - io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); - return true; - } + { + int key = event->key.keysym.sym & ~SDLK_SCANCODE_MASK; + io.KeysDown[key] = (event->type == SDL_KEYDOWN); + io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); + io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); + io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); + io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); + return true; + } } return false; } diff --git a/examples/sdl_opengl_example/imgui_impl_sdl.cpp b/examples/sdl_opengl_example/imgui_impl_sdl.cpp index ba1746afd..0d885b07f 100644 --- a/examples/sdl_opengl_example/imgui_impl_sdl.cpp +++ b/examples/sdl_opengl_example/imgui_impl_sdl.cpp @@ -143,6 +143,7 @@ bool ImGui_ImplSdl_ProcessEvent(SDL_Event* event) io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); + io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); return true; } } diff --git a/imgui.cpp b/imgui.cpp index 8ff7c63ff..8f2b87ffe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -441,6 +441,7 @@ !- window: begin with *p_opened == false should return false. - window: get size/pos helpers given names (see discussion in #249) - window: a collapsed window can be stuck behind the main menu bar? + - window: when window is small, prioritize resize button over close button. - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later. - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. @@ -450,15 +451,19 @@ - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. - widgets: clean up widgets internal toward exposing everything. - widgets: add disabled and read-only modes (#211) - - main: considering adding EndFrame()/Init(). some constructs are awkward in the implementation because of the lack of them. - - main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows). + - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them. +!- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows). - 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? - - input text: add ImGuiInputTextFlags_EnterToApply? (off #218) + - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now. - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541) + - input text: flag to disable live update of the user buffer (also applies to float/int text input) + - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text? + - input text: add ImGuiInputTextFlags_EnterToApply? (off #218) - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc). - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200) - input text multi-line: line numbers? status bar? (follow up on #200) + - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position. - input number: optional range min/max for Input*() functions - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) - input number: use mouse wheel to step up/down @@ -497,7 +502,7 @@ - statusbar: add a per-window status bar helper similar to what menubar does. - tabs (#261, #351) - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) - - color: the color helpers/typing is a mess and needs sorting out. +!- color: the color helpers/typing is a mess and needs sorting out. - color: add a better color picker (#346) - node/graph editor (#306) - pie menus patterns (#434) @@ -512,14 +517,11 @@ - slider & drag: int data passing through a float - drag float: up/down axis - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits) - - 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? - tree node / optimization: avoid formatting when clipped. - tree node: clarify spacing, perhaps provide API to query exact spacing. provide API to draw the primitive. same with Bullet(). - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling. - tree node: add treenode/treepush int variants? because (void*) cast from int warns on some platforms/settings + - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits? - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer) - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (git issue #249) - settings: write more decent code to allow saving/loading new fields @@ -529,6 +531,7 @@ - style: color-box not always square? - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc. - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation). + - style/opt: PopStyleVar could be optimized by having GetStyleVar returns the type, using a table mapping stylevar enum to data type. - style: global scale setting. - text: simple markup language for color change? - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. @@ -552,7 +555,8 @@ - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438) - style editor: color child window height expressed in multiple of line height. - remote: make a system like RemoteImGui first-class citizen/project (#75) - - drawlist: user probably can't call Clear() because we expect a texture to be pushed in the stack. +!- demo: custom render demo pushes a clipping rectangle past parent window bounds. expose ImGui::PushClipRect() from imgui_internal.h? + - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. - examples: directx9/directx11: save/restore device state more thoroughly. - optimization: use another hash function than crc32, e.g. FNV1a - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)? @@ -789,6 +793,14 @@ ImGuiIO::ImGuiIO() GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; + + // Set OS X style defaults based on __APPLE__ compile time flag +#ifdef __APPLE__ + WordMovementUsesAltKey = true; // OS X style: Text editing cursor movement using Alt instead of Ctrl + ShortcutsUseSuperKey = true; // OS X style: Shortcuts using Cmd/Super instead of Ctrl + DoubleClickSelectsWord = true; // OS X style: Double click selects by word instead of selecting whole text + MultiSelectUsesSuperKey = true; // OS X style: Multi-selection in lists uses Cmd/Super instead of Ctrl +#endif } // Pass in translated ASCII characters for text input. @@ -7090,8 +7102,18 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } -static bool is_separator(unsigned int c) { return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -#define STB_TEXTEDIT_IS_SPACE(CH) ( ImCharIsSpace((unsigned int)CH) || is_separator((unsigned int)CH) ) +static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } +static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; } +static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +#ifdef __APPLE__ // FIXME: Move setting to IO structure +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#else +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#endif +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL + static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) { ImWchar* dst = obj->Text.Data + pos; @@ -7321,6 +7343,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool is_ctrl_down = io.KeyCtrl; const bool is_shift_down = io.KeyShift; const bool is_alt_down = io.KeyAlt; + const bool is_super_down = io.KeySuper; const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 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; @@ -7408,11 +7431,17 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const float mouse_x = (g.IO.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; const float mouse_y = (is_multiline ? (g.IO.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); - if (select_all || (hovered && io.MouseDoubleClicked[0])) + if (select_all || (hovered && !io.DoubleClickSelectsWord && io.MouseDoubleClicked[0])) { edit_state.SelectAll(); edit_state.SelectedAllMouseLock = true; } + else if (hovered && io.DoubleClickSelectsWord && io.MouseDoubleClicked[0]) + { + // Select a word only, OS X style (by simulating keystrokes) + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + } else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) { stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); @@ -7450,9 +7479,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Handle various key-presses bool cancel_edit = false; const int k_mask = (is_shift_down ? STB_TEXTEDIT_K_SHIFT : 0); - const bool is_ctrl_only = is_ctrl_down && !is_alt_down && !is_shift_down; - if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_WORDLEFT | k_mask : STB_TEXTEDIT_K_LEFT | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_WORDRIGHT | k_mask : STB_TEXTEDIT_K_RIGHT | k_mask); } + const bool is_shortcutkey_only = (io.ShortcutsUseSuperKey ? (is_super_down && !is_alt_down && !is_shift_down && !is_ctrl_down) : (is_ctrl_down && !is_alt_down && !is_shift_down && !is_super_down)); + const bool is_wordmove_key_down = (io.WordMovementUsesAltKey ? io.KeyAlt : io.KeyCtrl); + + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed(is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT | k_mask : STB_TEXTEDIT_K_LEFT | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed(is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT | k_mask : STB_TEXTEDIT_K_RIGHT | k_mask); } else if (is_multiline && IsKeyPressedMap(ImGuiKey_UpArrow)) { if (is_ctrl_down) SetWindowScrollY(draw_window, draw_window->Scroll.y - g.FontSize); else edit_state.OnKeyPressed(STB_TEXTEDIT_K_UP | k_mask); } else if (is_multiline && IsKeyPressedMap(ImGuiKey_DownArrow)) { if (is_ctrl_down) SetWindowScrollY(draw_window, draw_window->Scroll.y + g.FontSize); else edit_state.OnKeyPressed(STB_TEXTEDIT_K_DOWN| k_mask); } else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(is_ctrl_down ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } @@ -7480,11 +7511,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (InputTextFilterCharacter(&c, flags, callback, user_data)) edit_state.OnKeyPressed((int)c); } - else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } - else if (is_ctrl_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection())) + else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } + else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } + else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } + else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } + else if (is_shortcutkey_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection())) { // Cut, Copy const bool cut = IsKeyPressedMap(ImGuiKey_X); @@ -7506,7 +7537,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 stb_textedit_cut(&edit_state, &edit_state.StbState); } } - else if (is_ctrl_only && IsKeyPressedMap(ImGuiKey_V) && is_editable) + else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_V) && is_editable) { // Paste if (g.IO.GetClipboardTextFn) diff --git a/imgui.h b/imgui.h index f5b3feb2d..c7eaa1b6f 100644 --- a/imgui.h +++ b/imgui.h @@ -105,7 +105,6 @@ namespace ImGui IMGUI_API ImGuiStyle& GetStyle(); IMGUI_API ImDrawData* GetDrawData(); // same value as passed to your io.RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame() IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until NewFrame()/Render(). - IMGUI_API void EndFrame(); // ends the ImGui frame. automatically called by Render()! IMGUI_API void Render(); // ends the ImGui frame, finalize rendering data, then call your io.RenderDrawListsFn() function if set. IMGUI_API void Shutdown(); IMGUI_API void ShowUserGuide(); // help block @@ -114,11 +113,11 @@ namespace ImGui IMGUI_API void ShowMetricsWindow(bool* opened = NULL); // metrics window for debugging ImGui // Window - IMGUI_API bool Begin(const char* name, bool* p_opened = NULL, ImGuiWindowFlags flags = 0); // see .cpp for details. return false when window is collapsed, so you can early out in your code. 'bool* p_opened' creates a widget on the upper-right to close the window (which sets your bool to false). - IMGUI_API bool Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags = 0); // this is the older/longer API. the extra parameters aren't very relevant. call SetNextWindowSize() instead if you want to set a window size. For regular windows, 'size_on_first_use' only applies to the first time EVER the window is created and probably not what you want! maybe obsolete this API eventually. - IMGUI_API void End(); - IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). - IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // " + IMGUI_API bool Begin(const char* name, bool* p_opened = NULL, ImGuiWindowFlags flags = 0); // push window to the stack and start appending to it. see .cpp for details. return false when window is collapsed, so you can early out in your code. 'bool* p_opened' creates a widget on the upper-right to close the window (which sets your bool to false). + IMGUI_API bool Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags = 0); // ". this is the older/longer API. the extra parameters aren't very relevant. call SetNextWindowSize() instead if you want to set a window size. For regular windows, 'size_on_first_use' only applies to the first time EVER the window is created and probably not what you want! might obsolete this API eventually. + IMGUI_API void End(); // finish appending to current window, pop it off the window stack. + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). + 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 ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() @@ -713,6 +712,12 @@ struct ImGuiIO ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize + // Advanced/subtle behaviors + bool WordMovementUsesAltKey; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl + bool ShortcutsUseSuperKey; // = defined(__APPLE__) // OS X style: Shortcuts using Cmd/Super instead of Ctrl + bool DoubleClickSelectsWord; // = defined(__APPLE__) // OS X style: Double click selects by word instead of selecting whole text + bool MultiSelectUsesSuperKey; // = defined(__APPLE__) // OS X style: Multi-selection in lists uses Cmd/Super instead of Ctrl [unused yet] + //------------------------------------------------------------------ // User Functions //------------------------------------------------------------------ @@ -748,6 +753,7 @@ struct ImGuiIO bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift bool KeyAlt; // Keyboard modifier pressed: Alt + bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows 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. @@ -985,7 +991,8 @@ struct ImGuiTextEditCallbackData bool HasSelection() const { return SelectionStart != SelectionEnd; } }; -// ImColor() is just a helper that implicity converts to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) +// ImColor() helper to implicity converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) +// Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class. // None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. struct ImColor { diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7a0833a7c..132e8e7f8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1500,7 +1500,7 @@ void ImGui::ShowTestWindow(bool* p_opened) ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } - ImGui::Text("KeyMods: %s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : ""); + ImGui::Text("KeyMods: %s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("WantCaptureMouse: %s", io.WantCaptureMouse ? "true" : "false"); ImGui::Text("WantCaptureKeyboard: %s", io.WantCaptureKeyboard ? "true" : "false"); diff --git a/imgui_internal.h b/imgui_internal.h index e8177f432..384a8467d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -678,6 +678,8 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); + IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! + IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); IMGUI_API void SetHoveredID(ImGuiID id); IMGUI_API void KeepAliveID(ImGuiID id); diff --git a/stb_textedit.h b/stb_textedit.h index 1d42862df..3c3003252 100644 --- a/stb_textedit.h +++ b/stb_textedit.h @@ -1,8 +1,8 @@ -// [ImGui] this is a slightly modified version of stb_truetype.h 1.4 -// [ImGui] we made a fix for using the END key on multi-line text edit, see https://github.com/ocornut/imgui/issues/275 -// [ImGui] we made a fix for using keyboard while using mouse, see https://github.com/nothings/stb/pull/209 +// [ImGui] this is a slightly modified version of stb_truetype.h 1.8 +// [ImGui] - fixed some minor warnings +// [ImGui] - added STB_TEXTEDIT_MOVEWORDLEFT/STB_TEXTEDIT_MOVEWORDRIGHT custom handler (#473) -// stb_textedit.h - v1.4 - public domain - Sean Barrett +// stb_textedit.h - v1.8 - public domain - Sean Barrett // Development of this library was sponsored by RAD Game Tools // // This C header file implements the guts of a multi-line text-editing @@ -21,19 +21,24 @@ // // LICENSE // -// This software has been placed in the public domain by its author. -// Where that dedication is not recognized, you are granted a perpetual, -// irrevocable license to copy and modify this file as you see fit. +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. // // // DEPENDENCIES // -// Uses the C runtime function 'memmove'. Uses no other functions. -// Performs no runtime allocations. +// Uses the C runtime function 'memmove', which you can override +// by defining STB_TEXTEDIT_memmove before the implementation. +// Uses no other functions. Performs no runtime allocations. // // // VERSION HISTORY // +// 1.8 (2016-04-02) better keyboard handling when mouse button is down +// 1.7 (2015-09-13) change y range handling in case baseline is non-0 +// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove +// 1.5 (2014-09-10) add support for secondary keys for OS X // 1.4 (2014-08-17) fix signed/unsigned warnings // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary // 1.2 (2014-05-27) fix some RAD types that had crept into the new code @@ -46,7 +51,13 @@ // ADDITIONAL CONTRIBUTORS // // Ulf Winklemann: move-by-word in 1.1 -// Scott Graham: mouse selection bugfix in 1.3 +// Fabian Giesen: secondary key inputs in 1.5 +// Martins Mozeiko: STB_TEXTEDIT_memmove +// +// Bugfixes: +// Scott Graham +// Daniel Keller +// Omar Cornut // // USAGE // @@ -141,11 +152,17 @@ // STB_TEXTEDIT_K_REDO keyboard input to perform redo // // Optional: -// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode -// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), -// required for WORDLEFT/WORDRIGHT -// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT -// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT +// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode +// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), +// required for default WORDLEFT/WORDRIGHT handlers +// STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to +// STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to +// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT +// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT +// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line +// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line +// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text +// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text // // Todo: // STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page @@ -356,7 +373,10 @@ typedef struct // included just the "header" portion #ifdef STB_TEXTEDIT_IMPLEMENTATION -#include // memmove +#ifndef STB_TEXTEDIT_memmove +#include +#define STB_TEXTEDIT_memmove memmove +#endif ///////////////////////////////////////////////////////////////////////////// @@ -372,9 +392,6 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) float base_y = 0, prev_x; int i=0, k; - if (y < 0) - return 0; - r.x0 = r.x1 = 0; r.ymin = r.ymax = 0; r.num_chars = 0; @@ -385,6 +402,9 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) if (r.num_chars <= 0) return n; + if (i==0 && y < base_y + r.ymin) + return 0; + if (y < base_y + r.ymax) break; @@ -601,9 +621,9 @@ static int is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx ) return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : 1; } -static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) +#ifndef STB_TEXTEDIT_MOVEWORDLEFT +static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, int c ) { - int c = _state->cursor - 1; while( c >= 0 && !is_word_boundary( _str, c ) ) --c; @@ -612,11 +632,13 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_Te return c; } +#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous +#endif -static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) +#ifndef STB_TEXTEDIT_MOVEWORDRIGHT +static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, int c ) { const int len = STB_TEXTEDIT_STRINGLEN(_str); - int c = _state->cursor+1; while( c < len && !is_word_boundary( _str, c ) ) ++c; @@ -625,6 +647,9 @@ static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, STB_Texted return c; } +#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next +#endif + #endif // update selection and cursor to match each other @@ -746,21 +771,12 @@ retry: state->has_preferred_x = 0; break; -#ifdef STB_TEXTEDIT_IS_SPACE +#ifdef STB_TEXTEDIT_MOVEWORDLEFT case STB_TEXTEDIT_K_WORDLEFT: if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_first(state); else { - state->cursor = stb_textedit_move_to_word_previous(str, state); - stb_textedit_clamp( str, state ); - } - break; - - case STB_TEXTEDIT_K_WORDRIGHT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str, state); - else { - state->cursor = stb_textedit_move_to_word_next(str, state); + state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor-1); stb_textedit_clamp( str, state ); } break; @@ -769,17 +785,28 @@ retry: if( !STB_TEXT_HAS_SELECTION( state ) ) stb_textedit_prep_selection_at_cursor(state); - state->cursor = stb_textedit_move_to_word_previous(str, state); + state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor-1); state->select_end = state->cursor; stb_textedit_clamp( str, state ); break; +#endif + +#ifdef STB_TEXTEDIT_MOVEWORDRIGHT + case STB_TEXTEDIT_K_WORDRIGHT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else { + state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor+1); + stb_textedit_clamp( str, state ); + } + break; case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: if( !STB_TEXT_HAS_SELECTION( state ) ) stb_textedit_prep_selection_at_cursor(state); - state->cursor = stb_textedit_move_to_word_next(str, state); + state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor+1); state->select_end = state->cursor; stb_textedit_clamp( str, state ); @@ -923,23 +950,35 @@ retry: state->has_preferred_x = 0; break; +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2: +#endif case STB_TEXTEDIT_K_TEXTSTART: state->cursor = state->select_start = state->select_end = 0; state->has_preferred_x = 0; break; +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2: +#endif case STB_TEXTEDIT_K_TEXTEND: state->cursor = STB_TEXTEDIT_STRINGLEN(str); state->select_start = state->select_end = 0; state->has_preferred_x = 0; break; +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: +#endif case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: stb_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = 0; state->has_preferred_x = 0; break; +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: +#endif case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: stb_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); @@ -947,6 +986,9 @@ retry: break; +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2: +#endif case STB_TEXTEDIT_K_LINESTART: { StbFindState find; stb_textedit_clamp(str, state); @@ -957,18 +999,25 @@ retry: break; } +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2: +#endif case STB_TEXTEDIT_K_LINEEND: { StbFindState find; stb_textedit_clamp(str, state); stb_textedit_move_to_first(state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + state->has_preferred_x = 0; state->cursor = find.first_char + find.length; if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) - state->cursor--; - state->has_preferred_x = 0; + --state->cursor; break; } +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: +#endif case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: { StbFindState find; stb_textedit_clamp(str, state); @@ -979,15 +1028,19 @@ retry: break; } +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: +#endif case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { StbFindState find; stb_textedit_clamp(str, state); stb_textedit_prep_selection_at_cursor(state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - state->cursor = state->select_end = find.first_char + find.length; - if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) - state->cursor = state->select_end = state->cursor - 1; state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) + --state->cursor; + state->select_end = state->cursor; break; } @@ -1018,13 +1071,13 @@ static void stb_textedit_discard_undo(StbUndoState *state) int n = state->undo_rec[0].insert_length, i; // delete n characters from all other records state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 - memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); + STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); for (i=0; i < state->undo_point; ++i) if (state->undo_rec[i].char_storage >= 0) state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it } --state->undo_point; - memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0]))); + STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0]))); } } @@ -1042,13 +1095,13 @@ static void stb_textedit_discard_redo(StbUndoState *state) int n = state->undo_rec[k].insert_length, i; // delete n characters from all other records state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 - memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); for (i=state->redo_point; i < k; ++i) if (state->undo_rec[i].char_storage >= 0) state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 } ++state->redo_point; - memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); + STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); } } diff --git a/stb_truetype.h b/stb_truetype.h index 1f93135b0..e6dae9751 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -1,4 +1,4 @@ -// stb_truetype.h - v1.08 - public domain +// stb_truetype.h - v1.10 - public domain // authored from 2009-2015 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -21,6 +21,10 @@ // Mikko Mononen: compound shape support, more cmap formats // Tor Andersson: kerning, subpixel rendering // +// Misc other: +// Ryan Gordon +// Simon Glass +// // Bug/warning reports/fixes: // "Zer" on mollyrocket (with fix) // Cass Everitt @@ -42,12 +46,13 @@ // Sergey Popov // Giumo X. Clanjor // Higor Euripedes -// -// Misc other: -// Ryan Gordon +// Thomas Fields +// Derek Vinyard // // VERSION HISTORY // +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; // variant PackFontRanges to pack and render in separate phases; @@ -65,9 +70,9 @@ // // LICENSE // -// This software is in the public domain. Where that dedication is not -// recognized, you are granted a perpetual, irrevocable license to copy, -// distribute, and modify this file as you see fit. +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. // // USAGE // @@ -403,6 +408,11 @@ int main(int arg, char **argv) #define STBTT_sqrt(x) sqrt(x) #endif + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h #ifndef STBTT_malloc #include @@ -626,7 +636,7 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); // The following structure is defined publically so you can declare one on // the stack or as a global or etc, but you should treat it as opaque. -typedef struct stbtt_fontinfo +struct stbtt_fontinfo { void * userdata; unsigned char * data; // pointer to .ttf file @@ -637,7 +647,7 @@ typedef struct stbtt_fontinfo int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph -} stbtt_fontinfo; +}; STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); // Given an offset into the file that defines a font, this function builds @@ -1556,7 +1566,7 @@ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) { - int x0,y0,x1,y1; + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { // e.g. space character if (ix0) *ix0 = 0; @@ -1672,6 +1682,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i { stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); if (!z) return z; // round dx down to avoid overshooting @@ -1693,6 +1704,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i { stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); //STBTT_assert(e->y0 <= start_point); if (!z) return z; z->fdx = dxdy; @@ -1817,21 +1829,23 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, while (e->y0 <= scan_y) { if (e->y1 > scan_y) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); - // find insertion point - if (active == NULL) - active = z; - else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - stbtt__active_edge *p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } } } ++e; @@ -1986,7 +2000,7 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, } y_crossing += dy * (x2 - (x1+1)); - STBTT_assert(fabs(area) <= 1.01f); + STBTT_assert(STBTT_fabs(area) <= 1.01f); scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); @@ -2102,10 +2116,12 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, while (e->y0 <= scan_y_bottom) { if (e->y0 != e->y1) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); - STBTT_assert(z->ey >= scan_y_top); - // insert at front - z->next = active; - active = z; + if (z != NULL) { + STBTT_assert(z->ey >= scan_y_top); + // insert at front + z->next = active; + active = z; + } } ++e; } @@ -2121,7 +2137,7 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int m; sum += scanline2[i]; k = scanline[i] + sum; - k = (float) fabs(k)*255 + 0.5f; + k = (float) STBTT_fabs(k)*255 + 0.5f; m = (int) k; if (m > 255) m = 255; result->pixels[j*result->stride + i] = (unsigned char) m; @@ -2422,7 +2438,10 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info if (scale_x == 0) scale_x = scale_y; if (scale_y == 0) { - if (scale_x == 0) return NULL; + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } scale_y = scale_x; } @@ -2514,6 +2533,7 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // fo float scale; int x,y,bottom_y, i; stbtt_fontinfo f; + f.userdata = NULL; if (!stbtt_InitFont(&f, data, offset)) return -1; STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels @@ -2707,6 +2727,7 @@ static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_i unsigned char buffer[STBTT_MAX_OVERSAMPLE]; int safe_w = w - kernel_width; int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze for (j=0; j < h; ++j) { int i; unsigned int total; @@ -2768,6 +2789,7 @@ static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_i unsigned char buffer[STBTT_MAX_OVERSAMPLE]; int safe_h = h - kernel_width; int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze for (j=0; j < w; ++j) { int i; unsigned int total; @@ -2976,6 +2998,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd if (rects == NULL) return 0; + info.userdata = spc->user_allocator_context; stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); @@ -3193,6 +3216,10 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const // FULL VERSION HISTORY // +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; // allow PackFontRanges to pack and render in separate phases;