diff --git a/data/skins/Coal.stkskin b/data/skins/Coal.stkskin
index 2b15c04fd..7d97d1d4c 100644
--- a/data/skins/Coal.stkskin
+++ b/data/skins/Coal.stkskin
@@ -294,6 +294,7 @@ when the border that intersect at this corner are enabled.
+
diff --git a/data/skins/Forest.stkskin b/data/skins/Forest.stkskin
index e1bb6fcdf..d375b8f14 100644
--- a/data/skins/Forest.stkskin
+++ b/data/skins/Forest.stkskin
@@ -294,6 +294,7 @@ when the border that intersect at this corner are enabled.
+
diff --git a/data/skins/Ocean.stkskin b/data/skins/Ocean.stkskin
index afc904d0e..fc2e81765 100644
--- a/data/skins/Ocean.stkskin
+++ b/data/skins/Ocean.stkskin
@@ -293,6 +293,7 @@ when the border that intersect at this corner are enabled.
+
diff --git a/data/skins/Peach.stkskin b/data/skins/Peach.stkskin
index 23f3d402e..e09672c1f 100644
--- a/data/skins/Peach.stkskin
+++ b/data/skins/Peach.stkskin
@@ -291,6 +291,7 @@ when the border that intersect at this corner are enabled.
+
diff --git a/data/skins/Ruby.stkskin b/data/skins/Ruby.stkskin
index bb228b173..388cdddab 100644
--- a/data/skins/Ruby.stkskin
+++ b/data/skins/Ruby.stkskin
@@ -292,6 +292,7 @@ when the border that intersect at this corner are enabled.
+
diff --git a/lib/irrlicht/include/GlyphLayout.h b/lib/irrlicht/include/GlyphLayout.h
index c47c541da..b6f6c0050 100644
--- a/lib/irrlicht/include/GlyphLayout.h
+++ b/lib/irrlicht/include/GlyphLayout.h
@@ -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. */
};
+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.
struct GlyphLayout
{
@@ -37,6 +44,7 @@ s32 y_offset;
/* Above variable is same for raqm_glyph_t */
// If some characters share the same glyph
std::vector cluster;
+std::vector draw_flags;
//! used to sorting back the visual order after line breaking
u32 original_index;
u16 flags;
diff --git a/src/font/font_manager.cpp b/src/font/font_manager.cpp
index b4340fb4b..89f32fe18 100644
--- a/src/font/font_manager.cpp
+++ b/src/font/font_manager.cpp
@@ -437,6 +437,8 @@ void FontManager::shape(const std::u32string& text,
extra_cluster <= next_cluster - 1; 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
std::sort(cur_line.begin(), cur_line.end(), []
diff --git a/src/font/font_with_face.cpp b/src/font/font_with_face.cpp
index 460d3d5d4..9bf259f22 100644
--- a/src/font/font_with_face.cpp
+++ b/src/font/font_with_face.cpp
@@ -574,6 +574,8 @@ void FontWithFace::render(const std::vector& gl,
core::array indices(text_size);
core::array> offsets(text_size);
std::vector fallback(text_size);
+ core::array> gld_offsets;
+ gui::GlyphLayoutDraw df_used = gui::GLD_NONE;
// Check if the line is RTL
bool rtl = (gl[0].flags & gui::GLF_RTL_LINE) != 0;
@@ -596,7 +598,7 @@ void FontWithFace::render(const std::vector& gl,
if (line_changed)
{
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);
if (hcenter)
{
@@ -680,8 +682,32 @@ void FontWithFace::render(const std::vector& gl,
}
else
{
- offset.X +=
- (int)(glyph_layout.x_advance * m_inverse_shaping) * scale;
+ int width = (int)(glyph_layout.x_advance * m_inverse_shaping);
+ 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
@@ -825,6 +851,27 @@ void FontWithFace::render(const std::vector& gl,
}
}
}
+ for (unsigned i = 0; i < gld_offsets.size(); i += 2)
+ {
+ if (df_used == gui::GLD_MARKED)
+ {
+ core::rect 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 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
} // render
diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp
index 5f2205746..5e2f9f18a 100644
--- a/src/guiengine/widgets/CGUIEditBox.cpp
+++ b/src/guiengine/widgets/CGUIEditBox.cpp
@@ -13,16 +13,18 @@
#include "Keycodes.h"
#include "config/user_config.hpp"
+#include "font/font_manager.hpp"
#include "graphics/2dutils.hpp"
#include "graphics/irr_driver.hpp"
+#include "guiengine/engine.hpp"
#include "guiengine/screen_keyboard.hpp"
#include "utils/string_utils.hpp"
-#include "utils/translation.hpp"
#include "utils/time.hpp"
#include "../../../lib/irrlicht/include/IrrCompileConfig.h"
#include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h"
+#include
#include
#ifdef ANDROID
@@ -36,41 +38,159 @@
double click/ctrl click: word select + drag to select whole words, triple click to select line
optional? dragging selected text
numerical
- correct the mark position in RTL text, currently you can identify by highlight the text
*/
-#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
-#define UTF16_IS_SURROGATE_LO(c) (((c) & 0xFC00) == 0xDC00)
-#define UTF16_IS_SURROGATE_HI(c) (((c) & 0xFC00) == 0xD800)
+#ifndef SERVER_ONLY
+#include
+// Copied from libraqm
+namespace Grahem
+{
+typedef enum
+{
+ RAQM_GRAPHEM_CR,
+ RAQM_GRAPHEM_LF,
+ RAQM_GRAPHEM_CONTROL,
+ RAQM_GRAPHEM_EXTEND,
+ RAQM_GRAPHEM_REGIONAL_INDICATOR,
+ RAQM_GRAPHEM_PREPEND,
+ RAQM_GRAPHEM_SPACING_MARK,
+ RAQM_GRAPHEM_HANGUL_SYLLABLE,
+ RAQM_GRAPHEM_OTHER
+} _raqm_grapheme_t;
+
+static _raqm_grapheme_t
+_raqm_get_grapheme_break (hb_codepoint_t ch,
+ hb_unicode_general_category_t category);
+
+static bool
+_raqm_allowed_grapheme_boundary (hb_codepoint_t l_char,
+ hb_codepoint_t r_char)
+{
+ hb_unicode_general_category_t l_category;
+ hb_unicode_general_category_t r_category;
+ _raqm_grapheme_t l_grapheme, r_grapheme;
+ hb_unicode_funcs_t* unicode_funcs = hb_unicode_funcs_get_default ();
+
+ l_category = hb_unicode_general_category (unicode_funcs, l_char);
+ r_category = hb_unicode_general_category (unicode_funcs, r_char);
+ l_grapheme = _raqm_get_grapheme_break (l_char, l_category);
+ r_grapheme = _raqm_get_grapheme_break (r_char, r_category);
+
+ if (l_grapheme == RAQM_GRAPHEM_CR && r_grapheme == RAQM_GRAPHEM_LF)
+ return false; /*Do not break between a CR and LF GB3*/
+ if (l_grapheme == RAQM_GRAPHEM_CONTROL || l_grapheme == RAQM_GRAPHEM_CR ||
+ l_grapheme == RAQM_GRAPHEM_LF || r_grapheme == RAQM_GRAPHEM_CONTROL ||
+ r_grapheme == RAQM_GRAPHEM_CR || r_grapheme == RAQM_GRAPHEM_LF)
+ return true; /*Break before and after CONTROL GB4, GB5*/
+ if (r_grapheme == RAQM_GRAPHEM_HANGUL_SYLLABLE)
+ return false; /*Do not break Hangul syllable sequences. GB6, GB7, GB8*/
+ if (l_grapheme == RAQM_GRAPHEM_REGIONAL_INDICATOR &&
+ r_grapheme == RAQM_GRAPHEM_REGIONAL_INDICATOR)
+ return false; /*Do not break between regional indicator symbols. GB8a*/
+ if (r_grapheme == RAQM_GRAPHEM_EXTEND)
+ return false; /*Do not break before extending characters. GB9*/
+ /*Do not break before SpacingMarks, or after Prepend characters.GB9a, GB9b*/
+ if (l_grapheme == RAQM_GRAPHEM_PREPEND)
+ return false;
+ if (r_grapheme == RAQM_GRAPHEM_SPACING_MARK)
+ return false;
+ return true; /*Otherwise, break everywhere. GB1, GB2, GB10*/
+}
+
+static _raqm_grapheme_t
+_raqm_get_grapheme_break (hb_codepoint_t ch,
+ hb_unicode_general_category_t category)
+{
+ _raqm_grapheme_t gb_type;
+
+ gb_type = RAQM_GRAPHEM_OTHER;
+ switch ((int)category)
+ {
+ case HB_UNICODE_GENERAL_CATEGORY_FORMAT:
+ if (ch == 0x200C || ch == 0x200D)
+ gb_type = RAQM_GRAPHEM_EXTEND;
+ else
+ gb_type = RAQM_GRAPHEM_CONTROL;
+ break;
+
+ case HB_UNICODE_GENERAL_CATEGORY_CONTROL:
+ if (ch == 0x000D)
+ gb_type = RAQM_GRAPHEM_CR;
+ else if (ch == 0x000A)
+ gb_type = RAQM_GRAPHEM_LF;
+ else
+ gb_type = RAQM_GRAPHEM_CONTROL;
+ break;
+
+ case HB_UNICODE_GENERAL_CATEGORY_SURROGATE:
+ case HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR:
+ case HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR:
+ case HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED:
+ if ((ch >= 0xFFF0 && ch <= 0xFFF8) ||
+ (ch >= 0xE0000 && ch <= 0xE0FFF))
+ gb_type = RAQM_GRAPHEM_CONTROL;
+ break;
+
+ case HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK:
+ case HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK:
+ case HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK:
+ if (ch != 0x102B || ch != 0x102C || ch != 0x1038 ||
+ (ch <= 0x1062 && ch >= 0x1064) || (ch <= 0x1067 && ch >= 0x106D) ||
+ ch != 0x1083 || (ch <= 0x1087 && ch >= 0x108C) || ch != 0x108F ||
+ (ch <= 0x109A && ch >= 0x109C) || ch != 0x1A61 || ch != 0x1A63 ||
+ ch != 0x1A64 || ch != 0xAA7B || ch != 0xAA70 || ch != 0x11720 ||
+ ch != 0x11721) /**/
+ gb_type = RAQM_GRAPHEM_SPACING_MARK;
+
+ else if (ch == 0x09BE || ch == 0x09D7 ||
+ ch == 0x0B3E || ch == 0x0B57 || ch == 0x0BBE || ch == 0x0BD7 ||
+ ch == 0x0CC2 || ch == 0x0CD5 || ch == 0x0CD6 ||
+ ch == 0x0D3E || ch == 0x0D57 || ch == 0x0DCF || ch == 0x0DDF ||
+ ch == 0x1D165 || (ch >= 0x1D16E && ch <= 0x1D172))
+ gb_type = RAQM_GRAPHEM_EXTEND;
+ break;
+
+ case HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER:
+ if (ch == 0x0E33 || ch == 0x0EB3)
+ gb_type = RAQM_GRAPHEM_SPACING_MARK;
+ break;
+
+ case HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL:
+ if (ch >= 0x1F1E6 && ch <= 0x1F1FF)
+ gb_type = RAQM_GRAPHEM_REGIONAL_INDICATOR;
+ break;
+
+ default:
+ gb_type = RAQM_GRAPHEM_OTHER;
+ break;
+ }
+
+ return gb_type;
+}
+
+};
+#endif
+
+#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
namespace irr
{
void updateICPos(void* hWnd, s32 x, s32 y, s32 height);
}
#endif
-StkTime::TimeType getTime()
-{
- return StkTime::getTimeSinceEpoch();
-}
-
//! constructor
CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,
IGUIEnvironment* environment, IGUIElement* parent, s32 id,
- const core::rect& rectangle, bool is_rtl)
+ const core::rect& rectangle)
: IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false),
- Border(border), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0),
+ Border(border), OverrideColorEnabled(false), m_mark_begin(0), m_mark_end(0),
OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0),
- Operator(0), BlinkStartTime(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0),
- WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false),
- PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),
+ Operator(0), m_force_show_cursor_time(0), m_cursor_pos(0), m_scroll_pos(0), m_cursor_distance(0),
+ m_max_chars(0), AutoScroll(true), PasswordBox(false),
+ PasswordChar(U'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),
CurrentTextRect(0,0,1,1), FrameRect(rectangle)
{
- //m_rtl = is_rtl;
- m_rtl = false;
- // FIXME quick hack to enable mark movement with keyboard and mouse for rtl language,
- // don't know why it's disabled in the first place, because STK fail
- // to input unicode characters before?
m_from_android_edittext = false;
m_composing_start = 0;
m_composing_end = 0;
@@ -81,6 +201,7 @@ CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,
#endif
Text = text;
+ m_edit_text = StringUtils::wideToUtf32(text);
#ifndef SERVER_ONLY
if (Environment)
@@ -104,8 +225,6 @@ CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,
FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
}
- breakText();
-
calculateScrollPos();
}
@@ -146,7 +265,6 @@ void CGUIEditBox::setOverrideFont(IGUIFont* font)
if (OverrideFont)
OverrideFont->grab();
- breakText();
}
@@ -183,45 +301,10 @@ bool CGUIEditBox::isOverrideColorEnabled() const
return OverrideColorEnabled;
}
-//! Enables or disables word wrap
-void CGUIEditBox::setWordWrap(bool enable)
-{
- WordWrap = enable;
- breakText();
-}
-
void CGUIEditBox::updateAbsolutePosition()
{
- core::rect oldAbsoluteRect(AbsoluteRect);
IGUIElement::updateAbsolutePosition();
- if ( oldAbsoluteRect != AbsoluteRect )
- {
- breakText();
- }
-}
-
-
-//! Checks if word wrap is enabled
-bool CGUIEditBox::isWordWrapEnabled() const
-{
- _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
- return WordWrap;
-}
-
-
-//! Enables or disables newlines.
-void CGUIEditBox::setMultiLine(bool enable)
-{
- MultiLine = enable;
-}
-
-
-//! Checks if multi line editing is enabled
-bool CGUIEditBox::isMultiLineEnabled() const
-{
- _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
- return MultiLine;
}
@@ -229,12 +312,7 @@ void CGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
{
PasswordBox = passwordBox;
if (PasswordBox)
- {
- PasswordChar = passwordChar;
- setMultiLine(false);
- setWordWrap(false);
- BrokenText.clear();
- }
+ PasswordChar = (char32_t)passwordChar;
}
@@ -286,7 +364,8 @@ bool CGUIEditBox::OnEvent(const SEvent& event)
}
else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)
{
- MarkBegin = MarkEnd = CursorPos = (u32)Text.size();
+ m_mark_begin = m_mark_end = m_cursor_pos = getTextCount();
+ updateCursorDistance();
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
if (irr_driver->getDevice()->getType() == irr::EIDT_X11)
{
@@ -306,7 +385,7 @@ bool CGUIEditBox::OnEvent(const SEvent& event)
m_from_android_edittext = true;
CIrrDeviceAndroid* dl = dynamic_cast(
irr_driver->getDevice());
- dl->fromSTKEditBox(getID(), Text, MarkBegin, MarkEnd, m_type);
+ dl->fromSTKEditBox(getID(), Text, m_mark_begin, m_mark_end, m_type);
}
else
#endif
@@ -338,17 +417,43 @@ bool CGUIEditBox::OnEvent(const SEvent& event)
}
+void CGUIEditBox::correctCursor(s32& cursor_pos, bool left)
+{
+#ifndef SERVER_ONLY
+ if (left)
+ {
+ if (cursor_pos >= (s32)m_edit_text.size())
+ return;
+ while (cursor_pos != 0 &&
+ !Grahem::_raqm_allowed_grapheme_boundary(m_edit_text[cursor_pos - 1],
+ m_edit_text[cursor_pos]))
+ cursor_pos--;
+ }
+ else
+ {
+ while (cursor_pos != 0 && cursor_pos != (s32)m_edit_text.size() &&
+ !Grahem::_raqm_allowed_grapheme_boundary(m_edit_text[cursor_pos - 1],
+ m_edit_text[cursor_pos]))
+ cursor_pos++;
+ }
+#endif
+}
+
+
bool CGUIEditBox::processKey(const SEvent& event)
{
+#ifdef SERVER_ONLY
+ return false;
+#else
if (!event.KeyInput.PressedDown)
return false;
- bool textChanged = false;
- s32 newMarkBegin = MarkBegin;
- s32 newMarkEnd = MarkEnd;
+ bool text_changed = false;
+ s32 new_mark_begin = m_mark_begin;
+ s32 new_mark_end = m_mark_end;
+ s32 new_cursor_pos = m_cursor_pos;
// control shortcut handling
-
if (event.KeyInput.Control)
{
// german backlash '\' entered with control + '?'
@@ -362,161 +467,138 @@ bool CGUIEditBox::processKey(const SEvent& event)
{
case IRR_KEY_A:
// select all
- newMarkBegin = 0;
- newMarkEnd = Text.size();
+ new_mark_begin = 0;
+ new_mark_end = (s32)m_edit_text.size();
+ new_cursor_pos = new_mark_end;
break;
case IRR_KEY_C:
// copy to clipboard
- if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+ if (!PasswordBox && Operator && m_mark_begin != m_mark_end)
{
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
- core::stringw s;
- s = Text.subString(realmbgn, realmend - realmbgn).c_str();
+ std::u32string s = m_edit_text.substr(realmbgn, realmend - realmbgn);
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
- Operator->copyToClipboard(s.c_str());
+ Operator->copyToClipboard(StringUtils::utf32ToWide(s).c_str());
#else
- Operator->copyToClipboard(StringUtils::wideToUtf8(s).c_str());
+ Operator->copyToClipboard(StringUtils::utf32ToUtf8(s).c_str());
#endif
}
break;
case IRR_KEY_X:
// cut to the clipboard
- if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+ if (!PasswordBox && Operator && m_mark_begin != m_mark_end)
{
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
// copy
- core::stringw sc;
- sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
+ std::u32string s = m_edit_text.substr(realmbgn, realmend - realmbgn);
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
- Operator->copyToClipboard(sc.c_str());
+ Operator->copyToClipboard(StringUtils::utf32ToWide(s).c_str());
#else
- Operator->copyToClipboard(StringUtils::wideToUtf8(sc).c_str());
+ Operator->copyToClipboard(StringUtils::utf32ToUtf8(s).c_str());
#endif
if (isEnabled())
{
// delete
- core::stringw s;
- s = Text.subString(0, realmbgn);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
+ std::u32string sub_str = m_edit_text.substr(0, realmbgn);
+ sub_str += m_edit_text.substr(realmend, m_edit_text.size() - realmend);
+ m_edit_text = sub_str;
- CursorPos = realmbgn;
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ new_cursor_pos = realmbgn;
+ text_changed = true;
}
}
break;
case IRR_KEY_V:
- if ( !isEnabled() )
+ if (!isEnabled())
break;
// paste from the clipboard
if (Operator)
{
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
// add new character
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
- const wchar_t* p = Operator->getTextFromClipboard();
+ const std::u32string clipboard =
+ StringUtils::wideToUtf32(Operator->getTextFromClipboard());
#else
- const c8* p = Operator->getTextFromClipboard();
+ const std::u32string clipboard =
+ StringUtils::utf8ToUtf32(Operator->getTextFromClipboard());
#endif
- if (p)
+ if (!clipboard.empty())
{
- if (MarkBegin == MarkEnd)
+ if (m_mark_begin == m_mark_end)
{
// insert text
- core::stringw s = Text.subString(0, CursorPos);
-#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
- s.append(StringUtils::utf8ToWide(p));
-#else
- s.append(p);
-#endif
- s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+ std::u32string sub_str = m_edit_text.substr(0, m_cursor_pos);
+ sub_str += clipboard;
+ sub_str += m_edit_text.substr(m_cursor_pos, m_edit_text.size() - m_cursor_pos);
- if (!Max || s.size()<=Max) // thx to Fish FH for fix
+ if (m_max_chars == 0 || sub_str.size() <= m_max_chars) // thx to Fish FH for fix
{
- Text = s;
-#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
- s = StringUtils::utf8ToWide(p);
-#else
- s = p;
-#endif
- CursorPos += s.size();
+ m_edit_text = sub_str;
+ new_cursor_pos = m_cursor_pos + (s32)clipboard.size();
}
}
else
{
// replace text
+ std::u32string sub_str = m_edit_text.substr(0, realmbgn);
+ sub_str += clipboard;
+ sub_str += m_edit_text.substr(realmend, m_edit_text.size() - realmend);
- core::stringw s = Text.subString(0, realmbgn);
-
-#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
- s.append(StringUtils::utf8ToWide(p));
-#else
- s.append(p);
-#endif
- s.append( Text.subString(realmend, Text.size()-realmend) );
-
- if (!Max || s.size()<=Max) // thx to Fish FH for fix
+ if (m_max_chars == 0 || sub_str.size() <= m_max_chars) // thx to Fish FH for fix
{
- Text = s;
-#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
- s = StringUtils::utf8ToWide(p);
-#else
- s = p;
-#endif
- CursorPos = realmbgn + s.size();
+ m_edit_text = sub_str;
+ new_cursor_pos = realmbgn + (s32)sub_str.size();
}
}
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ text_changed = true;
}
-
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
}
break;
case IRR_KEY_HOME:
- if (!m_rtl)
{
// move/highlight to start of text
if (event.KeyInput.Shift)
{
- newMarkEnd = CursorPos;
- newMarkBegin = 0;
- CursorPos = 0;
+ new_mark_end = m_cursor_pos;
+ new_mark_begin = 0;
+ new_cursor_pos = 0;
}
else
{
- CursorPos = 0;
- newMarkBegin = 0;
- newMarkEnd = 0;
+ new_cursor_pos = 0;
+ new_mark_begin = 0;
+ new_mark_end = 0;
}
}
break;
case IRR_KEY_END:
- if (!m_rtl)
{
// move/highlight to end of text
if (event.KeyInput.Shift)
{
- newMarkBegin = CursorPos;
- newMarkEnd = Text.size();
- CursorPos = 0;
+ new_mark_begin = m_cursor_pos;
+ new_mark_end = (s32)m_edit_text.size();
+ new_cursor_pos = 0;
}
else
{
- CursorPos = Text.size();
- newMarkBegin = 0;
- newMarkEnd = 0;
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ new_cursor_pos = (s32)m_edit_text.size();
}
}
break;
@@ -531,281 +613,199 @@ bool CGUIEditBox::processKey(const SEvent& event)
/*
case IRR_KEY_Q:
inputChar(L'\u05DC');
- textChanged = true;
+ text_changed = true;
return true;
case IRR_KEY_W:
inputChar(L'\u05DB');
- textChanged = true;
+ text_changed = true;
return true;
case IRR_KEY_E:
inputChar(L'\u05DA');
- textChanged = true;
+ text_changed = true;
return true;
case IRR_KEY_R:
inputChar(L'\u05D9');
- textChanged = true;
+ text_changed = true;
return true;
case IRR_KEY_T:
inputChar(L'\u05D8');
- textChanged = true;
+ text_changed = true;
return true;
case IRR_KEY_Y:
inputChar(L'\u05D7');
- textChanged = true;
+ text_changed = true;
return true;
*/
case IRR_KEY_END:
- if (!m_rtl)
{
- s32 p = Text.size();
- if (WordWrap || MultiLine)
- {
- p = getLineFromPos(CursorPos);
- p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
- if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
- p-=1;
- }
-
+ s32 p = getTextCount();
if (event.KeyInput.Shift)
{
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
-
- newMarkEnd = p;
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
+ new_mark_end = p;
}
else
{
- newMarkBegin = 0;
- newMarkEnd = 0;
+ new_mark_begin = 0;
+ new_mark_end = 0;
}
- CursorPos = p;
- BlinkStartTime = getTime();
+ new_cursor_pos = p;
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
}
break;
case IRR_KEY_HOME:
- if (!m_rtl)
{
-
s32 p = 0;
- if (WordWrap || MultiLine)
- {
- p = getLineFromPos(CursorPos);
- p = BrokenTextPositions[p];
- }
-
if (event.KeyInput.Shift)
{
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
- newMarkEnd = p;
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
+ new_mark_end = p;
}
else
{
- newMarkBegin = 0;
- newMarkEnd = 0;
+ new_mark_begin = 0;
+ new_mark_end = 0;
}
- CursorPos = p;
- BlinkStartTime = getTime();
+ new_cursor_pos = p;
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
}
break;
case IRR_KEY_RETURN:
- if (MultiLine)
- {
- inputChar(L'\n');
- return true;
- }
- else
{
irr_driver->getDevice()->toggleOnScreenKeyboard(false);
sendGuiEvent( EGET_EDITBOX_ENTER );
}
break;
case IRR_KEY_LEFT:
- if (!m_rtl)
{
if (event.KeyInput.Shift)
{
- if (CursorPos > 0)
+ if (m_cursor_pos > 0)
{
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
- newMarkEnd = CursorPos-1;
+ new_mark_end = m_cursor_pos-1;
+ correctCursor(new_mark_end, true/*left*/);
}
}
else
{
- newMarkBegin = 0;
- newMarkEnd = 0;
+ new_mark_begin = 0;
+ new_mark_end = 0;
}
- if (CursorPos > 0) CursorPos--;
- BlinkStartTime = getTime();
+ if (m_cursor_pos > 0)
+ {
+ new_cursor_pos = m_cursor_pos - 1;
+ correctCursor(new_cursor_pos, true/*left*/);
+ }
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
}
break;
case IRR_KEY_RIGHT:
- if (!m_rtl)
{
if (event.KeyInput.Shift)
{
- if (Text.size() > (u32)CursorPos)
+ if (m_edit_text.size() > (u32)m_cursor_pos)
{
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
- newMarkEnd = CursorPos+1;
+ new_mark_end = m_cursor_pos + 1;
+ correctCursor(new_mark_end, false/*left*/);
}
}
else
{
- newMarkBegin = 0;
- newMarkEnd = 0;
+ new_mark_begin = 0;
+ new_mark_end = 0;
}
- if (Text.size() > (u32)CursorPos) CursorPos++;
- BlinkStartTime = getTime();
+ if (m_edit_text.size() > (u32)m_cursor_pos)
+ {
+ new_cursor_pos = m_cursor_pos + 1;
+ correctCursor(new_cursor_pos, false/*left*/);
+ }
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
}
break;
case IRR_KEY_UP:
- if (MultiLine || (WordWrap && BrokenText.size() > 1) )
- {
- s32 lineNo = getLineFromPos(CursorPos);
- s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
- if (lineNo > 0)
- {
- s32 cp = CursorPos - BrokenTextPositions[lineNo];
- if ((s32)BrokenText[lineNo-1].size() < cp)
- CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
- else
- CursorPos = BrokenTextPositions[lineNo-1] + cp;
- }
-
- if (event.KeyInput.Shift)
- {
- newMarkBegin = mb;
- newMarkEnd = CursorPos;
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
-
- }
- else
- {
- return false;
- }
- break;
case IRR_KEY_DOWN:
- if (MultiLine || (WordWrap && BrokenText.size() > 1) )
- {
- s32 lineNo = getLineFromPos(CursorPos);
- s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
- if (lineNo < (s32)BrokenText.size()-1)
- {
- s32 cp = CursorPos - BrokenTextPositions[lineNo];
- if ((s32)BrokenText[lineNo+1].size() < cp)
- CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
- else
- CursorPos = BrokenTextPositions[lineNo+1] + cp;
- }
-
- if (event.KeyInput.Shift)
- {
- newMarkBegin = mb;
- newMarkEnd = CursorPos;
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
-
- }
- else
{
return false;
}
break;
case IRR_KEY_BACK:
- if ( !isEnabled() )
+ if (!isEnabled())
break;
- if (Text.size())
+ if (!m_edit_text.empty())
{
- core::stringw s;
-
- if (MarkBegin != MarkEnd)
+ std::u32string sub_str;
+ if (m_mark_begin != m_mark_end)
{
// delete marked text
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
- s = Text.subString(0, realmbgn);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
-
- CursorPos = realmbgn;
+ std::u32string sub_str = m_edit_text.substr(0, realmbgn);
+ sub_str += m_edit_text.substr(realmend, m_edit_text.size() - realmend);
+ m_edit_text = sub_str;
+ new_cursor_pos = realmbgn;
}
else
{
// delete text behind cursor
- if (CursorPos>0)
- s = Text.subString(0, CursorPos-1);
+ if (m_cursor_pos > 0)
+ sub_str = m_edit_text.substr(0, m_cursor_pos - 1);
else
- s = L"";
- s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
- Text = s;
- --CursorPos;
+ sub_str.clear();
+ sub_str += m_edit_text.substr(m_cursor_pos, m_edit_text.size() - m_cursor_pos);
+ m_edit_text = sub_str;
+ new_cursor_pos = m_cursor_pos - 1;
}
-
- if (CursorPos < 0)
- CursorPos = 0;
- BlinkStartTime = getTime();
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ text_changed = true;
}
break;
case IRR_KEY_DELETE:
- if ( !isEnabled() )
+ if (!isEnabled())
break;
- if (Text.size() != 0)
+ if (!m_edit_text.empty())
{
- core::stringw s;
-
- if (MarkBegin != MarkEnd)
+ std::u32string sub_str;
+ if (m_mark_begin != m_mark_end)
{
// delete marked text
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
- s = Text.subString(0, realmbgn);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
-
- CursorPos = realmbgn;
+ sub_str = m_edit_text.substr(0, realmbgn);
+ sub_str += m_edit_text.substr(realmend, m_edit_text.size() - realmend);
+ m_edit_text = sub_str;
+ new_cursor_pos = realmbgn;
+ text_changed = true;
}
- else
+ else if (m_cursor_pos != getTextCount())
{
// delete text before cursor
- s = Text.subString(0, CursorPos);
- s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
- Text = s;
+ sub_str = m_edit_text.substr(0, m_cursor_pos);
+ sub_str += m_edit_text.substr(m_cursor_pos + 1, m_edit_text.size() - m_cursor_pos - 1);
+ m_edit_text = sub_str;
+ text_changed = true;
}
-
- if (CursorPos > (s32)Text.size())
- CursorPos = (s32)Text.size();
-
- BlinkStartTime = getTime();
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
+ new_mark_begin = 0;
+ new_mark_end = 0;
}
break;
@@ -844,58 +844,26 @@ bool CGUIEditBox::processKey(const SEvent& event)
return true;
}
- // Set new text markers
- setTextMarkers( newMarkBegin, newMarkEnd );
-
- // break the text if it has changed
- if (textChanged)
+ // Update glyph layouts, the next setTextMarks will update text to android
+ if (text_changed)
{
- breakText();
+ updateGlyphLayouts();
sendGuiEvent(EGET_EDITBOX_CHANGED);
}
+ // Set new text markers
+ setTextMarkers(new_mark_begin, new_mark_end);
+
+ if (new_cursor_pos > getTextCount())
+ new_cursor_pos = getTextCount();
+ m_cursor_pos = new_cursor_pos;
+ if (m_cursor_pos < 0)
+ m_cursor_pos = 0;
+
calculateScrollPos();
- if (CursorPos > (s32)Text.size())
- CursorPos = Text.size();
-
-#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
- switch(event.KeyInput.Key)
- {
- // If cursor points the surrogate low, send IRR_KEY_LEFT event.
- case IRR_KEY_UP:
- case IRR_KEY_DOWN:
- if (MultiLine || (WordWrap && BrokenText.size() > 1) )
- {
- if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
- {
- SEvent leftEvent;
- leftEvent = event;
- leftEvent.KeyInput.Key = IRR_KEY_LEFT;
- Environment->postEventFromUser(leftEvent);
- }
- }
- break;
- // If cursor points the surrogate low, send a same event.
- case IRR_KEY_LEFT:
- case IRR_KEY_RIGHT:
- case IRR_KEY_DELETE:
- if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
- Environment->postEventFromUser(event);
- break;
- // If cursor points front of the surrogate high, send a same event.
- case IRR_KEY_BACK:
- if (CursorPos > 0)
- {
- if (UTF16_IS_SURROGATE_HI(Text[CursorPos-1]))
- Environment->postEventFromUser(event);
- }
- break;
- default:
- break;
- }
-#endif
return true;
+#endif
}
@@ -909,6 +877,7 @@ bool CGUIEditBox::processIMEEvent(const SEvent& event)
return true;
case EIME_CHANGE_POS:
{
+ updateCursorDistance();
core::position2di pos = calculateICPos();
IGUIFont* font = OverrideFont;
@@ -917,7 +886,7 @@ bool CGUIEditBox::processIMEEvent(const SEvent& event)
if (!OverrideFont)
font = skin->getFont();
- irr::updateICPos(event.InputMethodEvent.Handle, pos.X,pos.Y, font->getDimension(L"|").Height);
+ irr::updateICPos(event.InputMethodEvent.Handle, pos.X,pos.Y, font->getHeightPerLine());
return true;
}
@@ -934,24 +903,9 @@ bool CGUIEditBox::processIMEEvent(const SEvent& event)
core::position2di CGUIEditBox::calculateICPos()
{
core::position2di pos;
- IGUIFont* font = OverrideFont;
- IGUISkin* skin = Environment->getSkin();
- if (!OverrideFont)
- font = skin->getFont();
-
- //drop the text that clipping on the right side
- if (WordWrap | MultiLine)
- {
- // todo : It looks like a heavy drinker. Strange!!
- pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, BrokenTextPositions[getLineFromPos(CursorPos)] + BrokenText[getLineFromPos(CursorPos)].size() - CursorPos).c_str()).Width;
- pos.Y = CurrentTextRect.UpperLeftCorner.Y + font->getDimension(L"|").Height + (Border ? 3 : 0) - ((MultiLine | WordWrap) ? 3 : 0);
- }
- else
- {
- pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, Text.size() - CursorPos).c_str()).Width;
- pos.Y = AbsoluteRect.getCenter().Y + (Border ? 3 : 0); //bug? The text is always drawn in the height of the center. SetTextAlignment() doesn't influence.
- }
-
+ pos.X = CurrentTextRect.UpperLeftCorner.X + m_cursor_distance;
+ //bug? The text is always drawn in the height of the center. SetTextAlignment() doesn't influence.
+ pos.Y = AbsoluteRect.getCenter().Y + (Border ? 3 : 0);
return pos;
}
@@ -976,11 +930,10 @@ void CGUIEditBox::draw()
FrameRect = AbsoluteRect;
// draw the border
-
if (Border)
{
EGUI_DEFAULT_COLOR col = EGDC_GRAY_EDITABLE;
- if ( isEnabled() )
+ if (isEnabled())
col = focus ? EGDC_FOCUSED_EDITABLE : EGDC_EDITABLE;
skin->draw3DSunkenPane(this, skin->getColor(col),
false, true, FrameRect, &AbsoluteClippingRect);
@@ -993,196 +946,78 @@ void CGUIEditBox::draw()
core::rect localClipRect = FrameRect;
localClipRect.clipAgainst(AbsoluteClippingRect);
- // draw the text
-
IGUIFont* font = OverrideFont;
if (!OverrideFont)
font = skin->getFont();
- s32 cursorLine = 0;
- s32 charcursorpos = 0;
+ if (!font)
+ return;
- if (font)
+ setTextRect(0);
+ // Save the override color information.
+ // Then, alter it if the edit box is disabled.
+ const bool prevOver = OverrideColorEnabled;
+ const video::SColor prevColor = OverrideColor;
+
+ if (!isEnabled() && !OverrideColorEnabled)
{
- if (LastBreakFont != font)
+ OverrideColorEnabled = true;
+ OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
+ }
+
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+ const s32 realcbgn = m_composing_start < m_composing_end ? m_composing_start : m_composing_end;
+ const s32 realcend = m_composing_start < m_composing_end ? m_composing_end : m_composing_start;
+
+ for (unsigned i = 0; i < m_glyph_layouts.size(); i++)
+ {
+ GlyphLayout& glyph = m_glyph_layouts[i];
+ auto& cluster = glyph.cluster;
+ for (unsigned c = 0; c < glyph.cluster.size(); c++)
{
- breakText();
- }
-
- // calculate cursor pos
-
- core::stringw *txtLine = &Text;
- s32 startPos = 0;
-
- core::stringw s, s2;
-
- // get mark position
- const bool ml = (!PasswordBox && (WordWrap || MultiLine));
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
- const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
- const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
- const s32 lineCount = ml ? BrokenText.size() : 1;
-
- // Save the override color information.
- // Then, alter it if the edit box is disabled.
- const bool prevOver = OverrideColorEnabled;
- const video::SColor prevColor = OverrideColor;
-
- if (Text.size())
- {
- if (!isEnabled() && !OverrideColorEnabled)
+ if (realmbgn != realmend)
{
- OverrideColorEnabled = true;
- OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
+ if (cluster[c] >= realmbgn && cluster[c] < realmend)
+ glyph.draw_flags.at(c) = GLD_MARKED;
}
-
- for (s32 i=0; i < lineCount; ++i)
+ else if (!PasswordBox && realcbgn != realcend)
{
- setTextRect(i);
-
- // clipping test - don't draw anything outside the visible area
- core::rect c = localClipRect;
- c.clipAgainst(CurrentTextRect);
- if (!c.isValid())
- continue;
-
- // get current line
- if (PasswordBox)
- {
- if (BrokenText.size() != 1)
- {
- BrokenText.clear();
- BrokenText.push_back(core::stringw());
- }
- if (BrokenText[0].size() != Text.size())
- {
- BrokenText[0] = Text;
- for (u32 q = 0; q < Text.size(); ++q)
- {
- BrokenText[0] [q] = PasswordChar;
- }
- }
- txtLine = &BrokenText[0];
- startPos = 0;
- }
- else
- {
- txtLine = ml ? &BrokenText[i] : &Text;
- startPos = ml ? BrokenTextPositions[i] : 0;
- }
-
- font->draw(translations->fribidize(txtLine->c_str()), CurrentTextRect,
- OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
- false, true, &localClipRect);
- // draw with fribidize no matter what language, because in fribidize function,
- // it will return the input pointer if (this->isRTLLanguage()) from Translations::isRTLText
- // is false
-
- // draw composing text underline
- if (!PasswordBox && focus && m_composing_start != m_composing_end && i == hlineStart)
- {
- s = txtLine->subString(0, m_composing_start);
- s32 underline_begin = font->getDimension(s.c_str()).Width;
- core::rect underline = CurrentTextRect;
- underline.UpperLeftCorner.X += underline_begin;
- s32 end_length = m_composing_end - m_composing_start;
- if (end_length > 0 && end_length != (s32)Text.size())
- {
- s = txtLine->subString(m_composing_start, end_length);
- s32 underline_end = font->getDimension(s.c_str()).Width;
- underline.LowerRightCorner.X = underline.UpperLeftCorner.X + underline_end;
- }
- s32 height = underline.LowerRightCorner.Y - underline.UpperLeftCorner.Y;
- underline.UpperLeftCorner.Y += s32(std::abs(height) * 0.9f);
- underline.LowerRightCorner.Y -= s32(std::abs(height) * 0.08f);
- GL32_draw2DRectangle(video::SColor(255, 0, 0, 0), underline);
- }
-
- // draw mark and marked text
- if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
- {
-
- s32 mbegin = 0, mend = 0;
- s32 lineStartPos = 0, lineEndPos = txtLine->size();
-
- if (i == hlineStart)
- {
- // highlight start is on this line
- s = txtLine->subString(0, realmbgn - startPos);
- mbegin = font->getDimension(s.c_str()).Width;
-
- // deal with kerning
- mbegin += font->getKerningWidth(
- &((*txtLine)[realmbgn - startPos]),
- realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
-
- lineStartPos = realmbgn - startPos;
- }
- if (i == hlineStart + hlineCount - 1)
- {
- // highlight end is on this line
- s2 = txtLine->subString(0, realmend - startPos);
- mend = font->getDimension(s2.c_str()).Width;
- lineEndPos = (s32)s2.size();
- }
- else
- mend = font->getDimension(txtLine->c_str()).Width;
-
- CurrentTextRect.UpperLeftCorner.X += mbegin;
- CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
-
- // draw mark
- skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
-
- // draw marked text
- s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
-
- if (s.size())
- font->draw(s.c_str(), CurrentTextRect,
- OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
- false, true, &localClipRect);
-
- }
+ if (cluster[c] >= realcbgn && cluster[c] < realcend)
+ glyph.draw_flags.at(c) = GLD_COMPOSING;
}
-
- // Return the override color information to its previous settings.
- OverrideColorEnabled = prevOver;
- OverrideColor = prevColor;
- }
-
- // draw cursor
-
- if (WordWrap || MultiLine)
- {
- cursorLine = getLineFromPos(CursorPos);
- txtLine = &BrokenText[cursorLine];
- startPos = BrokenTextPositions[cursorLine];
- }
- s = txtLine->subString(0,CursorPos-startPos);
- charcursorpos = font->getDimension(s.c_str()).Width ;
- // + font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
-
- if (focus && (getTime() - BlinkStartTime) % 2 == 0 && !m_rtl)
- {
- //setTextRect(cursorLine);
- //CurrentTextRect.UpperLeftCorner.X += charcursorpos;
-
- setTextRect(0);
-
- core::rect< s32 > caret_rect = CurrentTextRect;
- caret_rect.UpperLeftCorner.X += charcursorpos - 1;
- caret_rect.LowerRightCorner.X = caret_rect.UpperLeftCorner.X + 2;
- GL32_draw2DRectangle( video::SColor(255,0,0,0), caret_rect );
-
- /*
- font->draw(L"_", CurrentTextRect,
- OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
- false, true, &localClipRect);
- */
+ else
+ glyph.draw_flags.at(c) = GLD_NONE;
}
}
+ // draw the text layout
+ font->draw(m_glyph_layouts, CurrentTextRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+ false, true, &localClipRect);
+
+ // Reset draw flags
+ for (unsigned i = 0; i < m_glyph_layouts.size(); i++)
+ {
+ GlyphLayout& glyph = m_glyph_layouts[i];
+ std::fill(glyph.draw_flags.begin(), glyph.draw_flags.end(), GLD_NONE);
+ }
+
+ // draw cursor
+ uint64_t time_ms = StkTime::getMonoTimeMs();
+ if (focus &&
+ ((time_ms / 600) % 2 == 0 || m_force_show_cursor_time > time_ms))
+ {
+ core::rect< s32 > caret_rect = CurrentTextRect;
+ caret_rect.UpperLeftCorner.X += m_cursor_distance - 1;
+ caret_rect.LowerRightCorner.X = caret_rect.UpperLeftCorner.X + 2;
+ GL32_draw2DRectangle(video::SColor(255, 0, 0, 0), caret_rect);
+ }
+
+ // Return the override color information to its previous settings.
+ OverrideColorEnabled = prevOver;
+ OverrideColor = prevColor;
+
// draw children
IGUIElement::draw();
#endif
@@ -1192,10 +1027,10 @@ void CGUIEditBox::draw()
//! Sets the new caption of this element.
void CGUIEditBox::setText(const wchar_t* text)
{
- Text = text;
- MarkBegin = MarkEnd = CursorPos = (u32)Text.size();
- HScrollPos = 0;
- breakText();
+ m_edit_text = StringUtils::wideToUtf32(text);
+ updateGlyphLayouts();
+ m_mark_begin = m_mark_end = m_cursor_pos = getTextCount();
+ m_scroll_pos = 0;
#ifdef ANDROID
if (GUIEngine::ScreenKeyboard::shouldUseScreenKeyboard() &&
irr_driver->getDevice()->hasOnScreenKeyboard() &&
@@ -1203,7 +1038,7 @@ void CGUIEditBox::setText(const wchar_t* text)
{
CIrrDeviceAndroid* dl = dynamic_cast(
irr_driver->getDevice());
- dl->fromSTKEditBox(getID(), Text, MarkBegin, MarkEnd, m_type);
+ dl->fromSTKEditBox(getID(), Text, m_mark_begin, m_mark_end, m_type);
}
#endif
}
@@ -1230,19 +1065,19 @@ bool CGUIEditBox::isAutoScrollEnabled() const
//! \return Returns the size in pixels of the text
core::dimension2du CGUIEditBox::getTextDimension()
{
- core::rect ret;
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return core::dimension2du(0, 0);
- setTextRect(0);
- ret = CurrentTextRect;
+ IGUIFont* font = OverrideFont;
+ if (!OverrideFont)
+ font = skin->getFont();
- for (u32 i=1; i < BrokenText.size(); ++i)
- {
- setTextRect(i);
- ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
- ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
- }
+ if (!font)
+ return core::dimension2du(0, 0);
- return core::dimension2du(ret.getSize());
+ return gui::getGlyphLayoutsDimension(m_glyph_layouts,
+ font->getHeightPerLine(), font->getInverseShaping(), font->getScale());
}
@@ -1251,17 +1086,16 @@ core::dimension2du CGUIEditBox::getTextDimension()
//! infinity.
void CGUIEditBox::setMax(u32 max)
{
- Max = max;
-
- if (Text.size() > Max && Max != 0)
- Text = Text.subString(0, Max);
+ m_max_chars = max;
+ if (m_max_chars != 0 && m_edit_text.size() > m_max_chars)
+ m_edit_text.substr(0, m_max_chars);
}
//! Returns maximum amount of characters, previously set by setMax();
u32 CGUIEditBox::getMax() const
{
- return Max;
+ return m_max_chars;
}
@@ -1270,20 +1104,15 @@ bool CGUIEditBox::processMouse(const SEvent& event)
switch(event.MouseInput.Event)
{
case irr::EMIE_LMOUSE_LEFT_UP:
- if (Environment->hasFocus(this) && !m_rtl)
+ if (Environment->hasFocus(this))
{
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
- if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
- {
- if (CursorPos > 0)
- --CursorPos;
- }
-#endif
+ m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ s32 old_cursor = m_cursor_pos;
+ correctCursor(m_cursor_pos, old_cursor < m_mark_begin);
+ correctCursor(m_mark_begin, old_cursor > m_mark_begin);
if (MouseMarking)
- {
- setTextMarkers( MarkBegin, CursorPos );
- }
+ setTextMarkers(m_mark_begin, m_cursor_pos);
+
MouseMarking = false;
calculateScrollPos();
return true;
@@ -1297,15 +1126,9 @@ bool CGUIEditBox::processMouse(const SEvent& event)
{
if (MouseMarking)
{
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
- if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
- {
- if (CursorPos > 0)
- --CursorPos;
- }
-#endif
- setTextMarkers( MarkBegin, CursorPos );
+ m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ correctCursor(m_cursor_pos, m_cursor_pos < m_mark_begin);
+ setTextMarkers(m_mark_begin, m_cursor_pos);
calculateScrollPos();
return true;
}
@@ -1314,22 +1137,16 @@ bool CGUIEditBox::processMouse(const SEvent& event)
case EMIE_LMOUSE_PRESSED_DOWN:
if (!Environment->hasFocus(this))
{
- BlinkStartTime = getTime();
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
MouseMarking = true;
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
- if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
- {
- if (CursorPos > 0)
- --CursorPos;
- }
-#endif
- setTextMarkers(CursorPos, CursorPos );
+ m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ correctCursor(m_cursor_pos, m_cursor_pos < m_mark_begin);
+ setTextMarkers(m_cursor_pos, m_cursor_pos);
calculateScrollPos();
return true;
}
- else if (!m_rtl)
+ else
{
if (!AbsoluteClippingRect.isPointInside(
core::position2d(event.MouseInput.X, event.MouseInput.Y)))
@@ -1346,21 +1163,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
}
// move cursor
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-
-#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
- if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
- {
- if (CursorPos > 0)
- --CursorPos;
- }
-#endif
- s32 newMarkBegin = MarkBegin;
+ m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ s32 new_mark_begin = m_mark_begin;
if (!MouseMarking)
- newMarkBegin = CursorPos;
+ new_mark_begin = m_cursor_pos;
MouseMarking = true;
- setTextMarkers( newMarkBegin, CursorPos);
+ setTextMarkers(new_mark_begin, m_cursor_pos);
calculateScrollPos();
return true;
@@ -1377,157 +1186,17 @@ s32 CGUIEditBox::getCursorPos(s32 x, s32 y)
{
IGUIFont* font = OverrideFont;
IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return 0;
if (!OverrideFont)
font = skin->getFont();
- const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
-
- core::stringw *txtLine=0;
- s32 startPos=0;
- x+=3;
-
- for (u32 i=0; i < lineCount; ++i)
- {
- setTextRect(i);
- if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
- y = CurrentTextRect.UpperLeftCorner.Y;
- if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )
- y = CurrentTextRect.LowerRightCorner.Y;
-
- // is it inside this region?
- if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)
- {
- // we've found the clicked line
- txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
- startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
- break;
- }
- }
-
- if (x < CurrentTextRect.UpperLeftCorner.X)
- x = CurrentTextRect.UpperLeftCorner.X;
-
- s32 idx = txtLine ? font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X) : -1;
-
- // click was on or left of the line
- if (idx != -1)
- return idx + startPos;
-
- // click was off the right edge of the line, go to end.
- return txtLine->size() + startPos;
-}
-
-
-//! Breaks the single text line.
-void CGUIEditBox::breakText()
-{
- IGUISkin* skin = Environment->getSkin();
-
- if ((!WordWrap && !MultiLine) || !skin)
- return;
-
- BrokenText.clear(); // need to reallocate :/
- BrokenTextPositions.set_used(0);
-
- IGUIFont* font = OverrideFont;
- if (!OverrideFont)
- font = skin->getFont();
-
- if (!font)
- return;
-
- LastBreakFont = font;
-
- core::stringw line;
- core::stringw word;
- core::stringw whitespace;
- s32 lastLineStart = 0;
- s32 size = Text.size();
- s32 length = 0;
- s32 elWidth = RelativeRect.getWidth() - 6;
-
- for (s32 i=0; igetDimension(whitespace.c_str()).Width;
- s32 worldlgth = font->getDimension(word.c_str()).Width;
-
- if (WordWrap && length + worldlgth + whitelgth > elWidth)
- {
- // break to next line
- length = worldlgth;
- BrokenText.push_back(line);
- BrokenTextPositions.push_back(lastLineStart);
- lastLineStart = i - (s32)word.size();
- line = word;
- }
- else
- {
- // add word to line
- line += whitespace;
- line += word;
- length += whitelgth + worldlgth;
- }
-
- word = L"";
- whitespace = L"";
- }
-
- whitespace += c;
-
- // compute line break
- if (lineBreak)
- {
- line += whitespace;
- line += word;
- BrokenText.push_back(line);
- BrokenTextPositions.push_back(lastLineStart);
- lastLineStart = i+1;
- line = L"";
- word = L"";
- whitespace = L"";
- length = 0;
- }
- }
- else
- {
- // yippee this is a word..
- word += c;
- }
- }
-
- line += whitespace;
- line += word;
- BrokenText.push_back(line);
- BrokenTextPositions.push_back(lastLineStart);
+ x -= AbsoluteRect.UpperLeftCorner.X;
+ x += m_scroll_pos;
+ if (x < 0)
+ x = 0;
+ return getCurosrFromDimension(x, y, m_glyph_layouts, font->getHeightPerLine(),
+ font->getInverseShaping(), font->getScale());
}
@@ -1545,18 +1214,8 @@ void CGUIEditBox::setTextRect(s32 line)
return;
// get text dimension
- const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
- if (WordWrap || MultiLine)
- {
- d = font->getDimension(BrokenText[line].c_str());
- }
- else
- {
- d = font->getDimension(Text.c_str());
- d.Height = AbsoluteRect.getHeight();
- }
- d.Height += font->getKerningHeight();
-
+ d = gui::getGlyphLayoutsDimension(m_glyph_layouts,
+ font->getHeightPerLine(), font->getInverseShaping(), font->getScale());
// justification
switch (HAlign)
{
@@ -1582,12 +1241,12 @@ void CGUIEditBox::setTextRect(s32 line)
case EGUIA_CENTER:
// align to v centre
CurrentTextRect.UpperLeftCorner.Y =
- (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
+ (FrameRect.getHeight()/2) - (d.Height)/2 + d.Height*line;
break;
case EGUIA_LOWERRIGHT:
// align to bottom edge
CurrentTextRect.UpperLeftCorner.Y =
- FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
+ FrameRect.getHeight() - d.Height + d.Height*line;
break;
default:
// align to top edge
@@ -1595,9 +1254,8 @@ void CGUIEditBox::setTextRect(s32 line)
break;
}
- CurrentTextRect.UpperLeftCorner.X -= HScrollPos;
- CurrentTextRect.LowerRightCorner.X -= HScrollPos;
- CurrentTextRect.UpperLeftCorner.Y -= VScrollPos;
+ CurrentTextRect.UpperLeftCorner.X -= m_scroll_pos;
+ CurrentTextRect.LowerRightCorner.X -= m_scroll_pos;
CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
CurrentTextRect += FrameRect.UpperLeftCorner;
@@ -1605,112 +1263,104 @@ void CGUIEditBox::setTextRect(s32 line)
}
-s32 CGUIEditBox::getLineFromPos(s32 pos)
-{
- if (!WordWrap && !MultiLine)
- return 0;
-
- s32 i=0;
- while (i < (s32)BrokenTextPositions.size())
- {
- if (BrokenTextPositions[i] > pos)
- return i-1;
- ++i;
- }
- return (s32)BrokenTextPositions.size() - 1;
-}
-
-
void CGUIEditBox::inputChar(wchar_t c)
{
if (!isEnabled())
return;
- if (c != 0)
+ // Ignore unsupported characters
+ if (c < 32)
+ return;
+
+ if ((u32)getTextCount() < m_max_chars || m_max_chars == 0)
{
- if (Text.size() < Max || Max == 0)
+ std::u32string sub_str;
+
+ if (m_mark_begin != m_mark_end)
{
- core::stringw s;
+ // replace marked text
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
- if (MarkBegin != MarkEnd)
- {
- // replace marked text
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
- s = Text.subString(0, realmbgn);
- s.append(c);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
- CursorPos = realmbgn+1;
- }
- else
- {
- // add new character
- s = Text.subString(0, CursorPos);
- s.append(c);
- s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
- Text = s;
- ++CursorPos;
- }
-
- BlinkStartTime = getTime();
- setTextMarkers(0, 0);
+ sub_str = m_edit_text.substr(0, realmbgn);
+ sub_str += c;
+ sub_str += m_edit_text.substr(realmend, m_edit_text.size() - realmend);
+ m_edit_text = sub_str;
+ m_cursor_pos = realmbgn + 1;
}
+ else
+ {
+ // add new character
+ sub_str = m_edit_text.substr(0, m_cursor_pos);
+ sub_str += c;
+ sub_str += m_edit_text.substr(m_cursor_pos, m_edit_text.size() - m_cursor_pos);
+ m_edit_text = sub_str;
+ m_cursor_pos++;
+ }
+
+ m_force_show_cursor_time = StkTime::getMonoTimeMs() + 200;
+ updateGlyphLayouts();
+ setTextMarkers(0, 0);
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ calculateScrollPos();
}
- breakText();
- sendGuiEvent(EGET_EDITBOX_CHANGED);
- calculateScrollPos();
}
+void CGUIEditBox::updateCursorDistance()
+{
+ m_cursor_distance = 0;
+ // Update cursor position
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+ IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+ if (!font)
+ return;
+ if (m_glyph_layouts.empty())
+ return;
+ if (m_cursor_pos != 0)
+ {
+ m_cursor_distance = getGlyphLayoutsDimension(m_glyph_layouts,
+ font->getHeightPerLine(), font->getInverseShaping(),
+ font->getScale(), m_cursor_pos - 1).Width;
+ }
+ else if ((m_glyph_layouts[0].flags & GLF_RTL_LINE) != 0)
+ {
+ // For rtl line the cursor in the begining is total width
+ m_cursor_distance = getGlyphLayoutsDimension(m_glyph_layouts,
+ font->getHeightPerLine(), font->getInverseShaping(),
+ font->getScale()).Width;
+ }
+}
void CGUIEditBox::calculateScrollPos()
{
#ifndef SERVER_ONLY
- if (!AutoScroll)
+ // Update cursor position
+ updateCursorDistance();
+
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+ IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+ if (!font)
return;
- // calculate horizontal scroll position
- s32 cursLine = getLineFromPos(CursorPos);
- setTextRect(cursLine);
+ if (!AutoScroll)
+ return;
+ setTextRect(0);
- // don't do horizontal scrolling when wordwrap is enabled.
- if (!WordWrap)
- {
- // get cursor position
- IGUISkin* skin = Environment->getSkin();
- if (!skin)
- return;
- IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
- if (!font)
- return;
+ s32 cStart = CurrentTextRect.UpperLeftCorner.X + m_scroll_pos +
+ m_cursor_distance;
+ // Reserver 2x font height at border to see the clipped text
+ s32 cEnd = cStart + GUIEngine::getFontHeight() * 2;
- core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
- s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
-
- s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
- font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
-
- s32 cEnd = cStart + font->getDimension(L"_ ").Width;
-
- if (FrameRect.LowerRightCorner.X < cEnd)
- HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
- else if (FrameRect.UpperLeftCorner.X > cStart)
- HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
- else
- HScrollPos = 0;
-
- // todo: adjust scrollbar
- }
-
- // vertical scroll position
- if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
- VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
-
- else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
- VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
+ if (FrameRect.LowerRightCorner.X < cEnd)
+ m_scroll_pos = cEnd - FrameRect.LowerRightCorner.X;
+ else if (FrameRect.UpperLeftCorner.X > cStart)
+ m_scroll_pos = cStart - FrameRect.UpperLeftCorner.X;
else
- VScrollPos = 0;
+ m_scroll_pos = 0;
// todo: adjust scrollbar
#if defined(_IRR_COMPILE_WITH_X11_DEVICE_)
@@ -1733,10 +1383,10 @@ void CGUIEditBox::setTextMarkers(s32 begin, s32 end)
if (GUIEngine::ScreenKeyboard::isActive())
return;
- if ( begin != MarkBegin || end != MarkEnd )
+ if (begin != m_mark_begin || end != m_mark_end)
{
- MarkBegin = begin;
- MarkEnd = end;
+ m_mark_begin = begin;
+ m_mark_end = end;
sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
#ifdef ANDROID
if (GUIEngine::ScreenKeyboard::shouldUseScreenKeyboard() &&
@@ -1745,7 +1395,7 @@ void CGUIEditBox::setTextMarkers(s32 begin, s32 end)
{
CIrrDeviceAndroid* dl = dynamic_cast(
irr_driver->getDevice());
- dl->fromSTKEditBox(getID(), Text, MarkBegin, MarkEnd, m_type);
+ dl->fromSTKEditBox(getID(), Text, m_mark_begin, m_mark_end, m_type);
}
#endif
}
@@ -1774,9 +1424,7 @@ void CGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWr
out->addBool ("OverrideColorEnabled",OverrideColorEnabled );
out->addColor ("OverrideColor", OverrideColor);
// out->addFont("OverrideFont",OverrideFont);
- out->addInt ("MaxChars", Max);
- out->addBool ("WordWrap", WordWrap);
- out->addBool ("MultiLine", MultiLine);
+ out->addInt ("MaxChars", m_max_chars);
out->addBool ("AutoScroll", AutoScroll);
out->addBool ("PasswordBox", PasswordBox);
core::stringw ch = L" ";
@@ -1797,8 +1445,6 @@ void CGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadW
setOverrideColor(in->getAttributeAsColor("OverrideColor"));
enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
setMax(in->getAttributeAsInt("MaxChars"));
- setWordWrap(in->getAttributeAsBool("WordWrap"));
- setMultiLine(in->getAttributeAsBool("MultiLine"));
setAutoScroll(in->getAttributeAsBool("AutoScroll"));
core::stringw ch = in->getAttributeAsStringW("PasswordChar");
@@ -1821,31 +1467,31 @@ void CGUIEditBox::openScreenKeyboard()
if (GUIEngine::ScreenKeyboard::getCurrent() != NULL)
return;
-
+
new GUIEngine::ScreenKeyboard(1.0f, 0.40f, this);
}
// Real copying is happening in text_box_widget.cpp with static function
-void CGUIEditBox::fromAndroidEditText(const core::stringw& text, int start,
+void CGUIEditBox::fromAndroidEditText(const std::u32string& text, int start,
int end, int composing_start,
int composing_end)
{
// When focus of this element is lost, this will be set to false again
m_from_android_edittext = true;
- Text = text;
// Prevent invalid start or end
- if ((unsigned)end > Text.size())
+ if ((unsigned)end > text.size())
{
- end = (int)Text.size();
+ end = (int)text.size();
start = end;
}
-
- CursorPos = end;
+ m_edit_text = text;
+ updateGlyphLayouts();
+ m_cursor_pos = end;
m_composing_start = 0;
m_composing_end = 0;
- MarkBegin = start;
- MarkEnd = end;
+ m_mark_begin = start;
+ m_mark_end = end;
if (composing_start != composing_end)
{
@@ -1857,3 +1503,25 @@ void CGUIEditBox::fromAndroidEditText(const core::stringw& text, int start,
m_composing_end = composing_end;
}
}
+
+void CGUIEditBox::updateGlyphLayouts()
+{
+#ifndef SERVER_ONLY
+ // Clear any unsupported characters
+ m_edit_text.erase(std::remove(m_edit_text.begin(), m_edit_text.end(),
+ U'\r'), m_edit_text.end());
+ m_edit_text.erase(std::remove(m_edit_text.begin(), m_edit_text.end(),
+ U'\t'), m_edit_text.end());
+ m_edit_text.erase(std::remove(m_edit_text.begin(), m_edit_text.end(),
+ U'\n'), m_edit_text.end());
+ m_glyph_layouts.clear();
+ if (PasswordBox)
+ {
+ font_manager->shape(std::u32string(m_edit_text.size(), PasswordChar),
+ m_glyph_layouts);
+ }
+ else
+ font_manager->shape(m_edit_text, m_glyph_layouts);
+ Text = StringUtils::utf32ToWide(m_edit_text);
+#endif
+}
diff --git a/src/guiengine/widgets/CGUIEditBox.hpp b/src/guiengine/widgets/CGUIEditBox.hpp
index d6c04d2db..e818a25c7 100644
--- a/src/guiengine/widgets/CGUIEditBox.hpp
+++ b/src/guiengine/widgets/CGUIEditBox.hpp
@@ -11,10 +11,14 @@
#include "IOSOperator.h"
#include "utils/leak_check.hpp"
-#include "utils/time.hpp"
+#include "GlyphLayout.h"
+
+#include
+#include
using namespace irr;
using namespace gui;
+
namespace GUIEngine
{
enum TextBoxType: int;
@@ -28,7 +32,7 @@ namespace GUIEngine
//! constructor
CGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
- IGUIElement* parent, s32 id, const core::rect& rectangle, bool is_rtl);
+ IGUIElement* parent, s32 id, const core::rect& rectangle);
//! destructor
virtual ~CGUIEditBox();
@@ -54,20 +58,20 @@ namespace GUIEngine
virtual void setDrawBorder(bool border);
//! 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
//! \return true if word wrap is enabled, false otherwise
- virtual bool isWordWrapEnabled() const;
+ virtual bool isWordWrapEnabled() const { return false; }
//! Enables or disables newlines.
/** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
instead a newline character will be inserted. */
- virtual void setMultiLine(bool enable);
+ virtual void setMultiLine(bool enable) {}
//! Checks if multi line editing is enabled
//! \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
//! \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 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);
void openScreenKeyboard();
- s32 getCursorPosInBox() const { return CursorPos; }
- s32 getTextCount() const { return (s32)Text.size(); }
+ s32 getCursorPosInBox() const { return m_cursor_pos; }
+ s32 getTextCount() const { return (s32)m_edit_text.size(); }
void setTextBoxType(GUIEngine::TextBoxType t) { m_type = t; }
protected:
- //! Breaks the single text line.
- void breakText();
//! sets the area of the given 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
void inputChar(wchar_t c);
//! calculates the current scroll position
@@ -144,7 +144,7 @@ namespace GUIEngine
void sendGuiEvent(EGUI_EVENT_TYPE type);
//! set text markers
void setTextMarkers(s32 begin, s32 end);
-
+ void updateCursorDistance();
bool processKey(const SEvent& event);
bool processMouse(const SEvent& event);
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
@@ -155,12 +155,13 @@ namespace GUIEngine
core::position2di calculateICPos();
#endif
s32 getCursorPos(s32 x, s32 y);
+ void updateGlyphLayouts();
bool MouseMarking;
bool Border;
bool OverrideColorEnabled;
- s32 MarkBegin;
- s32 MarkEnd;
+ s32 m_mark_begin;
+ s32 m_mark_end;
GUIEngine::TextBoxType m_type;
@@ -168,20 +169,16 @@ namespace GUIEngine
gui::IGUIFont *OverrideFont, *LastBreakFont;
IOSOperator* Operator;
- StkTime::TimeType BlinkStartTime;
- s32 CursorPos;
- s32 HScrollPos, VScrollPos; // scroll position in characters
- u32 Max;
+ uint64_t m_force_show_cursor_time;
+ s32 m_cursor_pos;
+ s32 m_scroll_pos;
+ s32 m_cursor_distance;
+ u32 m_max_chars;
- bool m_rtl;
-
- bool WordWrap, MultiLine, AutoScroll, PasswordBox;
- wchar_t PasswordChar;
+ bool AutoScroll, PasswordBox;
+ char32_t PasswordChar;
EGUI_ALIGNMENT HAlign, VAlign;
- core::array< core::stringw > BrokenText;
- core::array< s32 > BrokenTextPositions;
-
core::rect CurrentTextRect, FrameRect; // temporary values
s32 m_composing_start;
@@ -190,6 +187,12 @@ namespace GUIEngine
/* If true, this editbox will copy text and selection only from
* android edittext, and process only mouse event. */
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 m_glyph_layouts;
+ void correctCursor(s32& cursor_pos, bool left);
};
diff --git a/src/guiengine/widgets/text_box_widget.cpp b/src/guiengine/widgets/text_box_widget.cpp
index 342c13393..66ee17a84 100644
--- a/src/guiengine/widgets/text_box_widget.cpp
+++ b/src/guiengine/widgets/text_box_widget.cpp
@@ -22,7 +22,6 @@
#include "guiengine/widgets/CGUIEditBox.hpp"
#include "utils/ptr_vector.hpp"
#include "utils/translation.hpp"
-#include "utils/utf8/unchecked.h"
#include
#include
@@ -39,9 +38,8 @@ public:
MyCGUIEditBox(const wchar_t* text, bool border, gui::IGUIEnvironment* environment,
gui:: IGUIElement* parent, s32 id, const core::rect& 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)
@@ -68,8 +66,7 @@ public:
if (m_listeners[n].onEnterPressed(Text))
{
handled = true;
- Text = L"";
- CursorPos = MarkBegin = MarkEnd = 0;
+ setText(L"");
}
}
return handled;
@@ -271,14 +268,8 @@ ANDROID_EDITTEXT_CALLBACK(ANDROID_PACKAGE_CALLBACK_NAME)
if (utf8_text == NULL)
return;
- // Use utf32 for emoji later
- static_assert(sizeof(wchar_t) == sizeof(uint32_t), "Invalid wchar size");
- std::vector utf32line;
- utf8::unchecked::utf8to32(utf8_text, utf8_text + strlen(utf8_text),
- back_inserter(utf32line));
- utf32line.push_back(0);
-
- core::stringw to_editbox(&utf32line[0]);
+ // Android use 32bit wchar_t
+ std::u32string to_editbox = StringUtils::utf8ToUtf32(utf8_text);
env->ReleaseStringUTFChars(text, utf8_text);
GUIEngine::addGUIFunctionBeforeRendering([widget_id, to_editbox, start,