InputText: Fixed password fields displaying ASCII spaces as blanks. Fixed non-ASCII space occasionally creating unnecessary empty polygons. (#2149, #515)

This commit is contained in:
omar 2020-02-11 18:53:57 +01:00
parent ccaec1a270
commit d284a6cffc
4 changed files with 89 additions and 73 deletions

View File

@ -38,6 +38,9 @@ Other Changes:
- ColorButton: Added ImGuiColorEditFlags_NoBorder flag to remove the border normally enforced - ColorButton: Added ImGuiColorEditFlags_NoBorder flag to remove the border normally enforced
by default for standalone ColorButton. by default for standalone ColorButton.
- InputText: Fixed password fields displaying ASCII spaces as blanks instead of using the '*'
glyph. (#2149, #515)
- Font: Fixed non-ASCII space occasionally creating unnecessary empty polygons.
- Demo: Added a black and white gradient to Demo>Examples>Custom Rendering. - Demo: Added a black and white gradient to Demo>Examples>Custom Rendering.
- Backends: SDL: Added ImGui_ImplSDL2_InitForMetal() for API consistency (even though the function - Backends: SDL: Added ImGui_ImplSDL2_InitForMetal() for API consistency (even though the function
currently does nothing). currently does nothing).

View File

@ -2068,9 +2068,12 @@ struct ImFontConfig
IMGUI_API ImFontConfig(); IMGUI_API ImFontConfig();
}; };
// Hold rendering data for one glyph.
// (Note: some language parsers may fail to convert the 31+1 bitfield members, in this case maybe drop store a single u32 or we can rework this)
struct ImFontGlyph struct ImFontGlyph
{ {
ImWchar Codepoint; // 0x0000..0xFFFF unsigned int Codepoint : 31; // 0x0000..0xFFFF
unsigned int Visible : 1; // Flag to allow early out when rendering
float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in) float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in)
float X0, Y0, X1, Y1; // Glyph corners float X0, Y0, X1, Y1; // Glyph corners
float U0, V0, U1, V1; // Texture coordinates float U0, V0, U1, V1; // Texture coordinates
@ -2265,6 +2268,7 @@ struct ImFont
IMGUI_API void GrowIndex(int new_size); IMGUI_API void GrowIndex(int new_size);
IMGUI_API void AddGlyph(ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddGlyph(ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x);
IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built.
IMGUI_API void SetGlyphVisible(ImWchar c, bool visible);
IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API void SetFallbackChar(ImWchar c);
}; };

View File

@ -1040,11 +1040,11 @@ static void ShowDemoWindowWidgets()
static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters);
ImGui::Text("Password input"); ImGui::Text("Password input");
static char bufpass[64] = "password123"; static char password[64] = "password123";
ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
ImGui::InputTextWithHint("password (w/ hint)", "<password>", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank);
ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_CharsNoBlank);
ImGui::TreePop(); ImGui::TreePop();
} }
@ -3465,6 +3465,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::Text("Codepoint: U+%04X", base + n); ImGui::Text("Codepoint: U+%04X", base + n);
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Visible: %d", glyph->Visible);
ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX);
ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);

View File

@ -2622,6 +2622,7 @@ void ImFont::BuildLookupTable()
for (int i = 0; i != Glyphs.Size; i++) for (int i = 0; i != Glyphs.Size; i++)
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
// Build lookup table
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
IndexAdvanceX.clear(); IndexAdvanceX.clear();
IndexLookup.clear(); IndexLookup.clear();
@ -2638,7 +2639,7 @@ void ImFont::BuildLookupTable()
// FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
if (FindGlyph((ImWchar)' ')) if (FindGlyph((ImWchar)' '))
{ {
if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times (FIXME: Flaky)
Glyphs.resize(Glyphs.Size + 1); Glyphs.resize(Glyphs.Size + 1);
ImFontGlyph& tab_glyph = Glyphs.back(); ImFontGlyph& tab_glyph = Glyphs.back();
tab_glyph = *FindGlyph((ImWchar)' '); tab_glyph = *FindGlyph((ImWchar)' ');
@ -2648,6 +2649,11 @@ void ImFont::BuildLookupTable()
IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1); IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1);
} }
// Mark special glyphs as not visible (note that AddGlyph already mark as non-visible glyphs with zero-size polygons)
SetGlyphVisible((ImWchar)' ', false);
SetGlyphVisible((ImWchar)'\t', false);
// Setup fall-backs
FallbackGlyph = FindGlyphNoFallback(FallbackChar); FallbackGlyph = FindGlyphNoFallback(FallbackChar);
FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f;
for (int i = 0; i < max_codepoint + 1; i++) for (int i = 0; i < max_codepoint + 1; i++)
@ -2655,6 +2661,12 @@ void ImFont::BuildLookupTable()
IndexAdvanceX[i] = FallbackAdvanceX; IndexAdvanceX[i] = FallbackAdvanceX;
} }
void ImFont::SetGlyphVisible(ImWchar c, bool visible)
{
if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c))
glyph->Visible = visible ? 1 : 0;
}
void ImFont::SetFallbackChar(ImWchar c) void ImFont::SetFallbackChar(ImWchar c)
{ {
FallbackChar = c; FallbackChar = c;
@ -2676,7 +2688,8 @@ void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1,
{ {
Glyphs.resize(Glyphs.Size + 1); Glyphs.resize(Glyphs.Size + 1);
ImFontGlyph& glyph = Glyphs.back(); ImFontGlyph& glyph = Glyphs.back();
glyph.Codepoint = (ImWchar)codepoint; glyph.Codepoint = (unsigned int)codepoint;
glyph.Visible = (x0 != x1) && (y0 != y1);
glyph.X0 = x0; glyph.X0 = x0;
glyph.Y0 = y0; glyph.Y0 = y0;
glyph.X1 = x1; glyph.X1 = x1;
@ -2925,16 +2938,14 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const
{ {
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. const ImFontGlyph* glyph = FindGlyph(c);
if (!glyph || !glyph->Visible)
return; return;
if (const ImFontGlyph* glyph = FindGlyph(c)) float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
{ pos.x = IM_FLOOR(pos.x + DisplayOffset.x);
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; pos.y = IM_FLOOR(pos.y + DisplayOffset.y);
pos.x = IM_FLOOR(pos.x + DisplayOffset.x); draw_list->PrimReserve(6, 4);
pos.y = IM_FLOOR(pos.y + DisplayOffset.y); draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
draw_list->PrimReserve(6, 4);
draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
}
} }
void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
@ -3047,73 +3058,70 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
continue; continue;
} }
float char_width = 0.0f; const ImFontGlyph* glyph = FindGlyph((ImWchar)c);
if (const ImFontGlyph* glyph = FindGlyph((ImWchar)c)) if (glyph == NULL)
continue;
float char_width = glyph->AdvanceX * scale;
if (glyph->Visible)
{ {
char_width = glyph->AdvanceX * scale; // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w
float x1 = x + glyph->X0 * scale;
// Arbitrarily assume that both space and tabs are empty glyphs as an optimization float x2 = x + glyph->X1 * scale;
if (c != ' ' && c != '\t') float y1 = y + glyph->Y0 * scale;
float y2 = y + glyph->Y1 * scale;
if (x1 <= clip_rect.z && x2 >= clip_rect.x)
{ {
// We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w // Render a character
float x1 = x + glyph->X0 * scale; float u1 = glyph->U0;
float x2 = x + glyph->X1 * scale; float v1 = glyph->V0;
float y1 = y + glyph->Y0 * scale; float u2 = glyph->U1;
float y2 = y + glyph->Y1 * scale; float v2 = glyph->V1;
if (x1 <= clip_rect.z && x2 >= clip_rect.x)
// CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
if (cpu_fine_clip)
{ {
// Render a character if (x1 < clip_rect.x)
float u1 = glyph->U0;
float v1 = glyph->V0;
float u2 = glyph->U1;
float v2 = glyph->V1;
// CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
if (cpu_fine_clip)
{ {
if (x1 < clip_rect.x) u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);
{ x1 = clip_rect.x;
u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);
x1 = clip_rect.x;
}
if (y1 < clip_rect.y)
{
v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);
y1 = clip_rect.y;
}
if (x2 > clip_rect.z)
{
u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);
x2 = clip_rect.z;
}
if (y2 > clip_rect.w)
{
v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);
y2 = clip_rect.w;
}
if (y1 >= y2)
{
x += char_width;
continue;
}
} }
if (y1 < clip_rect.y)
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
{ {
idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2); v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);
idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3); y1 = clip_rect.y;
vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
vtx_write += 4;
vtx_current_idx += 4;
idx_write += 6;
} }
if (x2 > clip_rect.z)
{
u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);
x2 = clip_rect.z;
}
if (y2 > clip_rect.w)
{
v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);
y2 = clip_rect.w;
}
if (y1 >= y2)
{
x += char_width;
continue;
}
}
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
{
idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2);
idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3);
vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
vtx_write += 4;
vtx_current_idx += 4;
idx_write += 6;
} }
} }
} }
x += char_width; x += char_width;
} }