mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-15 01:17:00 +00:00
Added word-wrapping API TextWrapped(), PushTextWrapPos(), PopTextWrapPos()
Added word-wrapping sample in the test window. Added IsItemFocused() to tell if last widget is being focused for keyboard input.
This commit is contained in:
parent
74363c5a43
commit
78645a7dba
432
imgui.cpp
432
imgui.cpp
@ -122,7 +122,7 @@
|
|||||||
some functions like TreeNode() implicitly creates a scope for you by calling PushID()
|
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.
|
- 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!
|
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, using a static string as ID will preserve your node open/closed state when the targetted object change
|
e.g. When displaying a single object, 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
|
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.
|
- 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" display "Label" and uses "Label" as ID
|
||||||
@ -144,7 +144,7 @@
|
|||||||
- 2014/09/24 (1.12) moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
|
- 2014/09/24 (1.12) moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
|
||||||
- 2014/08/30 (1.09) removed IO.FontHeight (now computed automatically)
|
- 2014/08/30 (1.09) removed IO.FontHeight (now computed automatically)
|
||||||
- 2014/08/30 (1.09) moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
|
- 2014/08/30 (1.09) moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
|
||||||
- 2014/08/28 (1.09) changed the behaviour of IO.PixelCenterOffset following various rendering fixes
|
- 2014/08/28 (1.09) changed the behavior of IO.PixelCenterOffset following various rendering fixes
|
||||||
|
|
||||||
ISSUES & TODO-LIST
|
ISSUES & TODO-LIST
|
||||||
==================
|
==================
|
||||||
@ -225,7 +225,7 @@ namespace ImGui
|
|||||||
static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, bool repeat = false);
|
static bool ButtonBehaviour(const ImGuiAabb& bb, const ImGuiID& id, bool* out_hovered, bool* out_held, bool allow_key_modifiers, bool repeat = false);
|
||||||
static void LogText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
|
static void LogText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
|
||||||
|
|
||||||
static void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, const bool hide_text_after_hash = true);
|
static void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true, float wrap_width = 0.0f);
|
||||||
static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
|
static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
|
||||||
static void RenderCollapseTriangle(ImVec2 p_min, bool open, float scale = 1.0f, bool shadow = false);
|
static void RenderCollapseTriangle(ImVec2 p_min, bool open, float scale = 1.0f, bool shadow = false);
|
||||||
|
|
||||||
@ -590,9 +590,11 @@ struct ImGuiDrawContext
|
|||||||
int TreeDepth;
|
int TreeDepth;
|
||||||
ImGuiAabb LastItemAabb;
|
ImGuiAabb LastItemAabb;
|
||||||
bool LastItemHovered;
|
bool LastItemHovered;
|
||||||
|
bool LastItemFocused;
|
||||||
ImVector<ImGuiWindow*> ChildWindows;
|
ImVector<ImGuiWindow*> ChildWindows;
|
||||||
ImVector<bool> AllowKeyboardFocus;
|
ImVector<bool> AllowKeyboardFocus;
|
||||||
ImVector<float> ItemWidth;
|
ImVector<float> ItemWidth;
|
||||||
|
ImVector<float> TextWrapPos;
|
||||||
ImVector<ImGuiColMod> ColorModifiers;
|
ImVector<ImGuiColMod> ColorModifiers;
|
||||||
ImGuiColorEditMode ColorEditMode;
|
ImGuiColorEditMode ColorEditMode;
|
||||||
ImGuiStorage* StateStorage;
|
ImGuiStorage* StateStorage;
|
||||||
@ -613,6 +615,7 @@ struct ImGuiDrawContext
|
|||||||
TreeDepth = 0;
|
TreeDepth = 0;
|
||||||
LastItemAabb = ImGuiAabb(0.0f,0.0f,0.0f,0.0f);
|
LastItemAabb = ImGuiAabb(0.0f,0.0f,0.0f,0.0f);
|
||||||
LastItemHovered = false;
|
LastItemHovered = false;
|
||||||
|
LastItemFocused = true;
|
||||||
StateStorage = NULL;
|
StateStorage = NULL;
|
||||||
OpenNextNode = -1;
|
OpenNextNode = -1;
|
||||||
|
|
||||||
@ -1068,6 +1071,9 @@ bool ImGuiWindow::FocusItemRegister(bool is_active)
|
|||||||
if (allow_keyboard_focus)
|
if (allow_keyboard_focus)
|
||||||
FocusIdxTabCounter++;
|
FocusIdxTabCounter++;
|
||||||
|
|
||||||
|
if (is_active)
|
||||||
|
window->DC.LastItemFocused = true;
|
||||||
|
|
||||||
// Process keyboard input at this point: TAB, Shift-TAB switch focus
|
// Process keyboard input at this point: TAB, Shift-TAB switch focus
|
||||||
// We can always TAB out of a widget that doesn't allow tabbing in.
|
// We can always TAB out of a widget that doesn't allow tabbing in.
|
||||||
if (FocusIdxAllRequestNext == IM_INT_MAX && FocusIdxTabRequestNext == IM_INT_MAX && is_active && ImGui::IsKeyPressedMap(ImGuiKey_Tab))
|
if (FocusIdxAllRequestNext == IM_INT_MAX && FocusIdxTabRequestNext == IM_INT_MAX && is_active && ImGui::IsKeyPressedMap(ImGuiKey_Tab))
|
||||||
@ -1649,8 +1655,24 @@ static void LogText(const ImVec2& ref_pos, const char* text, const char* text_en
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
|
||||||
|
{
|
||||||
|
if (wrap_pos_x < 0.0f)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
if (wrap_pos_x == 0.0f)
|
||||||
|
wrap_pos_x = GetWindowContentRegionMax().x;
|
||||||
|
if (wrap_pos_x > 0.0f)
|
||||||
|
wrap_pos_x += window->Pos.x; // wrap_pos_x is provided is window local space
|
||||||
|
|
||||||
|
const float wrap_width = wrap_pos_x > 0.0f ? ImMax(wrap_pos_x - pos.x, 0.00001f) : 0.0f;
|
||||||
|
return wrap_width;
|
||||||
|
}
|
||||||
|
|
||||||
// Internal ImGui function to render text (called from ImGui::Text(), ImGui::TextUnformatted(), etc.)
|
// Internal ImGui function to render text (called from ImGui::Text(), ImGui::TextUnformatted(), etc.)
|
||||||
static void RenderText(ImVec2 pos, const char* text, const char* text_end, const bool hide_text_after_hash)
|
// ImGui::RenderText() calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
|
||||||
|
static void RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash, float wrap_width)
|
||||||
{
|
{
|
||||||
ImGuiState& g = GImGui;
|
ImGuiState& g = GImGui;
|
||||||
ImGuiWindow* window = GetCurrentWindow();
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
@ -1669,13 +1691,12 @@ static void RenderText(ImVec2 pos, const char* text, const char* text_end, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int text_len = (int)(text_display_end - text);
|
const int text_len = (int)(text_display_end - text);
|
||||||
//IM_ASSERT(text_len >= 0 && text_len < 10000); // Suspicious text length
|
|
||||||
if (text_len > 0)
|
if (text_len > 0)
|
||||||
{
|
{
|
||||||
// Render
|
// Render
|
||||||
window->DrawList->AddText(window->Font(), window->FontSize(), pos, window->Color(ImGuiCol_Text), text, text + text_len);
|
window->DrawList->AddText(window->Font(), window->FontSize(), pos, window->Color(ImGuiCol_Text), text, text + text_len, wrap_width);
|
||||||
|
|
||||||
// Log as text. We split text into individual lines to add the tree level padding
|
// Log as text. We split text into individual lines to add current tree level padding
|
||||||
if (g.LogEnabled)
|
if (g.LogEnabled)
|
||||||
LogText(pos, text, text_display_end);
|
LogText(pos, text, text_display_end);
|
||||||
}
|
}
|
||||||
@ -1689,7 +1710,7 @@ static void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border,
|
|||||||
window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
|
window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
|
||||||
if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
|
if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
|
||||||
{
|
{
|
||||||
// FIXME: I have no idea how this is working correctly but it is the best I've found that works on multiple rendering
|
// FIXME: This is the best I've found that works on multiple renderer/back ends. Rather dodgy.
|
||||||
const float offset = GImGui.IO.PixelCenterOffset;
|
const float offset = GImGui.IO.PixelCenterOffset;
|
||||||
window->DrawList->AddRect(p_min+ImVec2(1.5f-offset,1.5f-offset), p_max+ImVec2(1.0f-offset*2,1.0f-offset*2), window->Color(ImGuiCol_BorderShadow), rounding);
|
window->DrawList->AddRect(p_min+ImVec2(1.5f-offset,1.5f-offset), p_max+ImVec2(1.0f-offset*2,1.0f-offset*2), window->Color(ImGuiCol_BorderShadow), rounding);
|
||||||
window->DrawList->AddRect(p_min+ImVec2(0.5f-offset,0.5f-offset), p_max+ImVec2(0.0f-offset*2,0.0f-offset*2), window->Color(ImGuiCol_Border), rounding);
|
window->DrawList->AddRect(p_min+ImVec2(0.5f-offset,0.5f-offset), p_max+ImVec2(0.0f-offset*2,0.0f-offset*2), window->Color(ImGuiCol_Border), rounding);
|
||||||
@ -1727,7 +1748,7 @@ static void RenderCollapseTriangle(ImVec2 p_min, bool open, float scale, bool sh
|
|||||||
|
|
||||||
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
|
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
|
||||||
// CalcTextSize("") should return ImVec2(0.0f, GImGui.FontSize)
|
// CalcTextSize("") should return ImVec2(0.0f, GImGui.FontSize)
|
||||||
ImVec2 CalcTextSize(const char* text, const char* text_end, const bool hide_text_after_hash)
|
ImVec2 CalcTextSize(const char* text, const char* text_end, bool hide_text_after_hash, float wrap_width)
|
||||||
{
|
{
|
||||||
ImGuiWindow* window = GetCurrentWindow();
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
|
||||||
@ -1737,8 +1758,8 @@ ImVec2 CalcTextSize(const char* text, const char* text_end, const bool hide_text
|
|||||||
else
|
else
|
||||||
text_display_end = text_end;
|
text_display_end = text_end;
|
||||||
|
|
||||||
const ImVec2 size = window->Font()->CalcTextSizeA(window->FontSize(), 0, text, text_display_end, NULL);
|
const ImVec2 text_size = window->Font()->CalcTextSizeA(window->FontSize(), FLT_MAX, wrap_width, text, text_display_end, NULL);
|
||||||
return size;
|
return text_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find window given position, search front-to-back
|
// Find window given position, search front-to-back
|
||||||
@ -1864,6 +1885,12 @@ bool IsHovered()
|
|||||||
return window->DC.LastItemHovered;
|
return window->DC.LastItemHovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsItemFocused()
|
||||||
|
{
|
||||||
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
return window->DC.LastItemFocused;
|
||||||
|
}
|
||||||
|
|
||||||
ImVec2 GetItemBoxMin()
|
ImVec2 GetItemBoxMin()
|
||||||
{
|
{
|
||||||
ImGuiWindow* window = GetCurrentWindow();
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
@ -2299,6 +2326,8 @@ bool Begin(const char* name, bool* open, ImVec2 size, float fill_alpha, ImGuiWin
|
|||||||
window->DC.ItemWidth.push_back(window->ItemWidthDefault);
|
window->DC.ItemWidth.push_back(window->ItemWidthDefault);
|
||||||
window->DC.AllowKeyboardFocus.resize(0);
|
window->DC.AllowKeyboardFocus.resize(0);
|
||||||
window->DC.AllowKeyboardFocus.push_back(true);
|
window->DC.AllowKeyboardFocus.push_back(true);
|
||||||
|
window->DC.TextWrapPos.resize(0);
|
||||||
|
window->DC.TextWrapPos.push_back(-1.0f); // disabled
|
||||||
window->DC.ColorModifiers.resize(0);
|
window->DC.ColorModifiers.resize(0);
|
||||||
window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect;
|
window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect;
|
||||||
window->DC.ColumnCurrent = 0;
|
window->DC.ColumnCurrent = 0;
|
||||||
@ -2464,6 +2493,18 @@ void PopAllowKeyboardFocus()
|
|||||||
window->DC.AllowKeyboardFocus.pop_back();
|
window->DC.AllowKeyboardFocus.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PushTextWrapPos(float wrap_x)
|
||||||
|
{
|
||||||
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
window->DC.TextWrapPos.push_back(wrap_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopTextWrapPos()
|
||||||
|
{
|
||||||
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
window->DC.TextWrapPos.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
void PushStyleColor(ImGuiCol idx, const ImVec4& col)
|
void PushStyleColor(ImGuiCol idx, const ImVec4& col)
|
||||||
{
|
{
|
||||||
ImGuiState& g = GImGui;
|
ImGuiState& g = GImGui;
|
||||||
@ -2583,6 +2624,7 @@ ImVec2 GetWindowContentRegionMin()
|
|||||||
return ImVec2(0, window->TitleBarHeight()) + window->WindowPadding();
|
return ImVec2(0, window->TitleBarHeight()) + window->WindowPadding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Provide an equivalent that gives the min/max region considering columns.
|
||||||
ImVec2 GetWindowContentRegionMax()
|
ImVec2 GetWindowContentRegionMax()
|
||||||
{
|
{
|
||||||
ImGuiWindow* window = GetCurrentWindow();
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
@ -2718,6 +2760,21 @@ void TextColored(const ImVec4& col, const char* fmt, ...)
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextWrappedV(const char* fmt, va_list args)
|
||||||
|
{
|
||||||
|
ImGui::PushTextWrapPos(0.0f);
|
||||||
|
TextV(fmt, args);
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWrapped(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
TextWrappedV(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
void TextUnformatted(const char* text, const char* text_end)
|
void TextUnformatted(const char* text, const char* text_end)
|
||||||
{
|
{
|
||||||
ImGuiState& g = GImGui;
|
ImGuiState& g = GImGui;
|
||||||
@ -2730,11 +2787,14 @@ void TextUnformatted(const char* text, const char* text_end)
|
|||||||
if (text_end == NULL)
|
if (text_end == NULL)
|
||||||
text_end = text + strlen(text);
|
text_end = text + strlen(text);
|
||||||
|
|
||||||
if (text_end - text > 2000)
|
const float wrap_pos_x = window->DC.TextWrapPos.back();
|
||||||
|
const bool wrap_enabled = wrap_pos_x >= 0.0f;
|
||||||
|
if (text_end - text > 2000 && !wrap_enabled)
|
||||||
{
|
{
|
||||||
// Long text!
|
// Long text!
|
||||||
// Perform manual coarse clipping to optimize for long multi-line text
|
// Perform manual coarse clipping to optimize for long multi-line text
|
||||||
// From this point we will only compute the width of lines that are visible.
|
// From this point we will only compute the width of lines that are visible.
|
||||||
|
// Optimization only available when word-wrapping is disabled.
|
||||||
const char* line = text;
|
const char* line = text;
|
||||||
const float line_height = ImGui::GetTextLineHeight();
|
const float line_height = ImGui::GetTextLineHeight();
|
||||||
const ImVec2 start_pos = window->DC.CursorPos;
|
const ImVec2 start_pos = window->DC.CursorPos;
|
||||||
@ -2804,7 +2864,8 @@ void TextUnformatted(const char* text, const char* text_end)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const ImVec2 text_size = CalcTextSize(text_begin, text_end, false);
|
const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
|
||||||
|
const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
|
||||||
ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + text_size);
|
ImGuiAabb bb(window->DC.CursorPos, window->DC.CursorPos + text_size);
|
||||||
ItemSize(bb.GetSize(), &bb.Min);
|
ItemSize(bb.GetSize(), &bb.Min);
|
||||||
|
|
||||||
@ -2813,7 +2874,7 @@ void TextUnformatted(const char* text, const char* text_end)
|
|||||||
|
|
||||||
// Render
|
// Render
|
||||||
// We don't hide text after ## in this end-user function.
|
// We don't hide text after ## in this end-user function.
|
||||||
RenderText(bb.Min, text_begin, text_end, false);
|
RenderText(bb.Min, text_begin, text_end, false, wrap_width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3359,8 +3420,6 @@ bool SliderFloat(const char* label, float* v, float v_min, float v_max, const ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id);
|
|
||||||
|
|
||||||
const ImVec2 text_size = CalcTextSize(label);
|
const ImVec2 text_size = CalcTextSize(label);
|
||||||
const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f);
|
const ImGuiAabb frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, text_size.y) + style.FramePadding*2.0f);
|
||||||
const ImGuiAabb slider_bb(frame_bb.Min+g.Style.FramePadding, frame_bb.Max-g.Style.FramePadding);
|
const ImGuiAabb slider_bb(frame_bb.Min+g.Style.FramePadding, frame_bb.Max-g.Style.FramePadding);
|
||||||
@ -3373,6 +3432,8 @@ bool SliderFloat(const char* label, float* v, float v_min, float v_max, const ch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id);
|
||||||
|
|
||||||
const bool is_unbound = v_min == -FLT_MAX || v_min == FLT_MAX || v_max == -FLT_MAX || v_max == FLT_MAX;
|
const bool is_unbound = v_min == -FLT_MAX || v_min == FLT_MAX || v_max == -FLT_MAX || v_max == FLT_MAX;
|
||||||
|
|
||||||
const float grab_size_in_units = 1.0f; // In 'v' units. Probably needs to be parametrized, based on a 'v_step' value? decimal precision?
|
const float grab_size_in_units = 1.0f; // In 'v' units. Probably needs to be parametrized, based on a 'v_step' value? decimal precision?
|
||||||
@ -3879,7 +3940,7 @@ bool RadioButton(const char* label, int* v, int v_button)
|
|||||||
// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, ASCII, fixed-width font)
|
// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, ASCII, fixed-width font)
|
||||||
int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return (int)ImStrlenW(obj->Text); }
|
int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return (int)ImStrlenW(obj->Text); }
|
||||||
ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; }
|
ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; }
|
||||||
float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { (void)line_start_idx; return obj->Font->CalcTextSizeW(obj->FontSize, 0, &obj->Text[char_idx], &obj->Text[char_idx]+1, NULL).x; }
|
float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { (void)line_start_idx; return obj->Font->CalcTextSizeW(obj->FontSize, FLT_MAX, &obj->Text[char_idx], &obj->Text[char_idx]+1, NULL).x; }
|
||||||
int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
|
int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
|
||||||
ImWchar STB_TEXTEDIT_NEWLINE = '\n';
|
ImWchar STB_TEXTEDIT_NEWLINE = '\n';
|
||||||
void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
|
void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
|
||||||
@ -3947,7 +4008,7 @@ void ImGuiTextEditState::UpdateScrollOffset()
|
|||||||
{
|
{
|
||||||
// Scroll in chunks of quarter width
|
// Scroll in chunks of quarter width
|
||||||
const float scroll_x_increment = Width * 0.25f;
|
const float scroll_x_increment = Width * 0.25f;
|
||||||
const float cursor_offset_x = Font->CalcTextSizeW(FontSize, 0, Text, Text+StbState.cursor, NULL).x;
|
const float cursor_offset_x = Font->CalcTextSizeW(FontSize, FLT_MAX, Text, Text+StbState.cursor, NULL).x;
|
||||||
if (ScrollX > cursor_offset_x)
|
if (ScrollX > cursor_offset_x)
|
||||||
ScrollX = ImMax(0.0f, cursor_offset_x - scroll_x_increment);
|
ScrollX = ImMax(0.0f, cursor_offset_x - scroll_x_increment);
|
||||||
else if (ScrollX < cursor_offset_x - Width)
|
else if (ScrollX < cursor_offset_x - Width)
|
||||||
@ -3971,7 +4032,7 @@ const char* ImGuiTextEditState::GetTextPointerClippedA(ImFont font, float font_s
|
|||||||
return text;
|
return text;
|
||||||
|
|
||||||
const char* text_clipped_end = NULL;
|
const char* text_clipped_end = NULL;
|
||||||
const ImVec2 text_size = font->CalcTextSizeA(font_size, width, text, NULL, &text_clipped_end);
|
const ImVec2 text_size = font->CalcTextSizeA(font_size, width, 0.0f, text, NULL, &text_clipped_end);
|
||||||
if (out_text_size)
|
if (out_text_size)
|
||||||
*out_text_size = text_size;
|
*out_text_size = text_size;
|
||||||
return text_clipped_end;
|
return text_clipped_end;
|
||||||
@ -4835,6 +4896,7 @@ static bool ClipAdvance(const ImGuiAabb& bb)
|
|||||||
{
|
{
|
||||||
ImGuiWindow* window = GetCurrentWindow();
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
window->DC.LastItemAabb = bb;
|
window->DC.LastItemAabb = bb;
|
||||||
|
window->DC.LastItemFocused = false;
|
||||||
if (ImGui::IsClipped(bb))
|
if (ImGui::IsClipped(bb))
|
||||||
{
|
{
|
||||||
window->DC.LastItemHovered = false;
|
window->DC.LastItemHovered = false;
|
||||||
@ -5331,7 +5393,7 @@ void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImDrawList::AddText(ImFont font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
|
void ImDrawList::AddText(ImFont font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width)
|
||||||
{
|
{
|
||||||
if ((col >> 24) == 0)
|
if ((col >> 24) == 0)
|
||||||
return;
|
return;
|
||||||
@ -5345,7 +5407,7 @@ void ImDrawList::AddText(ImFont font, float font_size, const ImVec2& pos, ImU32
|
|||||||
const size_t vtx_begin = vtx_buffer.size();
|
const size_t vtx_begin = vtx_buffer.size();
|
||||||
ReserveVertices(vtx_count_max);
|
ReserveVertices(vtx_count_max);
|
||||||
|
|
||||||
font->RenderText(font_size, pos, col, clip_rect_stack.back(), text_begin, text_end, vtx_write);
|
font->RenderText(font_size, pos, col, clip_rect_stack.back(), text_begin, text_end, vtx_write, wrap_width);
|
||||||
|
|
||||||
// give back unused vertices
|
// give back unused vertices
|
||||||
vtx_buffer.resize((size_t)(vtx_write - &vtx_buffer.front()));
|
vtx_buffer.resize((size_t)(vtx_write - &vtx_buffer.front()));
|
||||||
@ -5491,7 +5553,7 @@ void ImBitmapFont::BuildLookupTable()
|
|||||||
IndexLookup[Glyphs[i].Id] = (int)i;
|
IndexLookup[Glyphs[i].Id] = (int)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImBitmapFont::FntGlyph* ImBitmapFont::FindGlyph(unsigned short c) const
|
const ImBitmapFont::FntGlyph* ImBitmapFont::FindGlyph(unsigned short c, const ImBitmapFont::FntGlyph* fallback) const
|
||||||
{
|
{
|
||||||
if (c < (int)IndexLookup.size())
|
if (c < (int)IndexLookup.size())
|
||||||
{
|
{
|
||||||
@ -5499,7 +5561,7 @@ const ImBitmapFont::FntGlyph* ImBitmapFont::FindGlyph(unsigned short c) const
|
|||||||
if (i >= 0 && i < (int)GlyphsCount)
|
if (i >= 0 && i < (int)GlyphsCount)
|
||||||
return &Glyphs[i];
|
return &Glyphs[i];
|
||||||
}
|
}
|
||||||
return NULL;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert UTF-8 to 32-bits character, process single character input.
|
// Convert UTF-8 to 32-bits character, process single character input.
|
||||||
@ -5635,10 +5697,110 @@ static ptrdiff_t ImTextStrToUtf8(char* buf, size_t buf_size, const ImWchar* in_t
|
|||||||
return buf_out - buf;
|
return buf_out - buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 ImBitmapFont::CalcTextSizeA(float size, float max_width, const char* text_begin, const char* text_end, const char** remaining) const
|
const char* ImBitmapFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width, const FntGlyph* fallback_glyph) const
|
||||||
|
{
|
||||||
|
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
|
||||||
|
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
|
||||||
|
|
||||||
|
// For references, possible wrap point marked with ^
|
||||||
|
// "aaa bbb, ccc,ddd. eee fff. ggg!"
|
||||||
|
// ^ ^ ^ ^ ^__ ^ ^
|
||||||
|
|
||||||
|
// List of hardcoded separators: .,;!?'"
|
||||||
|
|
||||||
|
// Skip extra blanks after a line returns (that includes not counting them in width computation)
|
||||||
|
// e.g. "Hello world"
|
||||||
|
// -->
|
||||||
|
// "Hello"
|
||||||
|
// "world"
|
||||||
|
|
||||||
|
// Cut words that cannot possibly fit within one line.
|
||||||
|
// e.g.: "The tropical fish" with ~5 characters worth of width
|
||||||
|
// -->
|
||||||
|
// "The tr"
|
||||||
|
// "opical"
|
||||||
|
// "fish"
|
||||||
|
|
||||||
|
float line_width = 0.0f;
|
||||||
|
float word_width = 0.0f;
|
||||||
|
float blank_width = 0.0f;
|
||||||
|
|
||||||
|
const char* word_end = text;
|
||||||
|
const char* prev_word_end = NULL;
|
||||||
|
bool inside_word = true;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
line_width = word_width = blank_width = 0.0f;
|
||||||
|
inside_word = true;
|
||||||
|
s = next_s;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float char_width = 0.0f;
|
||||||
|
if (c == '\t')
|
||||||
|
{
|
||||||
|
if (const FntGlyph* glyph = FindGlyph((unsigned short)' '))
|
||||||
|
char_width = (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (const FntGlyph* glyph = FindGlyph((unsigned short)c, fallback_glyph))
|
||||||
|
char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == ' ' || c == '\t')
|
||||||
|
{
|
||||||
|
if (inside_word)
|
||||||
|
{
|
||||||
|
line_width += blank_width;
|
||||||
|
blank_width = 0.0f;
|
||||||
|
}
|
||||||
|
blank_width += char_width;
|
||||||
|
inside_word = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
word_width += char_width;
|
||||||
|
if (inside_word)
|
||||||
|
{
|
||||||
|
word_end = next_s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev_word_end = word_end;
|
||||||
|
line_width += word_width + blank_width;
|
||||||
|
word_width = blank_width = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow wrapping after punctuation.
|
||||||
|
inside_word = !(c == '.' || c == ',' || c == ';' || c == '!' || c == '?' || c == '\'' || c == '\"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// We ignore blank width at the end of the line (they can be skipped)
|
||||||
|
if (line_width + word_width >= wrap_width)
|
||||||
|
{
|
||||||
|
// Words that cannot possibly fit within an entire line will be cut anywhere.
|
||||||
|
if (word_width < wrap_width)
|
||||||
|
s = prev_word_end ? prev_word_end : word_end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = next_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImBitmapFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const
|
||||||
{
|
{
|
||||||
if (max_width == 0.0f)
|
|
||||||
max_width = FLT_MAX;
|
|
||||||
if (!text_end)
|
if (!text_end)
|
||||||
text_end = text_begin + strlen(text_begin);
|
text_end = text_begin + strlen(text_begin);
|
||||||
|
|
||||||
@ -5649,41 +5811,71 @@ ImVec2 ImBitmapFont::CalcTextSizeA(float size, float max_width, const char* text
|
|||||||
ImVec2 text_size = ImVec2(0,0);
|
ImVec2 text_size = ImVec2(0,0);
|
||||||
float line_width = 0.0f;
|
float line_width = 0.0f;
|
||||||
|
|
||||||
|
const bool word_wrap_enabled = (wrap_width > 0.0f);
|
||||||
|
const char* word_wrap_eol = NULL;
|
||||||
|
|
||||||
const char* s = text_begin;
|
const char* s = text_begin;
|
||||||
while (s < text_end)
|
while (s < text_end)
|
||||||
{
|
{
|
||||||
|
if (word_wrap_enabled)
|
||||||
|
{
|
||||||
|
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
||||||
|
if (!word_wrap_eol)
|
||||||
|
{
|
||||||
|
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width, fallback_glyph);
|
||||||
|
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
|
||||||
|
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s >= word_wrap_eol)
|
||||||
|
{
|
||||||
|
if (text_size.x < line_width)
|
||||||
|
text_size.x = line_width;
|
||||||
|
text_size.y += line_height;
|
||||||
|
line_width = 0.0f;
|
||||||
|
word_wrap_eol = NULL;
|
||||||
|
|
||||||
|
// Wrapping skips upcoming blanks
|
||||||
|
while (s < text_end)
|
||||||
|
{
|
||||||
|
const char c = *s;
|
||||||
|
if (c == ' ' || c == '\t') { 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;
|
unsigned int c;
|
||||||
const int bytes_count = ImTextCharFromUtf8(&c, s, text_end);
|
const int bytes_count = ImTextCharFromUtf8(&c, s, text_end);
|
||||||
s += bytes_count > 0 ? bytes_count : 1; // Handle decoding failure by skipping to next byte
|
s += bytes_count > 0 ? bytes_count : 1;
|
||||||
|
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
{
|
{
|
||||||
if (text_size.x < line_width)
|
if (text_size.x < line_width)
|
||||||
text_size.x = line_width;
|
text_size.x = line_width;
|
||||||
text_size.y += line_height;
|
text_size.y += line_height;
|
||||||
line_width = 0;
|
line_width = 0.0f;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if (c == '\t')
|
|
||||||
|
float char_width = 0.0f;
|
||||||
|
if (c == '\t')
|
||||||
{
|
{
|
||||||
// FIXME: Better TAB handling needed.
|
// FIXME: Better TAB handling
|
||||||
if (const FntGlyph* glyph = FindGlyph((unsigned short)' '))
|
if (const FntGlyph* glyph = FindGlyph((unsigned short)' '))
|
||||||
line_width += (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale;
|
char_width = (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale;
|
||||||
}
|
}
|
||||||
else
|
else if (const FntGlyph* glyph = FindGlyph((unsigned short)c, fallback_glyph))
|
||||||
{
|
{
|
||||||
const FntGlyph* glyph = FindGlyph((unsigned short)c);
|
char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale;
|
||||||
if (!glyph)
|
}
|
||||||
glyph = fallback_glyph;
|
|
||||||
if (glyph)
|
|
||||||
{
|
|
||||||
const float char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale;
|
|
||||||
//const float char_extend = (glyph->XOffset + glyph->Width * scale);
|
|
||||||
if (line_width + char_width >= max_width)
|
if (line_width + char_width >= max_width)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
line_width += char_width;
|
line_width += char_width;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line_width > 0 || text_size.y == 0.0f)
|
if (line_width > 0 || text_size.y == 0.0f)
|
||||||
{
|
{
|
||||||
@ -5700,8 +5892,6 @@ ImVec2 ImBitmapFont::CalcTextSizeA(float size, float max_width, const char* text
|
|||||||
|
|
||||||
ImVec2 ImBitmapFont::CalcTextSizeW(float size, float max_width, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining) const
|
ImVec2 ImBitmapFont::CalcTextSizeW(float size, float max_width, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining) const
|
||||||
{
|
{
|
||||||
if (max_width == 0.0f)
|
|
||||||
max_width = FLT_MAX;
|
|
||||||
if (!text_end)
|
if (!text_end)
|
||||||
text_end = text_begin + ImStrlenW(text_begin);
|
text_end = text_begin + ImStrlenW(text_begin);
|
||||||
|
|
||||||
@ -5722,29 +5912,28 @@ ImVec2 ImBitmapFont::CalcTextSizeW(float size, float max_width, const ImWchar* t
|
|||||||
if (text_size.x < line_width)
|
if (text_size.x < line_width)
|
||||||
text_size.x = line_width;
|
text_size.x = line_width;
|
||||||
text_size.y += line_height;
|
text_size.y += line_height;
|
||||||
line_width = 0;
|
line_width = 0.0f;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if (c == '\t')
|
|
||||||
|
float char_width = 0.0f;
|
||||||
|
if (c == '\t')
|
||||||
{
|
{
|
||||||
// FIXME: Better TAB handling needed.
|
// FIXME: Better TAB handling
|
||||||
if (const FntGlyph* glyph = FindGlyph((unsigned short)' '))
|
if (const FntGlyph* glyph = FindGlyph((unsigned short)' '))
|
||||||
line_width += (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale;
|
char_width = (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const FntGlyph* glyph = FindGlyph((unsigned short)c);
|
if (const FntGlyph* glyph = FindGlyph((unsigned short)c, fallback_glyph))
|
||||||
if (!glyph)
|
char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale;
|
||||||
glyph = fallback_glyph;
|
}
|
||||||
if (glyph)
|
|
||||||
{
|
|
||||||
const float char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale;
|
|
||||||
//const float char_extend = (glyph->XOffset + glyph->Width * scale);
|
|
||||||
if (line_width + char_width >= max_width)
|
if (line_width + char_width >= max_width)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
line_width += char_width;
|
line_width += char_width;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line_width > 0 || text_size.y == 0.0f)
|
if (line_width > 0 || text_size.y == 0.0f)
|
||||||
{
|
{
|
||||||
@ -5759,7 +5948,7 @@ ImVec2 ImBitmapFont::CalcTextSizeW(float size, float max_width, const ImWchar* t
|
|||||||
return text_size;
|
return text_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImBitmapFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect_ref, const char* text_begin, const char* text_end, ImDrawVert*& out_vertices) const
|
void ImBitmapFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect_ref, const char* text_begin, const char* text_end, ImDrawVert*& out_vertices, float wrap_width) const
|
||||||
{
|
{
|
||||||
if (!text_end)
|
if (!text_end)
|
||||||
text_end = text_begin + strlen(text_begin);
|
text_end = text_begin + strlen(text_begin);
|
||||||
@ -5775,17 +5964,46 @@ void ImBitmapFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& c
|
|||||||
pos.x = (float)(int)pos.x;
|
pos.x = (float)(int)pos.x;
|
||||||
pos.y = (float)(int)pos.y + GImGui.IO.FontYOffset;
|
pos.y = (float)(int)pos.y + GImGui.IO.FontYOffset;
|
||||||
|
|
||||||
const ImVec4 clip_rect = clip_rect_ref;
|
const bool word_wrap_enabled = (wrap_width > 0.0f);
|
||||||
|
const char* word_wrap_eol = NULL;
|
||||||
|
|
||||||
|
const ImVec4 clip_rect = clip_rect_ref;
|
||||||
float x = pos.x;
|
float x = pos.x;
|
||||||
float y = pos.y;
|
float y = pos.y;
|
||||||
for (const char* s = text_begin; s < text_end; )
|
|
||||||
|
const char* s = text_begin;
|
||||||
|
while (s < text_end)
|
||||||
{
|
{
|
||||||
|
if (word_wrap_enabled)
|
||||||
|
{
|
||||||
|
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
|
||||||
|
if (!word_wrap_eol)
|
||||||
|
{
|
||||||
|
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - pos.x), fallback_glyph);
|
||||||
|
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
|
||||||
|
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s >= word_wrap_eol)
|
||||||
|
{
|
||||||
|
x = pos.x;
|
||||||
|
y += line_height * scale;
|
||||||
|
word_wrap_eol = NULL;
|
||||||
|
|
||||||
|
// Wrapping skips upcoming blanks
|
||||||
|
while (s < text_end)
|
||||||
|
{
|
||||||
|
const char c = *s;
|
||||||
|
if (c == ' ' || c == '\t') { 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;
|
unsigned int c;
|
||||||
const int bytes_count = ImTextCharFromUtf8(&c, s, text_end);
|
const int bytes_count = ImTextCharFromUtf8(&c, s, text_end);
|
||||||
s += bytes_count > 0 ? bytes_count : 1; // Handle decoding failure by skipping to next byte
|
s += bytes_count > 0 ? bytes_count : 1;
|
||||||
if (c >= 0x10000)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
{
|
{
|
||||||
@ -5794,33 +6012,28 @@ void ImBitmapFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& c
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FntGlyph* glyph = FindGlyph((unsigned short)c);
|
float char_width = 0.0f;
|
||||||
if (!glyph)
|
if (c == '\t')
|
||||||
glyph = fallback_glyph;
|
|
||||||
if (glyph)
|
|
||||||
{
|
{
|
||||||
const float char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale;
|
// FIXME: Better TAB handling
|
||||||
//const float char_extend = (glyph->XOffset + glyph->Width * scale);
|
if (const FntGlyph* glyph = FindGlyph((unsigned short)' '))
|
||||||
|
char_width += (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale;
|
||||||
if (c != ' ' && c != '\n')
|
}
|
||||||
|
else if (const FntGlyph* glyph = FindGlyph((unsigned short)c, fallback_glyph))
|
||||||
{
|
{
|
||||||
// Clipping due to Y limits is more likely
|
char_width = (glyph->XAdvance + Info->SpacingHoriz) * scale;
|
||||||
|
if (c != ' ')
|
||||||
|
{
|
||||||
|
// Clipping on Y is more likely
|
||||||
const float y1 = (float)(y + (glyph->YOffset + outline*2) * scale);
|
const float y1 = (float)(y + (glyph->YOffset + outline*2) * scale);
|
||||||
const float y2 = (float)(y1 + glyph->Height * scale);
|
const float y2 = (float)(y1 + glyph->Height * scale);
|
||||||
if (y1 > clip_rect.w || y2 < clip_rect.y)
|
if (y1 <= clip_rect.w && y2 >= clip_rect.y)
|
||||||
{
|
{
|
||||||
x += char_width;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float x1 = (float)(x + (glyph->XOffset + outline) * scale);
|
const float x1 = (float)(x + (glyph->XOffset + outline) * scale);
|
||||||
const float x2 = (float)(x1 + glyph->Width * scale);
|
const float x2 = (float)(x1 + glyph->Width * scale);
|
||||||
if (x1 > clip_rect.z || x2 < clip_rect.x)
|
if (x1 <= clip_rect.z && x2 >= clip_rect.x)
|
||||||
{
|
{
|
||||||
x += char_width;
|
// Render a character
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float s1 = (glyph->X) * tex_scale_x;
|
const float s1 = (glyph->X) * tex_scale_x;
|
||||||
const float t1 = (glyph->Y) * tex_scale_y;
|
const float t1 = (glyph->Y) * tex_scale_y;
|
||||||
const float s2 = (glyph->X + glyph->Width) * tex_scale_x;
|
const float s2 = (glyph->X + glyph->Width) * tex_scale_x;
|
||||||
@ -5847,15 +6060,12 @@ void ImBitmapFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& c
|
|||||||
|
|
||||||
out_vertices += 6;
|
out_vertices += 6;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
x += char_width;
|
x += char_width;
|
||||||
}
|
}
|
||||||
else if (c == '\t')
|
|
||||||
{
|
|
||||||
if (const FntGlyph* glyph = FindGlyph((unsigned short)' '))
|
|
||||||
x += (glyph->XAdvance + Info->SpacingHoriz) * 4 * scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -5948,7 +6158,7 @@ void ShowUserGuide()
|
|||||||
ImGui::BulletText("Mouse Wheel to scroll.");
|
ImGui::BulletText("Mouse Wheel to scroll.");
|
||||||
if (g.IO.FontAllowUserScaling)
|
if (g.IO.FontAllowUserScaling)
|
||||||
ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents.");
|
ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents.");
|
||||||
ImGui::BulletText("TAB/SHIFT+TAB to cycle thru keyboard editable fields.");
|
ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
|
||||||
ImGui::BulletText("CTRL+Click on a slider to input text.");
|
ImGui::BulletText("CTRL+Click on a slider to input text.");
|
||||||
ImGui::BulletText(
|
ImGui::BulletText(
|
||||||
"While editing text:\n"
|
"While editing text:\n"
|
||||||
@ -6114,20 +6324,45 @@ void ShowTestWindow(bool* open)
|
|||||||
|
|
||||||
if (ImGui::TreeNode("Colored Text"))
|
if (ImGui::TreeNode("Colored Text"))
|
||||||
{
|
{
|
||||||
// This is a merely a shortcut, you can use PushStyleColor()/PopStyleColor() for more flexibility.
|
// Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
|
||||||
ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink");
|
ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink");
|
||||||
ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow");
|
ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow");
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Word Wrapping"))
|
||||||
|
{
|
||||||
|
// Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
|
||||||
|
ImGui::TextWrapped("This is a long paragraph. The text should automatically wrap on the edge of the window. The current implementation follows simple rules that works for English and possibly other languages.");
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
static float wrap_width = 200.0f;
|
||||||
|
ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f");
|
||||||
|
|
||||||
|
ImGui::Text("Test paragraph 1:");
|
||||||
|
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos() + ImVec2(wrap_width, 0.0f), ImGui::GetCursorScreenPos() + ImVec2(wrap_width+10, ImGui::GetTextLineHeight()), 0xFFFF00FF);
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
|
||||||
|
ImGui::Text("lazy dog. This paragraph is made to fit within %.0f pixels. The quick brown fox jumps over the lazy dog.", wrap_width);
|
||||||
|
ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemBoxMin(), ImGui::GetItemBoxMax(), 0xFF00FFFF);
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
|
||||||
|
ImGui::Text("Test paragraph 2:");
|
||||||
|
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetCursorScreenPos() + ImVec2(wrap_width, 0.0f), ImGui::GetCursorScreenPos() + ImVec2(wrap_width+10, ImGui::GetTextLineHeight()), 0xFFFF00FF);
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
|
||||||
|
ImGui::Text("aaaaaaaa bbbbbbbb, cccccccc,dddddddd. eeeeeeee ffffffff. gggggggg!hhhhhhhh");
|
||||||
|
ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemBoxMin(), ImGui::GetItemBoxMax(), 0xFF00FFFF);
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::TreeNode("UTF-8 Text"))
|
if (ImGui::TreeNode("UTF-8 Text"))
|
||||||
{
|
{
|
||||||
// UTF-8 test (need a suitable font, try extra_fonts/mplus* files for example)
|
// UTF-8 test (need a suitable font, try extra_fonts/mplus* files for example)
|
||||||
// Most compiler appears to support UTF-8 in source code (with Visual Studio you need to save your file as 'UTF-8 without signature')
|
// Most compiler appears to support UTF-8 in source code (with Visual Studio you need to save your file as 'UTF-8 without signature')
|
||||||
// However for the sake for maximum portability here we are *not* including raw UTF-8 character in this source file, instead we encode the string with with hexadecimal constants.
|
// However for the sake for maximum portability here we are *not* including raw UTF-8 character in this source file, instead we encode the string with hexadecimal constants.
|
||||||
// In your own application please be reasonable and use UTF-8 in the source or get the data from external files. :)
|
// In your own application please be reasonable and use UTF-8 in the source or get the data from external files! :)
|
||||||
//const char* utf8_string = "\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93\x20\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; // Japanese text for "Kakikukeo" (Hiragana) followed by "Nihongo" (kanji)
|
ImGui::TextWrapped("(CJK text will only appears if the font supports it. Please check in the extra_fonts/ folder if you intend to use non-ASCII characters. Note that characters values are preserved even if the font cannot be displayed, so you can safely copy & paste garbled characters.)");
|
||||||
ImGui::Text("(CJK text will only appears if the font supports it. Please check in\nthe extra_fonts/ folder if you intend to use non-ASCII characters.\nNote that characters values are preserved even if the font cannot be\ndisplayed, so you can safely copy & paste garbled characters.)");
|
|
||||||
ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
|
ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
|
||||||
ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
|
ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
|
||||||
static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
|
static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
|
||||||
@ -6462,15 +6697,26 @@ void ShowTestWindow(bool* open)
|
|||||||
bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine();
|
bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine();
|
||||||
bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine();
|
bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine();
|
||||||
bool focus_3 = ImGui::Button("Focus on 3");
|
bool focus_3 = ImGui::Button("Focus on 3");
|
||||||
|
int has_focus = 0;
|
||||||
static char buf[128] = "click on a button to set focus";
|
static char buf[128] = "click on a button to set focus";
|
||||||
|
|
||||||
if (focus_1) ImGui::SetKeyboardFocusHere();
|
if (focus_1) ImGui::SetKeyboardFocusHere();
|
||||||
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
|
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
|
||||||
|
if (ImGui::IsItemFocused()) has_focus = 1;
|
||||||
|
|
||||||
if (focus_2) ImGui::SetKeyboardFocusHere();
|
if (focus_2) ImGui::SetKeyboardFocusHere();
|
||||||
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
|
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
|
||||||
|
if (ImGui::IsItemFocused()) has_focus = 2;
|
||||||
|
|
||||||
ImGui::PushAllowKeyboardFocus(false);
|
ImGui::PushAllowKeyboardFocus(false);
|
||||||
if (focus_3) ImGui::SetKeyboardFocusHere();
|
if (focus_3) ImGui::SetKeyboardFocusHere();
|
||||||
ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf));
|
ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf));
|
||||||
|
if (ImGui::IsItemFocused()) has_focus = 3;
|
||||||
ImGui::PopAllowKeyboardFocus();
|
ImGui::PopAllowKeyboardFocus();
|
||||||
|
if (has_focus)
|
||||||
|
ImGui::Text("Item with focus: %d", has_focus);
|
||||||
|
else
|
||||||
|
ImGui::Text("Item with focus: <none>");
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
imgui.h
32
imgui.h
@ -159,13 +159,16 @@ namespace ImGui
|
|||||||
void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use 'offset' to access sub components of a multiple component widget.
|
void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use 'offset' to access sub components of a multiple component widget.
|
||||||
void SetTreeStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it).
|
void SetTreeStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it).
|
||||||
ImGuiStorage* GetTreeStateStorage();
|
ImGuiStorage* GetTreeStateStorage();
|
||||||
void PushItemWidth(float item_width);
|
|
||||||
|
void PushItemWidth(float item_width); // width of items for the common item+label case. default to ~2/3 of windows width.
|
||||||
void PopItemWidth();
|
void PopItemWidth();
|
||||||
float GetItemWidth();
|
float GetItemWidth();
|
||||||
void PushAllowKeyboardFocus(bool v); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets.
|
void PushAllowKeyboardFocus(bool v); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets.
|
||||||
void PopAllowKeyboardFocus();
|
void PopAllowKeyboardFocus();
|
||||||
void PushStyleColor(ImGuiCol idx, const ImVec4& col);
|
void PushStyleColor(ImGuiCol idx, const ImVec4& col);
|
||||||
void PopStyleColor();
|
void PopStyleColor();
|
||||||
|
void PushTextWrapPos(float wrap_pos_x); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space.
|
||||||
|
void PopTextWrapPos();
|
||||||
|
|
||||||
// Tooltip
|
// Tooltip
|
||||||
void SetTooltip(const char* fmt, ...); // set tooltip under mouse-cursor, typically use with ImGui::IsHovered(). last call wins.
|
void SetTooltip(const char* fmt, ...); // set tooltip under mouse-cursor, typically use with ImGui::IsHovered(). last call wins.
|
||||||
@ -200,10 +203,12 @@ namespace ImGui
|
|||||||
// Widgets
|
// Widgets
|
||||||
void Text(const char* fmt, ...);
|
void Text(const char* fmt, ...);
|
||||||
void TextV(const char* fmt, va_list args);
|
void TextV(const char* fmt, va_list args);
|
||||||
void TextColored(const ImVec4& col, const char* fmt, ...); // shortcut to doing PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();
|
void TextColored(const ImVec4& col, const char* fmt, ...); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();
|
||||||
void TextColoredV(const ImVec4& col, const char* fmt, va_list args);
|
void TextColoredV(const ImVec4& col, const char* fmt, va_list args);
|
||||||
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, better for long chunks of text.
|
void TextWrapped(const char* fmt, ...); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();
|
||||||
void LabelText(const char* label, const char* fmt, ...);
|
void TextWrappedV(const char* fmt, va_list args);
|
||||||
|
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.
|
||||||
|
void LabelText(const char* label, const char* fmt, ...); // display text+label aligned the same way as value+label widgets
|
||||||
void LabelTextV(const char* label, const char* fmt, va_list args);
|
void LabelTextV(const char* label, const char* fmt, va_list args);
|
||||||
void BulletText(const char* fmt, ...);
|
void BulletText(const char* fmt, ...);
|
||||||
void BulletTextV(const char* fmt, va_list args);
|
void BulletTextV(const char* fmt, va_list args);
|
||||||
@ -265,9 +270,10 @@ namespace ImGui
|
|||||||
// Utilities
|
// Utilities
|
||||||
void SetNewWindowDefaultPos(const ImVec2& pos); // set position of window that do
|
void SetNewWindowDefaultPos(const ImVec2& pos); // set position of window that do
|
||||||
bool IsHovered(); // was the last item active area hovered by mouse?
|
bool IsHovered(); // was the last item active area hovered by mouse?
|
||||||
|
bool IsItemFocused(); // was the last item focused for keyboard input?
|
||||||
ImVec2 GetItemBoxMin(); // get bounding box of last item
|
ImVec2 GetItemBoxMin(); // get bounding box of last item
|
||||||
ImVec2 GetItemBoxMax(); // get bounding box of last item
|
ImVec2 GetItemBoxMax(); // get bounding box of last item
|
||||||
bool IsClipped(const ImVec2& item_size); // to perform coarse clipping on user's side (as an optimisation)
|
bool IsClipped(const ImVec2& item_size); // to perform coarse clipping on user's side (as an optimization)
|
||||||
bool IsKeyPressed(int key_index, bool repeat = true); // key_index into the keys_down[512] array, imgui doesn't know the semantic of each entry
|
bool IsKeyPressed(int key_index, bool repeat = true); // key_index into the keys_down[512] array, imgui doesn't know the semantic of each entry
|
||||||
bool IsMouseClicked(int button, bool repeat = false);
|
bool IsMouseClicked(int button, bool repeat = false);
|
||||||
bool IsMouseDoubleClicked(int button);
|
bool IsMouseDoubleClicked(int button);
|
||||||
@ -280,7 +286,7 @@ namespace ImGui
|
|||||||
int GetFrameCount();
|
int GetFrameCount();
|
||||||
const char* GetStyleColorName(ImGuiCol idx);
|
const char* GetStyleColorName(ImGuiCol idx);
|
||||||
void GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const void** png_data, unsigned int* png_size);
|
void GetDefaultFontData(const void** fnt_data, unsigned int* fnt_size, const void** png_data, unsigned int* png_size);
|
||||||
ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, const bool hide_text_after_hash = true);
|
ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_hash = true, float wrap_width = -1.0f);
|
||||||
|
|
||||||
} // namespace ImGui
|
} // namespace ImGui
|
||||||
|
|
||||||
@ -542,6 +548,7 @@ struct ImGuiTextBuffer
|
|||||||
ImVector<char> Buf;
|
ImVector<char> Buf;
|
||||||
|
|
||||||
ImGuiTextBuffer() { Buf.push_back(0); }
|
ImGuiTextBuffer() { Buf.push_back(0); }
|
||||||
|
~ImGuiTextBuffer() { clear(); }
|
||||||
const char* begin() const { return Buf.begin(); }
|
const char* begin() const { return Buf.begin(); }
|
||||||
const char* end() const { return Buf.end()-1; }
|
const char* end() const { return Buf.end()-1; }
|
||||||
size_t size() const { return Buf.size()-1; }
|
size_t size() const { return Buf.size()-1; }
|
||||||
@ -620,7 +627,7 @@ struct ImDrawList
|
|||||||
void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12);
|
void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12);
|
||||||
void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12);
|
void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12);
|
||||||
void AddArc(const ImVec2& center, float rad, ImU32 col, int a_min, int a_max, bool tris = false, const ImVec2& third_point_offset = ImVec2(0,0));
|
void AddArc(const ImVec2& center, float rad, ImU32 col, int a_min, int a_max, bool tris = false, const ImVec2& third_point_offset = ImVec2(0,0));
|
||||||
void AddText(ImFont font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end);
|
void AddText(ImFont font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width = 0.0f);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Optional bitmap font data loader & renderer into vertices
|
// Optional bitmap font data loader & renderer into vertices
|
||||||
@ -696,11 +703,16 @@ struct ImBitmapFont
|
|||||||
bool LoadFromFile(const char* filename);
|
bool LoadFromFile(const char* filename);
|
||||||
void Clear();
|
void Clear();
|
||||||
void BuildLookupTable();
|
void BuildLookupTable();
|
||||||
const FntGlyph * FindGlyph(unsigned short c) const;
|
const FntGlyph * FindGlyph(unsigned short c, const FntGlyph* fallback = NULL) const;
|
||||||
float GetFontSize() const { return (float)Info->FontSize; }
|
float GetFontSize() const { return (float)Info->FontSize; }
|
||||||
bool IsLoaded() const { return Info != NULL && Common != NULL && Glyphs != NULL; }
|
bool IsLoaded() const { return Info != NULL && Common != NULL && Glyphs != NULL; }
|
||||||
|
|
||||||
ImVec2 CalcTextSizeA(float size, float max_width, const char* text_begin, const char* text_end, const char** remaining = NULL) const; // utf8
|
// 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
|
||||||
|
// 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
|
||||||
|
ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining = NULL) const; // utf8
|
||||||
ImVec2 CalcTextSizeW(float size, float max_width, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL) const; // wchar
|
ImVec2 CalcTextSizeW(float size, float max_width, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL) const; // wchar
|
||||||
void RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, ImDrawVert*& out_vertices) const;
|
void RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, ImDrawVert*& out_vertices, float wrap_width = 0.0f) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width, const FntGlyph* fallback_glyph) const;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user