1
0

Add a possibility to copy text from non-writable edit box (#118)

This commit is contained in:
Deve 2023-01-24 06:42:01 +01:00 committed by GitHub
parent 22443daf2c
commit 0df81d3e20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 168 additions and 94 deletions

View File

@ -50,7 +50,7 @@ android {
// get precompiled deps
task downloadDeps(type: Download) {
def VERSION = "05122022"
def VERSION = "17012023"
src "https://github.com/MultiCraft/deps_android/releases/download/$VERSION/deps_android.zip"
dest new File(buildDir, 'deps.zip')
overwrite false

View File

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "IGUIFont.h"
#include "porting.h"
#include "touchscreengui.h"
#include "util/string.h"
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
@ -181,6 +182,8 @@ void GUIEditBox::setTextMarkers(s32 begin, s32 end)
if (begin != m_mark_begin || end != m_mark_end) {
m_mark_begin = begin;
m_mark_end = end;
m_real_mark_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
m_real_mark_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
}
}
@ -207,10 +210,13 @@ bool GUIEditBox::OnEvent(const SEvent &event)
switch (event.EventType) {
case EET_GUI_EVENT:
if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
if (event.GUIEvent.Caller == this) {
m_mouse_marking = false;
setTextMarkers(0, 0);
}
#ifdef HAVE_TOUCHSCREENGUI
if (!TouchScreenGUI::isActive())
#endif
if (event.GUIEvent.Caller == this) {
m_mouse_marking = false;
setTextMarkers(0, 0);
}
}
break;
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
@ -255,6 +261,26 @@ bool GUIEditBox::OnEvent(const SEvent &event)
if (processMouse(event))
return true;
break;
#if defined(__ANDROID__) || defined(__IOS__)
case EET_TOUCH_INPUT_EVENT:
if (event.TouchInput.Event == irr::ETIE_PRESSED_LONG) {
if (!m_mouse_marking) {
m_long_press = true;
bool success = onKeyControlC(event);
#ifdef __ANDROID__
if (success)
SDL_AndroidShowToast(
"Copied to clipboard", 2,
-1, 0, 0);
#elif __IOS__
if (success)
porting::showToast("Copied to clipboard");
#endif
}
return true;
}
break;
#endif
default:
break;
}
@ -265,10 +291,6 @@ bool GUIEditBox::OnEvent(const SEvent &event)
bool GUIEditBox::processKey(const SEvent &event)
{
if (!m_writable) {
return false;
}
if (!event.KeyInput.PressedDown)
return false;
@ -290,7 +312,7 @@ bool GUIEditBox::processKey(const SEvent &event)
if (event.KeyInput.Control && !altPressed) {
// german backlash '\' entered with control + '?'
if (event.KeyInput.Char == '\\') {
if (m_writable && event.KeyInput.Char == '\\') {
inputChar(event.KeyInput.Char);
return true;
}
@ -305,10 +327,14 @@ bool GUIEditBox::processKey(const SEvent &event)
onKeyControlC(event);
break;
case KEY_KEY_X:
text_changed = onKeyControlX(event, new_mark_begin, new_mark_end);
if (m_writable)
text_changed = onKeyControlX(
event, new_mark_begin, new_mark_end);
break;
case KEY_KEY_V:
text_changed = onKeyControlV(event, new_mark_begin, new_mark_end);
if (m_writable)
text_changed = onKeyControlV(
event, new_mark_begin, new_mark_end);
break;
case KEY_HOME:
// move/highlight to start of text
@ -382,13 +408,16 @@ bool GUIEditBox::processKey(const SEvent &event)
m_blink_start_time = porting::getTimeMs();
} break;
case KEY_RETURN:
if (m_multiline) {
inputChar(L'\n');
} else {
calculateScrollPos();
sendGuiEvent(EGET_EDITBOX_ENTER);
if (m_writable) {
if (m_multiline) {
inputChar(L'\n');
} else {
calculateScrollPos();
sendGuiEvent(EGET_EDITBOX_ENTER);
}
return true;
}
return true;
break;
case KEY_LEFT:
if (event.KeyInput.Shift) {
if (m_cursor_pos > 0) {
@ -434,11 +463,15 @@ bool GUIEditBox::processKey(const SEvent &event)
}
break;
case KEY_BACK:
text_changed = onKeyBack(event, new_mark_begin, new_mark_end);
if (m_writable)
text_changed = onKeyBack(
event, new_mark_begin, new_mark_end);
break;
case KEY_DELETE:
text_changed = onKeyDelete(event, new_mark_begin, new_mark_end);
if (m_writable)
text_changed = onKeyDelete(
event, new_mark_begin, new_mark_end);
break;
case KEY_ESCAPE:
@ -472,8 +505,10 @@ bool GUIEditBox::processKey(const SEvent &event)
return false;
default:
inputChar(event.KeyInput.Char);
return true;
if (m_writable) {
inputChar(event.KeyInput.Char);
return true;
}
}
}
@ -496,8 +531,7 @@ bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end)
// clang-format off
if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
s32 lineNo = getLineFromPos(m_cursor_pos);
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
(m_mark_begin > m_mark_end ? m_mark_begin : m_mark_end);
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : m_real_mark_begin;
if (lineNo > 0) {
s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
if ((s32)m_broken_text[lineNo - 1].size() < cp) {
@ -528,8 +562,7 @@ bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
// clang-format off
if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
s32 lineNo = getLineFromPos(m_cursor_pos);
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
(m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end);
s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : m_real_mark_begin;
if (lineNo < (s32)m_broken_text.size() - 1) {
s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
if ((s32)m_broken_text[lineNo + 1].size() < cp) {
@ -555,17 +588,16 @@ bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
return false;
}
void GUIEditBox::onKeyControlC(const SEvent &event)
bool GUIEditBox::onKeyControlC(const SEvent &event)
{
// copy to clipboard
if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
return;
return false;
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;
std::string s = stringw_to_utf8(Text.subString(realmbgn, realmend - realmbgn));
std::string s = stringw_to_utf8(Text.subString(
m_real_mark_begin, m_real_mark_end - m_real_mark_begin));
m_operator->copyToClipboard(s.c_str());
return true;
}
bool GUIEditBox::onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_end)
@ -576,18 +608,15 @@ bool GUIEditBox::onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_e
if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
return false;
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;
// Now remove from box if enabled
if (isEnabled()) {
// delete
core::stringw s;
s = Text.subString(0, realmbgn);
s.append(Text.subString(realmend, Text.size() - realmend));
s = Text.subString(0, m_real_mark_begin);
s.append(Text.subString(m_real_mark_end, Text.size() - m_real_mark_end));
Text = s;
m_cursor_pos = realmbgn;
m_cursor_pos = m_real_mark_begin;
mark_begin = 0;
mark_end = 0;
return true;
@ -605,9 +634,6 @@ bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_e
if (!m_operator)
return false;
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
if (const c8 *p = m_operator->getTextFromClipboard()) {
core::stringw inserted_text = utf8_to_stringw(p);
@ -625,13 +651,14 @@ bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_e
} else {
// replace text
core::stringw s = Text.subString(0, realmbgn);
core::stringw s = Text.subString(0, m_real_mark_begin);
s.append(inserted_text);
s.append(Text.subString(realmend, Text.size() - realmend));
s.append(Text.subString(
m_real_mark_end, Text.size() - m_real_mark_end));
if (!m_max || s.size() <= m_max) {
Text = s;
m_cursor_pos = realmbgn + inserted_text.size();
m_cursor_pos = m_real_mark_begin + inserted_text.size();
}
}
}
@ -650,16 +677,11 @@ bool GUIEditBox::onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end)
if (m_mark_begin != m_mark_end) {
// delete 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;
s = Text.subString(0, realmbgn);
s.append(Text.subString(realmend, Text.size() - realmend));
s = Text.subString(0, m_real_mark_begin);
s.append(Text.subString(m_real_mark_end, Text.size() - m_real_mark_end));
Text = s;
m_cursor_pos = realmbgn;
m_cursor_pos = m_real_mark_begin;
} else {
// delete text behind cursor
if (m_cursor_pos > 0)
@ -688,16 +710,11 @@ bool GUIEditBox::onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end
if (m_mark_begin != m_mark_end) {
// delete 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;
s = Text.subString(0, realmbgn);
s.append(Text.subString(realmend, Text.size() - realmend));
s = Text.subString(0, m_real_mark_begin);
s.append(Text.subString(m_real_mark_end, Text.size() - m_real_mark_end));
Text = s;
m_cursor_pos = realmbgn;
m_cursor_pos = m_real_mark_begin;
} else {
// delete text before cursor
s = Text.subString(0, m_cursor_pos);
@ -727,14 +744,11 @@ void GUIEditBox::inputChar(wchar_t c)
if (m_mark_begin != m_mark_end) {
// clang-format off
// replace marked text
s32 real_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
s32 real_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
s = Text.subString(0, real_begin);
s = Text.subString(0, m_real_mark_begin);
s.append(c);
s.append(Text.subString(real_end, Text.size() - real_end));
s.append(Text.subString(m_real_mark_end, Text.size() - m_real_mark_end));
Text = s;
m_cursor_pos = real_begin + 1;
m_cursor_pos = m_real_mark_begin + 1;
// clang-format on
} else {
// add new character
@ -758,56 +772,106 @@ void GUIEditBox::inputChar(wchar_t c)
bool GUIEditBox::processMouse(const SEvent &event)
{
switch (event.MouseInput.Event) {
case irr::EMIE_LMOUSE_LEFT_UP:
case irr::EMIE_LMOUSE_LEFT_UP: {
s32 cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
#ifdef HAVE_TOUCHSCREENGUI
// Remove text markers for short tap in one place
if (TouchScreenGUI::isActive() && !m_long_press &&
m_cursor_press_pos == cursor_pos &&
Environment->hasFocus(this)) {
setTextMarkers(cursor_pos, cursor_pos);
}
#endif
m_cursor_press_pos = -1;
m_long_press = false;
if (Environment->hasFocus(this)) {
m_cursor_pos = getCursorPos(
event.MouseInput.X, event.MouseInput.Y);
if (m_mouse_marking) {
setTextMarkers(m_mark_begin, m_cursor_pos);
}
m_cursor_pos = cursor_pos;
m_mouse_marking = false;
calculateScrollPos();
return true;
}
break;
} break;
case irr::EMIE_MOUSE_MOVED: {
s32 cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
#ifdef HAVE_TOUCHSCREENGUI
// Start text marking when cursor was moved, so that user doesn't want
// to copy text.
if (TouchScreenGUI::isActive() && !m_long_press && !m_mouse_marking &&
m_cursor_press_pos != -1 &&
cursor_pos != m_cursor_press_pos &&
Environment->hasFocus(this)) {
m_mouse_marking = true;
int mark_length = m_real_mark_end - m_real_mark_begin;
if (mark_length > 2 &&
std::abs(m_cursor_press_pos - m_mark_begin) < 3)
setTextMarkers(m_mark_end, m_cursor_press_pos);
else if (mark_length > 2 &&
std::abs(m_cursor_press_pos - m_mark_end) < 3)
setTextMarkers(m_mark_begin, m_cursor_press_pos);
else
setTextMarkers(m_cursor_press_pos, m_cursor_press_pos);
}
#endif
if (m_mouse_marking) {
m_cursor_pos = getCursorPos(
event.MouseInput.X, event.MouseInput.Y);
m_cursor_pos = cursor_pos;
setTextMarkers(m_mark_begin, m_cursor_pos);
calculateScrollPos();
return true;
}
} break;
case EMIE_LMOUSE_PRESSED_DOWN:
case EMIE_LMOUSE_PRESSED_DOWN: {
m_long_press = false;
if (!Environment->hasFocus(this)) {
m_blink_start_time = porting::getTimeMs();
m_mouse_marking = true;
m_cursor_pos = getCursorPos(
event.MouseInput.X, event.MouseInput.Y);
setTextMarkers(m_cursor_pos, m_cursor_pos);
m_cursor_press_pos = m_cursor_pos;
#ifdef HAVE_TOUCHSCREENGUI
if (!TouchScreenGUI::isActive() ||
m_cursor_pos < m_real_mark_begin ||
m_cursor_pos > m_real_mark_end) {
#endif
m_mouse_marking = true;
setTextMarkers(m_cursor_pos, m_cursor_pos);
#ifdef HAVE_TOUCHSCREENGUI
}
#endif
calculateScrollPos();
return true;
} else {
if (!AbsoluteClippingRect.isPointInside(core::position2d<s32>(
event.MouseInput.X, event.MouseInput.Y))) {
m_cursor_press_pos = -1;
return false;
} else {
// move cursor
m_cursor_pos = getCursorPos(
event.MouseInput.X, event.MouseInput.Y);
m_cursor_press_pos = m_cursor_pos;
s32 newMarkBegin = m_mark_begin;
if (!m_mouse_marking)
newMarkBegin = m_cursor_pos;
#ifdef HAVE_TOUCHSCREENGUI
if (!TouchScreenGUI::isActive() ||
m_cursor_pos < m_real_mark_begin ||
m_cursor_pos > m_real_mark_end) {
#endif
s32 newMarkBegin = m_mark_begin;
if (!m_mouse_marking)
newMarkBegin = m_cursor_pos;
m_mouse_marking = true;
setTextMarkers(newMarkBegin, m_cursor_pos);
m_mouse_marking = true;
setTextMarkers(newMarkBegin, m_cursor_pos);
#ifdef HAVE_TOUCHSCREENGUI
}
#endif
calculateScrollPos();
return true;
}
}
} break;
case EMIE_MOUSE_WHEEL:
if (m_vscrollbar && m_vscrollbar->isVisible()) {
s32 pos = m_vscrollbar->getPos();

View File

@ -184,6 +184,7 @@ protected:
u32 m_blink_start_time = 0;
s32 m_cursor_pos = 0;
s32 m_cursor_press_pos = 0;
s32 m_hscroll_pos = 0;
s32 m_vscroll_pos = 0; // scroll position in characters
u32 m_max = 0;
@ -195,9 +196,12 @@ protected:
bool m_writable;
bool m_mouse_marking = false;
bool m_long_press = false;
s32 m_mark_begin = 0;
s32 m_mark_end = 0;
s32 m_real_mark_begin = 0;
s32 m_real_mark_end = 0;
gui::IGUIFont *m_last_break_font = nullptr;
IOSOperator *m_operator = nullptr;
@ -212,7 +216,7 @@ private:
bool onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end);
bool onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end);
void onKeyControlC(const SEvent &event);
bool onKeyControlC(const SEvent &event);
bool onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_end);
bool onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_end);
bool onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end);

View File

@ -83,6 +83,7 @@ void GUIEditBoxWithScrollBar::draw()
return;
const bool focus = Environment->hasFocus(this);
const bool scollbar_focus = Environment->hasFocus(m_vscrollbar);
IGUISkin* skin = Environment->getSkin();
if (!skin)
@ -138,10 +139,8 @@ void GUIEditBoxWithScrollBar::draw()
// get mark position
const bool ml = (!m_passwordbox && (m_word_wrap || m_multiline));
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 hline_start = ml ? getLineFromPos(realmbgn) : 0;
const s32 hline_count = ml ? getLineFromPos(realmend) - hline_start + 1 : 1;
const s32 hline_start = ml ? getLineFromPos(m_real_mark_begin) : 0;
const s32 hline_count = ml ? getLineFromPos(m_real_mark_end) - hline_start + 1 : 1;
const s32 line_count = ml ? m_broken_text.size() : 1;
// Save the override color information.
@ -191,26 +190,26 @@ void GUIEditBoxWithScrollBar::draw()
false, true, &local_clip_rect);
// draw mark and marked text
if (focus && m_mark_begin != m_mark_end && i >= hline_start && i < hline_start + hline_count) {
if ((focus || scollbar_focus) && m_mark_begin != m_mark_end && i >= hline_start && i < hline_start + hline_count) {
s32 mbegin = 0, mend = 0;
s32 lineStartPos = 0, lineEndPos = txt_line->size();
if (i == hline_start) {
// highlight start is on this line
s = txt_line->subString(0, realmbgn - start_pos);
s = txt_line->subString(0, m_real_mark_begin - start_pos);
mbegin = font->getDimension(s.c_str()).Width;
// deal with kerning
mbegin += font->getKerningWidth(
&((*txt_line)[realmbgn - start_pos]),
realmbgn - start_pos > 0 ? &((*txt_line)[realmbgn - start_pos - 1]) : 0);
&((*txt_line)[m_real_mark_begin - start_pos]),
m_real_mark_begin - start_pos > 0 ? &((*txt_line)[m_real_mark_begin - start_pos - 1]) : 0);
lineStartPos = realmbgn - start_pos;
lineStartPos = m_real_mark_begin - start_pos;
}
if (i == hline_start + hline_count - 1) {
// highlight end is on this line
s2 = txt_line->subString(0, realmend - start_pos);
s2 = txt_line->subString(0, m_real_mark_end - start_pos);
mend = font->getDimension(s2.c_str()).Width;
lineEndPos = (s32)s2.size();
} else {

View File

@ -325,6 +325,13 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
enter(hovered);
}
gui::IGUIElement *focused = Environment->getFocus();
#if defined(__ANDROID__) || defined(__IOS__)
if (event.TouchInput.Event == ETIE_PRESSED_LONG) {
if (focused->getType() == irr::gui::EGUIET_EDIT_BOX)
focused->OnEvent(event);
return true;
}
#endif
SEvent mouse_event;
if (!convertToMouseEvent(mouse_event, event.TouchInput.Event))
return false;