Update stk edit box to be more i18n friendly

This commit is contained in:
Benau 2019-06-10 00:43:31 +08:00
parent acb9054dcb
commit 89e3bcd11b
11 changed files with 660 additions and 936 deletions

View File

@ -294,6 +294,7 @@ when the border that intersect at this corner are enabled.
<color type="text_field" state="background" a="255" r="35" g="35" b="35" /> <color type="text_field" state="background" a="255" r="35" g="35" b="35" />
<color type="text_field" state="background_focused" a="255" r="15" g="15" b="15" /> <color type="text_field" state="background_focused" a="255" r="15" g="15" b="15" />
<color type="text_field" state="background_deactivated" a="255" r="90" g="90" b="90" /> <color type="text_field" state="background_deactivated" a="255" r="90" g="90" b="90" />
<color type="text_field" state="background_marked" a="128" r="220" g="220" b="220" />
<color type="text_field" state="neutral" a="255" r="35" g="35" b="35" /> <color type="text_field" state="neutral" a="255" r="35" g="35" b="35" />
<color type="text_field" state="focused" a="255" r="15" g="15" b="15" /> <color type="text_field" state="focused" a="255" r="15" g="15" b="15" />
<color type="text_field" state="deactivated" a="255" r="90" g="90" b="90" /> <color type="text_field" state="deactivated" a="255" r="90" g="90" b="90" />

View File

@ -294,6 +294,7 @@ when the border that intersect at this corner are enabled.
<color type="text_field" state="background" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_focused" a="255" r="223" g="250" b="245" /> <color type="text_field" state="background_focused" a="255" r="223" g="250" b="245" />
<color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_marked" a="128" r="30" g="80" b="40" />
<color type="text_field" state="neutral" a="255" r="138" g="138" b="138" /> <color type="text_field" state="neutral" a="255" r="138" g="138" b="138" />
<color type="text_field" state="focused" a="255" r="80" g="240" b="80" /> <color type="text_field" state="focused" a="255" r="80" g="240" b="80" />
<color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" /> <color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" />

View File

@ -293,6 +293,7 @@ when the border that intersect at this corner are enabled.
<color type="text_field" state="background" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_focused" a="255" r="223" g="238" b="248" /> <color type="text_field" state="background_focused" a="255" r="223" g="238" b="248" />
<color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_marked" a="128" r="40" g="80" b="100" />
<color type="text_field" state="neutral" a="255" r="138" g="138" b="138" /> <color type="text_field" state="neutral" a="255" r="138" g="138" b="138" />
<color type="text_field" state="focused" a="255" r="42" g="169" b="211" /> <color type="text_field" state="focused" a="255" r="42" g="169" b="211" />
<color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" /> <color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" />

View File

@ -291,6 +291,7 @@ when the border that intersect at this corner are enabled.
<color type="text_field" state="background" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_focused" a="255" r="236" g="226" b="201" /> <color type="text_field" state="background_focused" a="255" r="236" g="226" b="201" />
<color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_marked" a="128" r="90" g="55" b="25" />
<color type="text_field" state="neutral" a="255" r="138" g="138" b="138" /> <color type="text_field" state="neutral" a="255" r="138" g="138" b="138" />
<color type="text_field" state="focused" a="255" r="243" g="164" b="80" /> <color type="text_field" state="focused" a="255" r="243" g="164" b="80" />
<color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" /> <color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" />

View File

@ -292,6 +292,7 @@ when the border that intersect at this corner are enabled.
<color type="text_field" state="background" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_focused" a="255" r="245" g="220" b="235" /> <color type="text_field" state="background_focused" a="255" r="245" g="220" b="235" />
<color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" /> <color type="text_field" state="background_deactivated" a="255" r="200" g="200" b="200" />
<color type="text_field" state="background_marked" a="128" r="80" g="40" b="40" />
<color type="text_field" state="neutral" a="255" r="138" g="138" b="138" /> <color type="text_field" state="neutral" a="255" r="138" g="138" b="138" />
<color type="text_field" state="focused" a="255" r="240" g="80" b="110" /> <color type="text_field" state="focused" a="255" r="240" g="80" b="110" />
<color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" /> <color type="text_field" state="deactivated" a="255" r="138" g="138" b="138" />

View File

@ -26,6 +26,13 @@ GLF_QUICK_DRAW = 8, /* This glyph is not created by libraqm, which get x_advance
GLF_NEWLINE = 16 /* This glyph will start a newline. */ GLF_NEWLINE = 16 /* This glyph will start a newline. */
}; };
enum GlyphLayoutDraw
{
GLD_NONE = 0, /* Default flag. */
GLD_MARKED = 1, /* This glyph will be drawn with background marked for marked text. */
GLD_COMPOSING = 2 /* This glyph will be drawn with underline (for example composing text). */
};
//! GlyphLayout copied from libraqm. //! GlyphLayout copied from libraqm.
struct GlyphLayout struct GlyphLayout
{ {
@ -37,6 +44,7 @@ s32 y_offset;
/* Above variable is same for raqm_glyph_t */ /* Above variable is same for raqm_glyph_t */
// If some characters share the same glyph // If some characters share the same glyph
std::vector<s32> cluster; std::vector<s32> cluster;
std::vector<u8> draw_flags;
//! used to sorting back the visual order after line breaking //! used to sorting back the visual order after line breaking
u32 original_index; u32 original_index;
u16 flags; u16 flags;

View File

@ -437,6 +437,8 @@ void FontManager::shape(const std::u32string& text,
extra_cluster <= next_cluster - 1; extra_cluster++) extra_cluster <= next_cluster - 1; extra_cluster++)
cur_line[l].cluster.push_back(extra_cluster); cur_line[l].cluster.push_back(extra_cluster);
} }
cur_line[l].draw_flags.resize(cur_line[l].cluster.size(),
gui::GLD_NONE);
} }
// Sort glyphs in visual order // Sort glyphs in visual order
std::sort(cur_line.begin(), cur_line.end(), [] std::sort(cur_line.begin(), cur_line.end(), []

View File

@ -574,6 +574,8 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
core::array<s32> indices(text_size); core::array<s32> indices(text_size);
core::array<core::position2d<float>> offsets(text_size); core::array<core::position2d<float>> offsets(text_size);
std::vector<bool> fallback(text_size); std::vector<bool> fallback(text_size);
core::array<core::position2d<float>> gld_offsets;
gui::GlyphLayoutDraw df_used = gui::GLD_NONE;
// Check if the line is RTL // Check if the line is RTL
bool rtl = (gl[0].flags & gui::GLF_RTL_LINE) != 0; bool rtl = (gl[0].flags & gui::GLF_RTL_LINE) != 0;
@ -596,7 +598,7 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
if (line_changed) if (line_changed)
{ {
line_changed = false; line_changed = false;
rtl = (gl[i].flags & gui::GLF_RTL_LINE) != 0; rtl = (glyph_layout.flags & gui::GLF_RTL_LINE) != 0;
offset.X = float(position.UpperLeftCorner.X); offset.X = float(position.UpperLeftCorner.X);
if (hcenter) if (hcenter)
{ {
@ -680,8 +682,32 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
} }
else else
{ {
offset.X += int width = (int)(glyph_layout.x_advance * m_inverse_shaping);
(int)(glyph_layout.x_advance * m_inverse_shaping) * scale; if (char_collector == NULL)
{
float each_size = width * scale /
(float)glyph_layout.cluster.size();
float start = offset.X;
for (unsigned df = 0; df < glyph_layout.draw_flags.size();
df++)
{
if (glyph_layout.draw_flags[df] != gui::GLD_NONE)
{
if (df_used == gui::GLD_NONE)
{
if (glyph_layout.draw_flags[df] & gui::GLD_MARKED)
df_used = gui::GLD_MARKED;
else if (glyph_layout.draw_flags[df] &
gui::GLD_COMPOSING)
df_used = gui::GLD_COMPOSING;
}
gld_offsets.push_back({start, offset.Y});
gld_offsets.push_back({start + each_size, offset.Y});
}
start += each_size;
}
}
offset.X += width * scale;
} }
} // for i < text_size } // for i < text_size
@ -825,6 +851,27 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
} }
} }
} }
for (unsigned i = 0; i < gld_offsets.size(); i += 2)
{
if (df_used == gui::GLD_MARKED)
{
core::rect<s32> gld((s32)gld_offsets[i].X, (s32)gld_offsets[i].Y,
(s32)gld_offsets[i + 1].X,
(s32)(gld_offsets[i + 1].Y + m_font_max_height * scale));
GL32_draw2DRectangle(GUIEngine::getSkin()->getColor(
"text_field::background_marked"), gld, clip);
}
else if (df_used == gui::GLD_COMPOSING)
{
float line1 = m_font_max_height * scale * 0.88f;
float line2 = m_font_max_height * scale * 0.92f;
core::rect<s32> gld((s32)gld_offsets[i].X, (s32)gld_offsets[i].Y + line1,
(s32)gld_offsets[i + 1].X,
(s32)(gld_offsets[i + 1].Y + line2));
GL32_draw2DRectangle(GUIEngine::getSkin()->getColor(
"text::neutral"), gld, clip);
}
}
#endif #endif
} // render } // render

File diff suppressed because it is too large Load Diff

View File

@ -11,10 +11,14 @@
#include "IOSOperator.h" #include "IOSOperator.h"
#include "utils/leak_check.hpp" #include "utils/leak_check.hpp"
#include "utils/time.hpp" #include "GlyphLayout.h"
#include <string>
#include <vector>
using namespace irr; using namespace irr;
using namespace gui; using namespace gui;
namespace GUIEngine namespace GUIEngine
{ {
enum TextBoxType: int; enum TextBoxType: int;
@ -28,7 +32,7 @@ namespace GUIEngine
//! constructor //! constructor
CGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment, CGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
IGUIElement* parent, s32 id, const core::rect<s32>& rectangle, bool is_rtl); IGUIElement* parent, s32 id, const core::rect<s32>& rectangle);
//! destructor //! destructor
virtual ~CGUIEditBox(); virtual ~CGUIEditBox();
@ -54,20 +58,20 @@ namespace GUIEngine
virtual void setDrawBorder(bool border); virtual void setDrawBorder(bool border);
//! Enables or disables word wrap for using the edit box as multiline text editor. //! Enables or disables word wrap for using the edit box as multiline text editor.
virtual void setWordWrap(bool enable); virtual void setWordWrap(bool enable) {}
//! Checks if word wrap is enabled //! Checks if word wrap is enabled
//! \return true if word wrap is enabled, false otherwise //! \return true if word wrap is enabled, false otherwise
virtual bool isWordWrapEnabled() const; virtual bool isWordWrapEnabled() const { return false; }
//! Enables or disables newlines. //! Enables or disables newlines.
/** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired, /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
instead a newline character will be inserted. */ instead a newline character will be inserted. */
virtual void setMultiLine(bool enable); virtual void setMultiLine(bool enable) {}
//! Checks if multi line editing is enabled //! Checks if multi line editing is enabled
//! \return true if mult-line is enabled, false otherwise //! \return true if mult-line is enabled, false otherwise
virtual bool isMultiLineEnabled() const; virtual bool isMultiLineEnabled() const { return false; }
//! Enables or disables automatic scrolling with cursor position //! Enables or disables automatic scrolling with cursor position
//! \param enable: If set to true, the text will move around with the cursor position //! \param enable: If set to true, the text will move around with the cursor position
@ -123,19 +127,15 @@ namespace GUIEngine
virtual irr::gui::IGUIFont* getActiveFont() const { return NULL; } virtual irr::gui::IGUIFont* getActiveFont() const { return NULL; }
virtual void setDrawBackground(bool) { } virtual void setDrawBackground(bool) { }
void fromAndroidEditText(const core::stringw& text, int start, int end, void fromAndroidEditText(const std::u32string& text, int start, int end,
int composing_start, int composing_end); int composing_start, int composing_end);
void openScreenKeyboard(); void openScreenKeyboard();
s32 getCursorPosInBox() const { return CursorPos; } s32 getCursorPosInBox() const { return m_cursor_pos; }
s32 getTextCount() const { return (s32)Text.size(); } s32 getTextCount() const { return (s32)m_edit_text.size(); }
void setTextBoxType(GUIEngine::TextBoxType t) { m_type = t; } void setTextBoxType(GUIEngine::TextBoxType t) { m_type = t; }
protected: protected:
//! Breaks the single text line.
void breakText();
//! sets the area of the given line //! sets the area of the given line
void setTextRect(s32 line); void setTextRect(s32 line);
//! returns the line number that the cursor is on
s32 getLineFromPos(s32 pos);
//! adds a letter to the edit box //! adds a letter to the edit box
void inputChar(wchar_t c); void inputChar(wchar_t c);
//! calculates the current scroll position //! calculates the current scroll position
@ -144,7 +144,7 @@ namespace GUIEngine
void sendGuiEvent(EGUI_EVENT_TYPE type); void sendGuiEvent(EGUI_EVENT_TYPE type);
//! set text markers //! set text markers
void setTextMarkers(s32 begin, s32 end); void setTextMarkers(s32 begin, s32 end);
void updateCursorDistance();
bool processKey(const SEvent& event); bool processKey(const SEvent& event);
bool processMouse(const SEvent& event); bool processMouse(const SEvent& event);
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) #if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
@ -155,12 +155,13 @@ namespace GUIEngine
core::position2di calculateICPos(); core::position2di calculateICPos();
#endif #endif
s32 getCursorPos(s32 x, s32 y); s32 getCursorPos(s32 x, s32 y);
void updateGlyphLayouts();
bool MouseMarking; bool MouseMarking;
bool Border; bool Border;
bool OverrideColorEnabled; bool OverrideColorEnabled;
s32 MarkBegin; s32 m_mark_begin;
s32 MarkEnd; s32 m_mark_end;
GUIEngine::TextBoxType m_type; GUIEngine::TextBoxType m_type;
@ -168,20 +169,16 @@ namespace GUIEngine
gui::IGUIFont *OverrideFont, *LastBreakFont; gui::IGUIFont *OverrideFont, *LastBreakFont;
IOSOperator* Operator; IOSOperator* Operator;
StkTime::TimeType BlinkStartTime; uint64_t m_force_show_cursor_time;
s32 CursorPos; s32 m_cursor_pos;
s32 HScrollPos, VScrollPos; // scroll position in characters s32 m_scroll_pos;
u32 Max; s32 m_cursor_distance;
u32 m_max_chars;
bool m_rtl; bool AutoScroll, PasswordBox;
char32_t PasswordChar;
bool WordWrap, MultiLine, AutoScroll, PasswordBox;
wchar_t PasswordChar;
EGUI_ALIGNMENT HAlign, VAlign; EGUI_ALIGNMENT HAlign, VAlign;
core::array< core::stringw > BrokenText;
core::array< s32 > BrokenTextPositions;
core::rect<s32> CurrentTextRect, FrameRect; // temporary values core::rect<s32> CurrentTextRect, FrameRect; // temporary values
s32 m_composing_start; s32 m_composing_start;
@ -190,6 +187,12 @@ namespace GUIEngine
/* If true, this editbox will copy text and selection only from /* If true, this editbox will copy text and selection only from
* android edittext, and process only mouse event. */ * android edittext, and process only mouse event. */
bool m_from_android_edittext; bool m_from_android_edittext;
/* UTF32 string for shaping and editing to avoid wchar_t issue in
* windows */
std::u32string m_edit_text;
std::vector<GlyphLayout> m_glyph_layouts;
void correctCursor(s32& cursor_pos, bool left);
}; };

View File

@ -22,7 +22,6 @@
#include "guiengine/widgets/CGUIEditBox.hpp" #include "guiengine/widgets/CGUIEditBox.hpp"
#include "utils/ptr_vector.hpp" #include "utils/ptr_vector.hpp"
#include "utils/translation.hpp" #include "utils/translation.hpp"
#include "utils/utf8/unchecked.h"
#include <IGUIElement.h> #include <IGUIElement.h>
#include <IGUIEnvironment.h> #include <IGUIEnvironment.h>
@ -39,9 +38,8 @@ public:
MyCGUIEditBox(const wchar_t* text, bool border, gui::IGUIEnvironment* environment, MyCGUIEditBox(const wchar_t* text, bool border, gui::IGUIEnvironment* environment,
gui:: IGUIElement* parent, s32 id, const core::rect<s32>& rectangle) : gui:: IGUIElement* parent, s32 id, const core::rect<s32>& rectangle) :
CGUIEditBox(text, border, environment, parent, id, rectangle, translations->isRTLLanguage()) CGUIEditBox(text, border, environment, parent, id, rectangle)
{ {
if (translations->isRTLLanguage()) setTextAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_CENTER);
} }
void addListener(GUIEngine::ITextBoxWidgetListener* listener) void addListener(GUIEngine::ITextBoxWidgetListener* listener)
@ -68,8 +66,7 @@ public:
if (m_listeners[n].onEnterPressed(Text)) if (m_listeners[n].onEnterPressed(Text))
{ {
handled = true; handled = true;
Text = L""; setText(L"");
CursorPos = MarkBegin = MarkEnd = 0;
} }
} }
return handled; return handled;
@ -271,14 +268,8 @@ ANDROID_EDITTEXT_CALLBACK(ANDROID_PACKAGE_CALLBACK_NAME)
if (utf8_text == NULL) if (utf8_text == NULL)
return; return;
// Use utf32 for emoji later // Android use 32bit wchar_t
static_assert(sizeof(wchar_t) == sizeof(uint32_t), "Invalid wchar size"); std::u32string to_editbox = StringUtils::utf8ToUtf32(utf8_text);
std::vector<wchar_t> utf32line;
utf8::unchecked::utf8to32(utf8_text, utf8_text + strlen(utf8_text),
back_inserter(utf32line));
utf32line.push_back(0);
core::stringw to_editbox(&utf32line[0]);
env->ReleaseStringUTFChars(text, utf8_text); env->ReleaseStringUTFChars(text, utf8_text);
GUIEngine::addGUIFunctionBeforeRendering([widget_id, to_editbox, start, GUIEngine::addGUIFunctionBeforeRendering([widget_id, to_editbox, start,