Skip to content

Commit d284a6c

Browse files
committedFeb 11, 2020
InputText: Fixed password fields displaying ASCII spaces as blanks. Fixed non-ASCII space occasionally creating unnecessary empty polygons. (ocornut#2149, ocornut#515)
1 parent ccaec1a commit d284a6c

File tree

4 files changed

+89
-73
lines changed

4 files changed

+89
-73
lines changed
 

‎docs/CHANGELOG.txt

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Other Changes:
3838

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

‎imgui.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -2068,9 +2068,12 @@ struct ImFontConfig
20682068
IMGUI_API ImFontConfig();
20692069
};
20702070

2071+
// Hold rendering data for one glyph.
2072+
// (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)
20712073
struct ImFontGlyph
20722074
{
2073-
ImWchar Codepoint; // 0x0000..0xFFFF
2075+
unsigned int Codepoint : 31; // 0x0000..0xFFFF
2076+
unsigned int Visible : 1; // Flag to allow early out when rendering
20742077
float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in)
20752078
float X0, Y0, X1, Y1; // Glyph corners
20762079
float U0, V0, U1, V1; // Texture coordinates
@@ -2265,6 +2268,7 @@ struct ImFont
22652268
IMGUI_API void GrowIndex(int new_size);
22662269
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);
22672270
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.
2271+
IMGUI_API void SetGlyphVisible(ImWchar c, bool visible);
22682272
IMGUI_API void SetFallbackChar(ImWchar c);
22692273
};
22702274

‎imgui_demo.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -1040,11 +1040,11 @@ static void ShowDemoWindowWidgets()
10401040
static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters);
10411041

10421042
ImGui::Text("Password input");
1043-
static char bufpass[64] = "password123";
1044-
ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank);
1043+
static char password[64] = "password123";
1044+
ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
10451045
ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
1046-
ImGui::InputTextWithHint("password (w/ hint)", "<password>", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank);
1047-
ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank);
1046+
ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank);
1047+
ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_CharsNoBlank);
10481048
ImGui::TreePop();
10491049
}
10501050

@@ -3465,6 +3465,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
34653465
ImGui::BeginTooltip();
34663466
ImGui::Text("Codepoint: U+%04X", base + n);
34673467
ImGui::Separator();
3468+
ImGui::Text("Visible: %d", glyph->Visible);
34683469
ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX);
34693470
ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
34703471
ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);

‎imgui_draw.cpp

+76-68
Original file line numberDiff line numberDiff line change
@@ -2622,6 +2622,7 @@ void ImFont::BuildLookupTable()
26222622
for (int i = 0; i != Glyphs.Size; i++)
26232623
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
26242624

2625+
// Build lookup table
26252626
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
26262627
IndexAdvanceX.clear();
26272628
IndexLookup.clear();
@@ -2638,7 +2639,7 @@ void ImFont::BuildLookupTable()
26382639
// FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
26392640
if (FindGlyph((ImWchar)' '))
26402641
{
2641-
if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times
2642+
if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times (FIXME: Flaky)
26422643
Glyphs.resize(Glyphs.Size + 1);
26432644
ImFontGlyph& tab_glyph = Glyphs.back();
26442645
tab_glyph = *FindGlyph((ImWchar)' ');
@@ -2648,13 +2649,24 @@ void ImFont::BuildLookupTable()
26482649
IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1);
26492650
}
26502651

2652+
// Mark special glyphs as not visible (note that AddGlyph already mark as non-visible glyphs with zero-size polygons)
2653+
SetGlyphVisible((ImWchar)' ', false);
2654+
SetGlyphVisible((ImWchar)'\t', false);
2655+
2656+
// Setup fall-backs
26512657
FallbackGlyph = FindGlyphNoFallback(FallbackChar);
26522658
FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f;
26532659
for (int i = 0; i < max_codepoint + 1; i++)
26542660
if (IndexAdvanceX[i] < 0.0f)
26552661
IndexAdvanceX[i] = FallbackAdvanceX;
26562662
}
26572663

2664+
void ImFont::SetGlyphVisible(ImWchar c, bool visible)
2665+
{
2666+
if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c))
2667+
glyph->Visible = visible ? 1 : 0;
2668+
}
2669+
26582670
void ImFont::SetFallbackChar(ImWchar c)
26592671
{
26602672
FallbackChar = c;
@@ -2676,7 +2688,8 @@ void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1,
26762688
{
26772689
Glyphs.resize(Glyphs.Size + 1);
26782690
ImFontGlyph& glyph = Glyphs.back();
2679-
glyph.Codepoint = (ImWchar)codepoint;
2691+
glyph.Codepoint = (unsigned int)codepoint;
2692+
glyph.Visible = (x0 != x1) && (y0 != y1);
26802693
glyph.X0 = x0;
26812694
glyph.Y0 = y0;
26822695
glyph.X1 = x1;
@@ -2925,16 +2938,14 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
29252938

29262939
void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const
29272940
{
2928-
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded.
2941+
const ImFontGlyph* glyph = FindGlyph(c);
2942+
if (!glyph || !glyph->Visible)
29292943
return;
2930-
if (const ImFontGlyph* glyph = FindGlyph(c))
2931-
{
2932-
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
2933-
pos.x = IM_FLOOR(pos.x + DisplayOffset.x);
2934-
pos.y = IM_FLOOR(pos.y + DisplayOffset.y);
2935-
draw_list->PrimReserve(6, 4);
2936-
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);
2937-
}
2944+
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
2945+
pos.x = IM_FLOOR(pos.x + DisplayOffset.x);
2946+
pos.y = IM_FLOOR(pos.y + DisplayOffset.y);
2947+
draw_list->PrimReserve(6, 4);
2948+
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);
29382949
}
29392950

29402951
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
30473058
continue;
30483059
}
30493060

3050-
float char_width = 0.0f;
3051-
if (const ImFontGlyph* glyph = FindGlyph((ImWchar)c))
3052-
{
3053-
char_width = glyph->AdvanceX * scale;
3061+
const ImFontGlyph* glyph = FindGlyph((ImWchar)c);
3062+
if (glyph == NULL)
3063+
continue;
30543064

3055-
// Arbitrarily assume that both space and tabs are empty glyphs as an optimization
3056-
if (c != ' ' && c != '\t')
3065+
float char_width = glyph->AdvanceX * scale;
3066+
if (glyph->Visible)
3067+
{
3068+
// 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
3069+
float x1 = x + glyph->X0 * scale;
3070+
float x2 = x + glyph->X1 * scale;
3071+
float y1 = y + glyph->Y0 * scale;
3072+
float y2 = y + glyph->Y1 * scale;
3073+
if (x1 <= clip_rect.z && x2 >= clip_rect.x)
30573074
{
3058-
// 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
3059-
float x1 = x + glyph->X0 * scale;
3060-
float x2 = x + glyph->X1 * scale;
3061-
float y1 = y + glyph->Y0 * scale;
3062-
float y2 = y + glyph->Y1 * scale;
3063-
if (x1 <= clip_rect.z && x2 >= clip_rect.x)
3075+
// Render a character
3076+
float u1 = glyph->U0;
3077+
float v1 = glyph->V0;
3078+
float u2 = glyph->U1;
3079+
float v2 = glyph->V1;
3080+
3081+
// CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
3082+
if (cpu_fine_clip)
30643083
{
3065-
// Render a character
3066-
float u1 = glyph->U0;
3067-
float v1 = glyph->V0;
3068-
float u2 = glyph->U1;
3069-
float v2 = glyph->V1;
3070-
3071-
// CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
3072-
if (cpu_fine_clip)
3084+
if (x1 < clip_rect.x)
30733085
{
3074-
if (x1 < clip_rect.x)
3075-
{
3076-
u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);
3077-
x1 = clip_rect.x;
3078-
}
3079-
if (y1 < clip_rect.y)
3080-
{
3081-
v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);
3082-
y1 = clip_rect.y;
3083-
}
3084-
if (x2 > clip_rect.z)
3085-
{
3086-
u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);
3087-
x2 = clip_rect.z;
3088-
}
3089-
if (y2 > clip_rect.w)
3090-
{
3091-
v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);
3092-
y2 = clip_rect.w;
3093-
}
3094-
if (y1 >= y2)
3095-
{
3096-
x += char_width;
3097-
continue;
3098-
}
3086+
u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);
3087+
x1 = clip_rect.x;
30993088
}
3100-
3101-
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
3089+
if (y1 < clip_rect.y)
3090+
{
3091+
v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);
3092+
y1 = clip_rect.y;
3093+
}
3094+
if (x2 > clip_rect.z)
31023095
{
3103-
idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2);
3104-
idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3);
3105-
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;
3106-
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;
3107-
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;
3108-
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;
3109-
vtx_write += 4;
3110-
vtx_current_idx += 4;
3111-
idx_write += 6;
3096+
u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);
3097+
x2 = clip_rect.z;
3098+
}
3099+
if (y2 > clip_rect.w)
3100+
{
3101+
v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);
3102+
y2 = clip_rect.w;
3103+
}
3104+
if (y1 >= y2)
3105+
{
3106+
x += char_width;
3107+
continue;
31123108
}
31133109
}
3110+
3111+
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
3112+
{
3113+
idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2);
3114+
idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3);
3115+
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;
3116+
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;
3117+
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;
3118+
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;
3119+
vtx_write += 4;
3120+
vtx_current_idx += 4;
3121+
idx_write += 6;
3122+
}
31143123
}
31153124
}
3116-
31173125
x += char_width;
31183126
}
31193127

0 commit comments

Comments
 (0)
Please sign in to comment.