1
0
Fork 0

Chat console fixes (#133)

* Some fixes for text selection in chat.

- Reset selection on chat open
- Reset selection on new message
- Reset selection when window size changed

* Don't move to bottom when user is scrolling chat

* Some work on prompt selection

* Make it working when text length is larger than max visible length

* Handle touch events for prompt

* Avoid moving chat history on new messages during scrolling.

Also simplify selection marks comparing.

* Move scroll to bottom on sending new message when console is opened without close on enter

* Fixed copy to clipboard when chat buffer is full and also keep text selected when new message arrives

* Some improvements for prompt selection

* Delete selected text on new input or backspace key press

* Fixed text selection and cursor pos for regular font

* Fixed drawing prompt with newlines

* Fixed empty prompt selection with ctrl+a

* Enable chat console on android tablets
minenux
Deve 2023-05-05 21:13:08 +02:00 committed by mckaygerhard
parent f58cf228ae
commit 8cd48bd34f
5 changed files with 634 additions and 105 deletions

View File

@ -42,13 +42,14 @@ void ChatBuffer::addLine(const std::wstring &name, const std::wstring &text)
{ {
m_lines_modified = true; m_lines_modified = true;
ChatLine line(name, text); ChatLine line(name, text, m_current_line_index);
m_unformatted.push_back(line); m_unformatted.push_back(line);
m_current_line_index++;
if (m_rows > 0) { if (m_rows > 0) {
// m_formatted is valid and must be kept valid // m_formatted is valid and must be kept valid
bool scrolled_at_bottom = (m_scroll == getBottomScrollPos()); bool scrolled_at_bottom = (m_scroll == getBottomScrollPos());
u32 num_added = formatChatLine(line, m_unformatted.size() - 1, m_cols, m_formatted); u32 num_added = formatChatLine(line, m_cols, m_formatted);
if (scrolled_at_bottom) if (scrolled_at_bottom)
m_scroll += num_added; m_scroll += num_added;
} }
@ -63,6 +64,7 @@ void ChatBuffer::clear()
{ {
m_unformatted.clear(); m_unformatted.clear();
m_formatted.clear(); m_formatted.clear();
m_current_line_index = 0;
m_scroll = 0; m_scroll = 0;
m_lines_modified = true; m_lines_modified = true;
} }
@ -117,6 +119,8 @@ void ChatBuffer::deleteOldest(u32 count)
m_scroll = getBottomScrollPos(); m_scroll = getBottomScrollPos();
else else
scrollAbsolute(m_scroll - del_formatted); scrollAbsolute(m_scroll - del_formatted);
m_del_formatted += del_formatted;
} }
void ChatBuffer::deleteByAge(f32 maxAge) void ChatBuffer::deleteByAge(f32 maxAge)
@ -173,7 +177,7 @@ void ChatBuffer::reformat(u32 cols, u32 rows)
{ {
if (i == restore_scroll_unformatted) if (i == restore_scroll_unformatted)
restore_scroll_formatted = m_formatted.size(); restore_scroll_formatted = m_formatted.size();
formatChatLine(m_unformatted[i], i, cols, m_formatted); formatChatLine(m_unformatted[i], cols, m_formatted);
} }
} }
@ -224,7 +228,7 @@ void ChatBuffer::scrollBottom()
m_scroll = getBottomScrollPos(); m_scroll = getBottomScrollPos();
} }
u32 ChatBuffer::formatChatLine(const ChatLine& line, int line_index, u32 cols, u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
std::vector<ChatFormattedLine>& destination) const std::vector<ChatFormattedLine>& destination) const
{ {
u32 num_added = 0; u32 num_added = 0;
@ -267,7 +271,7 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, int line_index, u32 cols,
//EnrichedString line_text(line.text); //EnrichedString line_text(line.text);
next_line.first = true; next_line.first = true;
next_line.line_index = line_index; next_line.line_index = line.line_index;
bool text_processing = false; bool text_processing = false;
// Produce fragments and layout them into lines // Produce fragments and layout them into lines
@ -391,6 +395,7 @@ void ChatPrompt::input(wchar_t ch)
clampView(); clampView();
m_nick_completion_start = 0; m_nick_completion_start = 0;
m_nick_completion_end = 0; m_nick_completion_end = 0;
m_line_modified = true;
} }
void ChatPrompt::input(const std::wstring &str) void ChatPrompt::input(const std::wstring &str)
@ -400,6 +405,7 @@ void ChatPrompt::input(const std::wstring &str)
clampView(); clampView();
m_nick_completion_start = 0; m_nick_completion_start = 0;
m_nick_completion_end = 0; m_nick_completion_end = 0;
m_line_modified = true;
} }
void ChatPrompt::addToHistory(const std::wstring &line) void ChatPrompt::addToHistory(const std::wstring &line)
@ -424,6 +430,7 @@ void ChatPrompt::clear()
m_cursor = 0; m_cursor = 0;
m_nick_completion_start = 0; m_nick_completion_start = 0;
m_nick_completion_end = 0; m_nick_completion_end = 0;
m_line_modified = true;
} }
std::wstring ChatPrompt::replace(const std::wstring &line) std::wstring ChatPrompt::replace(const std::wstring &line)
@ -434,6 +441,7 @@ std::wstring ChatPrompt::replace(const std::wstring &line)
clampView(); clampView();
m_nick_completion_start = 0; m_nick_completion_start = 0;
m_nick_completion_end = 0; m_nick_completion_end = 0;
m_line_modified = true;
return old_line; return old_line;
} }
@ -535,6 +543,7 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
clampView(); clampView();
m_nick_completion_start = prefix_start; m_nick_completion_start = prefix_start;
m_nick_completion_end = prefix_end; m_nick_completion_end = prefix_end;
m_line_modified = true;
} }
void ChatPrompt::reformat(u32 cols) void ChatPrompt::reformat(u32 cols)
@ -616,6 +625,7 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
m_line.erase(m_cursor, abs(new_cursor - old_cursor)); m_line.erase(m_cursor, abs(new_cursor - old_cursor));
} }
m_cursor_len = 0; m_cursor_len = 0;
m_line_modified = true;
break; break;
case CURSOROP_SELECT: case CURSOROP_SELECT:
if (scope == CURSOROP_SCOPE_LINE) { if (scope == CURSOROP_SCOPE_LINE) {
@ -635,6 +645,25 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
m_nick_completion_end = 0; m_nick_completion_end = 0;
} }
void ChatPrompt::setCursorPos(int cursor_pos)
{
s32 length = m_line.size();
m_cursor = MYMAX(MYMIN(cursor_pos, length), 0);
m_cursor_len = 0;
clampView();
m_nick_completion_start = 0;
m_nick_completion_end = 0;
}
void ChatPrompt::setViewPosition(int view)
{
m_view = view;
clampView();
}
void ChatPrompt::clampView() void ChatPrompt::clampView()
{ {
s32 length = m_line.size(); s32 length = m_line.size();

View File

@ -37,16 +37,22 @@ struct ChatLine
EnrichedString name; EnrichedString name;
// message text // message text
EnrichedString text; EnrichedString text;
// Line index in ChatLine buffer
int line_index;
ChatLine(const std::wstring &a_name, const std::wstring &a_text): ChatLine(const std::wstring &a_name, const std::wstring &a_text,
int a_line_index):
name(a_name), name(a_name),
text(a_text) text(a_text),
line_index(a_line_index)
{ {
} }
ChatLine(const EnrichedString &a_name, const EnrichedString &a_text): ChatLine(const EnrichedString &a_name, const EnrichedString &a_text,
int a_line_index):
name(a_name), name(a_name),
text(a_text) text(a_text),
line_index(a_line_index)
{ {
} }
}; };
@ -123,10 +129,13 @@ public:
bool getLinesModified() const { return m_lines_modified; } bool getLinesModified() const { return m_lines_modified; }
void resetLinesModified() { m_lines_modified = false; } void resetLinesModified() { m_lines_modified = false; }
u32 getDelFormatted() const { return m_del_formatted; }
void resetDelFormatted() { m_del_formatted = 0; }
// Format a chat line for the given number of columns. // Format a chat line for the given number of columns.
// Appends the formatted lines to the destination array and // Appends the formatted lines to the destination array and
// returns the number of formatted lines. // returns the number of formatted lines.
u32 formatChatLine(const ChatLine& line, int line_index, u32 cols, u32 formatChatLine(const ChatLine& line, u32 cols,
std::vector<ChatFormattedLine>& destination) const; std::vector<ChatFormattedLine>& destination) const;
void resize(u32 scrollback); void resize(u32 scrollback);
@ -155,6 +164,11 @@ private:
// Is always set to true when m_unformatted is modified, because that's what // Is always set to true when m_unformatted is modified, because that's what
// determines the output of getLineCount() and getLine() // determines the output of getLineCount() and getLine()
bool m_lines_modified = true; bool m_lines_modified = true;
// How many formatted lines have been deleted
u32 m_del_formatted = 0;
int m_current_line_index = 0;
}; };
class ChatPrompt class ChatPrompt
@ -196,6 +210,8 @@ public:
std::wstring getVisiblePortion() const; std::wstring getVisiblePortion() const;
// Get cursor position (relative to visible portion). -1 if invalid // Get cursor position (relative to visible portion). -1 if invalid
s32 getVisibleCursorPosition() const; s32 getVisibleCursorPosition() const;
// Get view position (absolute value)
s32 getViewPosition() const { return m_view; }
// Get length of cursor selection // Get length of cursor selection
s32 getCursorLength() const { return m_cursor_len; } s32 getCursorLength() const { return m_cursor_len; }
@ -231,6 +247,14 @@ public:
// deletes the word to the left of the cursor. // deletes the word to the left of the cursor.
void cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope scope); void cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope scope);
void setCursorPos(int cursor_pos);
void setViewPosition(int view);
// Functions for keeping track of whether the line was modified by any
// preceding operations
bool getLineModified() const { return m_line_modified; }
void resetLineModified() { m_line_modified = false; }
protected: protected:
// set m_view to ensure that 0 <= m_view <= m_cursor < m_view + m_cols // set m_view to ensure that 0 <= m_view <= m_cursor < m_view + m_cols
// if line can be fully shown, set m_view to zero // if line can be fully shown, set m_view to zero
@ -262,6 +286,9 @@ private:
s32 m_nick_completion_start = 0; s32 m_nick_completion_start = 0;
// Last nick completion start (index into m_line) // Last nick completion start (index into m_line)
s32 m_nick_completion_end = 0; s32 m_nick_completion_end = 0;
// True if line was modified
bool m_line_modified = true;
}; };
class ChatBackend class ChatBackend

View File

@ -2141,13 +2141,11 @@ void Game::openConsole(float scale, const wchar_t *line)
porting::showInputDialog("", "", 2); porting::showInputDialog("", "", 2);
gui_chat_console->setAndroidChatOpen(true); gui_chat_console->setAndroidChatOpen(true);
} }
#endif
#if defined(__ANDROID__)
return;
#elif defined(__IOS__)
if (!g_settings->getBool("device_is_tablet")) if (!g_settings->getBool("device_is_tablet"))
return; return;
#endif #endif
if (gui_chat_console->isOpenInhibited()) if (gui_chat_console->isOpenInhibited())
return; return;
gui_chat_console->openConsole(scale); gui_chat_console->openConsole(scale);
@ -2991,6 +2989,13 @@ void Game::updateChat(f32 dtime)
if (buf.getLinesModified()) { if (buf.getLinesModified()) {
buf.resetLinesModified(); buf.resetLinesModified();
m_game_ui->setChatText(chat_backend->getRecentChat(), buf.getLineCount()); m_game_ui->setChatText(chat_backend->getRecentChat(), buf.getLineCount());
gui_chat_console->onLinesModified();
}
auto &prompt = chat_backend->getPrompt();
if (prompt.getLineModified()) {
prompt.resetLineModified();
gui_chat_console->onPromptModified();
} }
// Make sure that the size is still correct // Make sure that the size is still correct

View File

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/fontengine.h" #include "client/fontengine.h"
#include "log.h" #include "log.h"
#include "gettext.h" #include "gettext.h"
#include <algorithm>
#include <string> #include <string>
#include "touchscreengui.h" #include "touchscreengui.h"
@ -132,6 +133,7 @@ void GUIChatConsole::openConsole(f32 scale)
m_desired_height = scale * m_screensize.Y; m_desired_height = scale * m_screensize.Y;
reformatConsole(); reformatConsole();
updateVScrollBar(false, true);
m_animate_time_old = porting::getTimeMs(); m_animate_time_old = porting::getTimeMs();
IGUIElement::setVisible(true); IGUIElement::setVisible(true);
m_vscrollbar->setVisible(true); m_vscrollbar->setVisible(true);
@ -228,6 +230,7 @@ void GUIChatConsole::draw()
m_screensize = screensize; m_screensize = screensize;
m_desired_height = m_desired_height_fraction * m_screensize.Y; m_desired_height = m_desired_height_fraction * m_screensize.Y;
reformatConsole(); reformatConsole();
updateVScrollBar(true, false);
} }
// Animation // Animation
@ -255,7 +258,12 @@ void GUIChatConsole::reformatConsole()
recalculateConsolePosition(); recalculateConsolePosition();
m_chat_backend->reformat(cols, rows); m_chat_backend->reformat(cols, rows);
updateVScrollBar(); m_mark_begin.reset();
m_mark_end.reset();
m_cursor_press_pos.reset();
m_history_marking = false;
m_prompt_marking = false;
m_long_press = false;
} }
void GUIChatConsole::recalculateConsolePosition() void GUIChatConsole::recalculateConsolePosition()
@ -367,6 +375,8 @@ void GUIChatConsole::drawText()
ChatSelection real_mark_begin = m_mark_end > m_mark_begin ? m_mark_begin : m_mark_end; ChatSelection real_mark_begin = m_mark_end > m_mark_begin ? m_mark_begin : m_mark_end;
ChatSelection real_mark_end = m_mark_end > m_mark_begin ? m_mark_end : m_mark_begin; ChatSelection real_mark_end = m_mark_end > m_mark_begin ? m_mark_end : m_mark_begin;
if (real_mark_begin != real_mark_end && if (real_mark_begin != real_mark_end &&
real_mark_begin.selection_type == ChatSelection::SELECTION_HISTORY &&
real_mark_end.selection_type == ChatSelection::SELECTION_HISTORY &&
(s32)row + scroll_pos >= real_mark_begin.row + real_mark_begin.scroll && (s32)row + scroll_pos >= real_mark_begin.row + real_mark_begin.scroll &&
(s32)row + scroll_pos <= real_mark_end.row + real_mark_end.scroll) { (s32)row + scroll_pos <= real_mark_end.row + real_mark_end.scroll) {
ChatFormattedFragment fragment_first = line.fragments[0]; ChatFormattedFragment fragment_first = line.fragments[0];
@ -454,24 +464,62 @@ void GUIChatConsole::drawPrompt()
ChatPrompt& prompt = m_chat_backend->getPrompt(); ChatPrompt& prompt = m_chat_backend->getPrompt();
std::wstring prompt_text = prompt.getVisiblePortion(); std::wstring prompt_text = prompt.getVisiblePortion();
std::replace_if(prompt_text.begin(), prompt_text.end(),
[](wchar_t c) { return (c == L'\n' || c == L'\r'); }, L' ');
// FIXME Draw string at once, not character by character ChatSelection real_mark_begin = m_mark_end > m_mark_begin ? m_mark_begin : m_mark_end;
// That will only work with the cursor once we have a monospace font ChatSelection real_mark_end = m_mark_end > m_mark_begin ? m_mark_end : m_mark_begin;
for (u32 i = 0; i < prompt_text.size(); ++i)
{ if (real_mark_begin != real_mark_end &&
wchar_t ws[2] = {prompt_text[i], 0}; real_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
s32 x = (1 + i) * m_fontsize.X; real_mark_end.selection_type == ChatSelection::SELECTION_PROMPT) {
core::rect<s32> destrect( std::wstring begin_text = L"]";
x, y, x + m_fontsize.X, y + m_fontsize.Y); int begin_text_size = m_font->getDimension(begin_text.c_str()).Width;
m_font->draw( int text_pos = m_fontsize.X + begin_text_size;
ws,
destrect, s32 x_begin = text_pos;
video::SColor(255, 255, 255, 255), s32 text_size = m_font->getDimension(prompt_text.c_str()).Width;
false, s32 x_end = x_begin + text_size;
false,
&AbsoluteClippingRect); int current_scroll = prompt.getViewPosition();
if (real_mark_begin.character > 0) {
irr::core::stringw text = prompt_text.c_str();
int scroll_offset = real_mark_begin.scroll - current_scroll;
int length = scroll_offset + real_mark_begin.character;
length = MYMIN(MYMAX(length, 0), prompt_text.size() - 1);
text = text.subString(1, length);
s32 text_size = m_font->getDimension(text.c_str()).Width;
x_begin = text_pos + text_size;
}
if (real_mark_end.character < prompt_text.size() - 1) {
irr::core::stringw text = prompt_text.c_str();
int scroll_offset = real_mark_end.scroll - current_scroll;
int length = scroll_offset + real_mark_end.character;
if (real_mark_end.x_max)
length++;
length = MYMIN(MYMAX(length, 0), prompt_text.size() - 1);
text = text.subString(1, length);
s32 text_size = m_font->getDimension(text.c_str()).Width;
x_end = text_pos + text_size;
}
core::rect<s32> destrect(x_begin, y, x_end, y + m_fontsize.Y);
video::IVideoDriver* driver = Environment->getVideoDriver();
IGUISkin* skin = Environment->getSkin();
driver->draw2DRectangle(skin->getColor(EGDC_HIGH_LIGHT), destrect, &AbsoluteClippingRect);
} }
core::rect<s32> destrect(
m_fontsize.X, y, prompt_text.size() * m_fontsize.X, y + m_fontsize.Y);
m_font->draw(
prompt_text.c_str(),
destrect,
video::SColor(255, 255, 255, 255),
false,
false,
&AbsoluteClippingRect);
// Draw the cursor during on periods // Draw the cursor during on periods
if ((m_cursor_blink & 0x8000) != 0) if ((m_cursor_blink & 0x8000) != 0)
{ {
@ -480,7 +528,9 @@ void GUIChatConsole::drawPrompt()
{ {
s32 cursor_len = prompt.getCursorLength(); s32 cursor_len = prompt.getCursorLength();
video::IVideoDriver* driver = Environment->getVideoDriver(); video::IVideoDriver* driver = Environment->getVideoDriver();
s32 x = (1 + cursor_pos) * m_fontsize.X; std::wstring text = prompt_text.substr(0, cursor_pos);
s32 x = m_font->getDimension(text.c_str()).Width + m_fontsize.X;
core::rect<s32> destrect( core::rect<s32> destrect(
x, x,
y + m_fontsize.Y * (1.0 - m_cursor_height), y + m_fontsize.Y * (1.0 - m_cursor_height),
@ -494,7 +544,6 @@ void GUIChatConsole::drawPrompt()
&AbsoluteClippingRect); &AbsoluteClippingRect);
} }
} }
} }
@ -507,7 +556,7 @@ ChatSelection GUIChatConsole::getCursorPos(s32 x, s32 y)
ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
selection.scroll = buf.getScrollPos(); selection.scroll = buf.getScrollPos();
selection.initialized = true; selection.selection_type = ChatSelection::SELECTION_HISTORY;
s32 line_height = m_fontsize.Y; s32 line_height = m_fontsize.Y;
s32 y_min = m_height - m_desired_height; s32 y_min = m_height - m_desired_height;
@ -533,7 +582,7 @@ ChatSelection GUIChatConsole::getCursorPos(s32 x, s32 y)
} }
ChatFormattedLine line = buf.getFormattedLine(selection.row); ChatFormattedLine line = buf.getFormattedLine(selection.row);
selection.row_buf = line.line_index; selection.line_index = line.line_index;
int current_row = selection.row; int current_row = selection.row;
while (!line.first) { while (!line.first) {
@ -585,6 +634,58 @@ ChatSelection GUIChatConsole::getCursorPos(s32 x, s32 y)
return selection; return selection;
} }
ChatSelection GUIChatConsole::getPromptCursorPos(s32 x, s32 y)
{
ChatSelection selection;
if (m_font == NULL)
return selection;
ChatPrompt& prompt = m_chat_backend->getPrompt();
selection.selection_type = ChatSelection::SELECTION_PROMPT;
selection.scroll = prompt.getViewPosition();
std::wstring prompt_text = prompt.getVisiblePortion();
irr::core::stringw text = prompt_text.c_str();
text = text.subString(1, prompt_text.size() - 1);
std::wstring begin_text = L"]";
int begin_text_size = m_font->getDimension(begin_text.c_str()).Width;
int text_pos = m_fontsize.X + begin_text_size;
int pos = m_font->getCharacterFromPos(text.c_str(), x - text_pos);
if (pos == -1) {
selection.x_max = true;
selection.character = text.size() - 1;
} else {
selection.character = pos;
}
return selection;
}
ChatSelection GUIChatConsole::getCurrentPromptCursorPos()
{
ChatSelection selection;
if (m_font == NULL)
return selection;
ChatPrompt& prompt = m_chat_backend->getPrompt();
selection.selection_type = ChatSelection::SELECTION_PROMPT;
selection.scroll = prompt.getViewPosition();
selection.character = prompt.getVisibleCursorPosition() - 1;
if ((unsigned int)selection.character > prompt.getLine().size() - selection.scroll - 1) {
selection.character--;
selection.x_max = true;
}
return selection;
}
irr::core::stringc GUIChatConsole::getSelectedText() irr::core::stringc GUIChatConsole::getSelectedText()
{ {
if (m_font == NULL) if (m_font == NULL)
@ -601,11 +702,19 @@ irr::core::stringc GUIChatConsole::getSelectedText()
ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
for (int row = real_mark_begin.row_buf; row < real_mark_end.row_buf + 1; row++) { const ChatLine& first_line = buf.getLine(0);
int first_line_index = first_line.line_index;
int mark_begin_row_buf = real_mark_begin.line_index - first_line_index;
int mark_end_row_buf = real_mark_end.line_index - first_line_index;
if (mark_begin_row_buf < 0 || mark_end_row_buf < 0)
return "";
for (int row = mark_begin_row_buf; row < mark_end_row_buf + 1; row++) {
const ChatLine& line = buf.getLine(row); const ChatLine& line = buf.getLine(row);
std::vector<ChatFormattedLine> formatted_lines; std::vector<ChatFormattedLine> formatted_lines;
buf.formatChatLine(line, 0, buf.getColsCount(), formatted_lines); buf.formatChatLine(line, buf.getColsCount(), formatted_lines);
for (unsigned int i = 0; i < formatted_lines.size(); i++) { for (unsigned int i = 0; i < formatted_lines.size(); i++) {
const ChatFormattedLine &line = formatted_lines[i]; const ChatFormattedLine &line = formatted_lines[i];
@ -615,7 +724,7 @@ irr::core::stringc GUIChatConsole::getSelectedText()
for (unsigned int k = 0; k < fragment.text.size(); k++) { for (unsigned int k = 0; k < fragment.text.size(); k++) {
if (!add_to_string && if (!add_to_string &&
row == real_mark_begin.row_buf && row == mark_begin_row_buf &&
i == real_mark_begin.line && i == real_mark_begin.line &&
j == real_mark_begin.fragment && j == real_mark_begin.fragment &&
k == real_mark_begin.character) { k == real_mark_begin.character) {
@ -626,7 +735,7 @@ irr::core::stringc GUIChatConsole::getSelectedText()
} }
if (add_to_string) { if (add_to_string) {
if (row == real_mark_end.row_buf && if (row == mark_end_row_buf &&
i == real_mark_end.line && i == real_mark_end.line &&
j == real_mark_end.fragment && j == real_mark_end.fragment &&
k == real_mark_end.character) { k == real_mark_end.character) {
@ -643,7 +752,7 @@ irr::core::stringc GUIChatConsole::getSelectedText()
} }
} }
if (row < real_mark_end.row_buf) { if (row < mark_end_row_buf) {
text += L"\n"; text += L"\n";
} }
} }
@ -654,6 +763,80 @@ irr::core::stringc GUIChatConsole::getSelectedText()
return text_c; return text_c;
} }
irr::core::stringc GUIChatConsole::getPromptSelectedText()
{
if (m_font == NULL)
return "";
if (m_mark_begin == m_mark_end)
return "";
ChatPrompt& prompt = m_chat_backend->getPrompt();
ChatSelection real_mark_begin = m_mark_end > m_mark_begin ? m_mark_begin : m_mark_end;
ChatSelection real_mark_end = m_mark_end > m_mark_begin ? m_mark_end : m_mark_begin;
std::wstring prompt_text = prompt.getLine();
if (real_mark_end.scroll + real_mark_end.character > prompt_text.size())
return "";
irr::core::stringw text = prompt_text.c_str();
int begin = real_mark_begin.scroll + real_mark_begin.character;
int length = real_mark_end.scroll + real_mark_end.character - begin;
if (real_mark_end.x_max)
length++;
text = text.subString(begin, length);
irr::core::stringc text_c;
text_c = wide_to_utf8(text.c_str()).c_str();
return text_c;
}
void GUIChatConsole::movePromptCursor(s32 x, s32 y)
{
ChatSelection selection = getPromptCursorPos(x, y);
int cursor_pos = selection.scroll + selection.character;
if (selection.x_max)
cursor_pos++;
ChatPrompt& prompt = m_chat_backend->getPrompt();
prompt.setCursorPos(cursor_pos);
}
void GUIChatConsole::deletePromptSelection()
{
if (m_mark_begin.selection_type != ChatSelection::SELECTION_PROMPT ||
m_mark_end.selection_type != ChatSelection::SELECTION_PROMPT ||
m_mark_begin == m_mark_end)
return;
ChatPrompt& prompt = m_chat_backend->getPrompt();
int scroll_pos = prompt.getViewPosition();
ChatSelection real_mark_begin = m_mark_end > m_mark_begin ? m_mark_begin : m_mark_end;
ChatSelection real_mark_end = m_mark_end > m_mark_begin ? m_mark_end : m_mark_begin;
int pos_begin = real_mark_begin.scroll + real_mark_begin.character;
int pos_end = real_mark_end.scroll + real_mark_end.character;
if (real_mark_end.x_max)
pos_end++;
std::wstring prompt_text = prompt.getLine();
std::wstring new_text;
new_text = prompt_text.substr(0, pos_begin);
new_text += prompt_text.substr(pos_end, prompt_text.size() - pos_end);
prompt.replace(new_text);
int cursor_pos = real_mark_begin.scroll + real_mark_begin.character;
prompt.setCursorPos(cursor_pos);
prompt.setViewPosition(scroll_pos);
m_mark_begin.reset();
m_mark_end.reset();
}
bool GUIChatConsole::OnEvent(const SEvent& event) bool GUIChatConsole::OnEvent(const SEvent& event)
{ {
@ -702,6 +885,8 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
if (m_close_on_enter) { if (m_close_on_enter) {
closeConsole(); closeConsole();
m_close_on_enter = false; m_close_on_enter = false;
} else {
updateVScrollBar(true, true);
} }
return true; return true;
} }
@ -721,11 +906,11 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT) else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
{ {
ChatSelection old_pos = getCurrentPromptCursorPos();
// Left/right pressed // Left/right pressed
// Move/select character/word to the left depending on control and shift keys // Move/select character/word to the left depending on control and shift keys
ChatPrompt::CursorOp op = event.KeyInput.Shift ? ChatPrompt::CursorOp op = ChatPrompt::CURSOROP_MOVE;
ChatPrompt::CURSOROP_SELECT :
ChatPrompt::CURSOROP_MOVE;
ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ? ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
ChatPrompt::CURSOROP_DIR_LEFT : ChatPrompt::CURSOROP_DIR_LEFT :
ChatPrompt::CURSOROP_DIR_RIGHT; ChatPrompt::CURSOROP_DIR_RIGHT;
@ -733,30 +918,86 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
ChatPrompt::CURSOROP_SCOPE_WORD : ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER; ChatPrompt::CURSOROP_SCOPE_CHARACTER;
prompt.cursorOperation(op, dir, scope); prompt.cursorOperation(op, dir, scope);
if (event.KeyInput.Shift) {
if (m_mark_begin.selection_type != ChatSelection::SELECTION_PROMPT ||
m_mark_end.selection_type != ChatSelection::SELECTION_PROMPT) {
m_mark_begin = old_pos;
}
m_mark_end = getCurrentPromptCursorPos();
} else {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT) {
m_mark_begin.reset();
m_mark_end.reset();
}
}
return true; return true;
} }
else if(event.KeyInput.Key == KEY_HOME) else if(event.KeyInput.Key == KEY_HOME)
{ {
ChatSelection old_pos = getCurrentPromptCursorPos();
// Home pressed // Home pressed
// move to beginning of line // move to beginning of line
prompt.cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
if (event.KeyInput.Shift) {
if (m_mark_begin.selection_type != ChatSelection::SELECTION_PROMPT ||
m_mark_end.selection_type != ChatSelection::SELECTION_PROMPT) {
m_mark_begin = old_pos;
}
m_mark_end = getCurrentPromptCursorPos();
} else {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT) {
m_mark_begin.reset();
m_mark_end.reset();
}
}
return true; return true;
} }
else if(event.KeyInput.Key == KEY_END) else if(event.KeyInput.Key == KEY_END)
{ {
ChatSelection old_pos = getCurrentPromptCursorPos();
// End pressed // End pressed
// move to end of line // move to end of line
prompt.cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
if (event.KeyInput.Shift) {
if (m_mark_begin.selection_type != ChatSelection::SELECTION_PROMPT ||
m_mark_end.selection_type != ChatSelection::SELECTION_PROMPT) {
m_mark_begin = old_pos;
}
m_mark_end = getCurrentPromptCursorPos();
} else {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT) {
m_mark_begin.reset();
m_mark_end.reset();
}
}
return true; return true;
} }
else if(event.KeyInput.Key == KEY_BACK) else if(event.KeyInput.Key == KEY_BACK)
{ {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_begin != m_mark_end) {
deletePromptSelection();
return true;
}
// Backspace or Ctrl-Backspace pressed // Backspace or Ctrl-Backspace pressed
// delete character / word to the left // delete character / word to the left
ChatPrompt::CursorOpScope scope = ChatPrompt::CursorOpScope scope =
@ -771,6 +1012,13 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
else if(event.KeyInput.Key == KEY_DELETE) else if(event.KeyInput.Key == KEY_DELETE)
{ {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_begin != m_mark_end) {
deletePromptSelection();
return true;
}
// Delete or Ctrl-Delete pressed // Delete or Ctrl-Delete pressed
// delete character / word to the right // delete character / word to the right
ChatPrompt::CursorOpScope scope = ChatPrompt::CursorOpScope scope =
@ -785,20 +1033,42 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control) else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
{ {
// Ctrl-A pressed if (prompt.getLine().size() > 0) {
// Select all text ChatPrompt& prompt = m_chat_backend->getPrompt();
prompt.cursorOperation(
ChatPrompt::CURSOROP_SELECT, m_mark_begin.reset();
ChatPrompt::CURSOROP_DIR_LEFT, // Ignored m_mark_begin.selection_type = ChatSelection::SELECTION_PROMPT;
ChatPrompt::CURSOROP_SCOPE_LINE); m_mark_begin.scroll = 0;
m_mark_begin.character = 0;
m_mark_end.reset();
m_mark_end.selection_type = ChatSelection::SELECTION_PROMPT;
m_mark_end.scroll = 0;
m_mark_end.character = prompt.getLine().size() - 1;
m_mark_end.x_max = true;
prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE);
}
return true; return true;
} }
else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control) else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
{ {
if (m_mark_begin != m_mark_end) { if (m_mark_begin != m_mark_end) {
irr::core::stringc text = getSelectedText(); if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
Environment->getOSOperator()->copyToClipboard(text.c_str()); m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT) {
return true; irr::core::stringc text = getPromptSelectedText();
Environment->getOSOperator()->copyToClipboard(text.c_str());
return true;
} else if (m_mark_begin.selection_type == ChatSelection::SELECTION_HISTORY &&
m_mark_end.selection_type == ChatSelection::SELECTION_HISTORY) {
irr::core::stringc text = getSelectedText();
Environment->getOSOperator()->copyToClipboard(text.c_str());
return true;
}
} }
// Ctrl-C pressed // Ctrl-C pressed
@ -812,6 +1082,12 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control) else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
{ {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_begin != m_mark_end) {
deletePromptSelection();
}
// Ctrl-V pressed // Ctrl-V pressed
// paste text from clipboard // paste text from clipboard
if (prompt.getCursorLength() > 0) { if (prompt.getCursorLength() > 0) {
@ -830,6 +1106,15 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control) else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
{ {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_begin != m_mark_end) {
irr::core::stringc text = getPromptSelectedText();
Environment->getOSOperator()->copyToClipboard(text.c_str());
deletePromptSelection();
return true;
}
// Ctrl-X pressed // Ctrl-X pressed
// Cut text to clipboard // Cut text to clipboard
if (prompt.getCursorLength() <= 0) if (prompt.getCursorLength() <= 0)
@ -872,6 +1157,12 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
prompt.nickCompletion(names, backwards); prompt.nickCompletion(names, backwards);
return true; return true;
} else if (!iswcntrl(event.KeyInput.Char) && !event.KeyInput.Control) { } else if (!iswcntrl(event.KeyInput.Char) && !event.KeyInput.Control) {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_begin != m_mark_end) {
deletePromptSelection();
}
#if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9) #if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9)
wchar_t wc = L'_'; wchar_t wc = L'_';
mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
@ -887,6 +1178,12 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
{ {
if (event.SDLTextEvent.Type == ESDLET_TEXTINPUT) if (event.SDLTextEvent.Type == ESDLET_TEXTINPUT)
{ {
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_begin != m_mark_end) {
deletePromptSelection();
}
std::wstring text = utf8_to_wide(event.SDLTextEvent.Text); std::wstring text = utf8_to_wide(event.SDLTextEvent.Text);
for (u32 i = 0; i < text.size(); i++) for (u32 i = 0; i < text.size(); i++)
@ -905,13 +1202,41 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
m_chat_backend->scroll(rows); m_chat_backend->scroll(rows);
m_vscrollbar->setPos(m_vscrollbar->getPos() + rows); m_vscrollbar->setPos(m_vscrollbar->getPos() + rows);
} else if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { } else if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
m_mouse_marking = true; u32 row = m_chat_backend->getConsoleBuffer().getRows();
m_mark_begin = getCursorPos(event.MouseInput.X, event.MouseInput.Y); s32 prompt_y = row * m_fontsize.Y + m_height - m_desired_height;
m_mark_end = m_mark_begin;
if (event.MouseInput.Y >= prompt_y) {
m_prompt_marking = true;
if (event.MouseInput.Shift) {
if (m_mark_begin.selection_type != ChatSelection::SELECTION_PROMPT ||
m_mark_end.selection_type != ChatSelection::SELECTION_PROMPT) {
m_mark_begin = getCurrentPromptCursorPos();
m_mark_end = getPromptCursorPos(event.MouseInput.X, event.MouseInput.Y);
}
} else {
m_mark_begin = getPromptCursorPos(event.MouseInput.X, event.MouseInput.Y);
m_mark_end = m_mark_begin;
}
movePromptCursor(event.MouseInput.X, event.MouseInput.Y);
} else {
m_history_marking = true;
m_mark_begin = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
m_mark_end = m_mark_begin;
}
} else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { } else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
if (m_mouse_marking) { if (m_prompt_marking) {
m_mark_end = getPromptCursorPos(event.MouseInput.X, event.MouseInput.Y);
m_prompt_marking = false;
if (!event.MouseInput.Shift) {
if (m_mark_begin == m_mark_end) {
m_mark_begin.reset();
m_mark_end.reset();
}
}
} else if (m_history_marking) {
m_mark_end = getCursorPos(event.MouseInput.X, event.MouseInput.Y); m_mark_end = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
m_mouse_marking = false; m_history_marking = false;
if (m_mark_begin == m_mark_end) { if (m_mark_begin == m_mark_end) {
m_mark_begin.reset(); m_mark_begin.reset();
@ -919,7 +1244,10 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
} }
} else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) { } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) {
if (m_mouse_marking) { if (m_prompt_marking) {
m_mark_end = getPromptCursorPos(event.MouseInput.X, event.MouseInput.Y);
movePromptCursor(event.MouseInput.X, event.MouseInput.Y);
} else if (m_history_marking) {
m_mark_end = getCursorPos(event.MouseInput.X, event.MouseInput.Y); m_mark_end = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
} }
} }
@ -929,47 +1257,101 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
#ifdef HAVE_TOUCHSCREENGUI #ifdef HAVE_TOUCHSCREENGUI
else if (event.EventType == EET_TOUCH_INPUT_EVENT) { else if (event.EventType == EET_TOUCH_INPUT_EVENT) {
if (event.TouchInput.Event == irr::ETIE_PRESSED_DOWN) { if (event.TouchInput.Event == irr::ETIE_PRESSED_DOWN) {
m_mouse_marking = false; m_history_marking = false;
m_prompt_marking = false;
m_long_press = false; m_long_press = false;
m_cursor_press_pos = getCursorPos(event.TouchInput.X, event.TouchInput.Y);
u32 row = m_chat_backend->getConsoleBuffer().getRows();
s32 prompt_y = row * m_fontsize.Y + m_height - m_desired_height;
ChatSelection real_mark_begin = m_mark_end > m_mark_begin ? m_mark_begin : m_mark_end; ChatSelection real_mark_begin = m_mark_end > m_mark_begin ? m_mark_begin : m_mark_end;
ChatSelection real_mark_end = m_mark_end > m_mark_begin ? m_mark_end : m_mark_begin; ChatSelection real_mark_end = m_mark_end > m_mark_begin ? m_mark_end : m_mark_begin;
if (m_cursor_press_pos < real_mark_begin || m_cursor_press_pos > real_mark_end) { if (event.TouchInput.Y >= prompt_y) {
m_mark_begin = m_cursor_press_pos;
m_mark_end = m_cursor_press_pos;
m_mouse_marking = true;
}
} else if (event.TouchInput.Event == irr::ETIE_LEFT_UP) {
ChatSelection cursor_pos = getCursorPos(event.TouchInput.X, event.TouchInput.Y);
if (!m_long_press && m_cursor_press_pos == cursor_pos) { m_cursor_press_pos = getPromptCursorPos(event.TouchInput.X, event.TouchInput.Y);
m_mark_begin.reset();
m_mark_end.reset(); if (real_mark_begin.selection_type != ChatSelection::SELECTION_PROMPT ||
real_mark_end.selection_type != ChatSelection::SELECTION_PROMPT ||
m_cursor_press_pos < real_mark_begin ||
m_cursor_press_pos > real_mark_end) {
m_mark_begin = m_cursor_press_pos;
m_mark_end = m_cursor_press_pos;
m_prompt_marking = true;
}
} else {
m_cursor_press_pos = getCursorPos(event.TouchInput.X, event.TouchInput.Y);
if (real_mark_begin.selection_type != ChatSelection::SELECTION_HISTORY ||
real_mark_end.selection_type != ChatSelection::SELECTION_HISTORY ||
m_cursor_press_pos < real_mark_begin ||
m_cursor_press_pos > real_mark_end) {
m_mark_begin = m_cursor_press_pos;
m_mark_end = m_cursor_press_pos;
m_history_marking = true;
}
}
} else if (event.TouchInput.Event == irr::ETIE_LEFT_UP) {
if (m_prompt_marking) {
ChatSelection cursor_pos = getPromptCursorPos(event.TouchInput.X, event.TouchInput.Y);
if (!m_long_press && m_cursor_press_pos == cursor_pos) {
m_mark_begin.reset();
m_mark_end.reset();
}
m_prompt_marking = false;
} else if (m_history_marking) {
ChatSelection cursor_pos = getCursorPos(event.TouchInput.X, event.TouchInput.Y);
if (!m_long_press && m_cursor_press_pos == cursor_pos) {
m_mark_begin.reset();
m_mark_end.reset();
}
m_history_marking = false;
} }
m_cursor_press_pos.reset(); m_cursor_press_pos.reset();
m_mouse_marking = false;
m_long_press = false; m_long_press = false;
} else if (event.TouchInput.Event == irr::ETIE_MOVED) { } else if (event.TouchInput.Event == irr::ETIE_MOVED) {
ChatSelection cursor_pos = getCursorPos(event.TouchInput.X, event.TouchInput.Y); ChatSelection cursor_pos = getCursorPos(event.TouchInput.X, event.TouchInput.Y);
ChatSelection prompt_cursor_pos = getPromptCursorPos(event.TouchInput.X, event.TouchInput.Y);
if (!m_mouse_marking && !m_long_press && m_cursor_press_pos.initialized && if (!m_prompt_marking && !m_long_press &&
m_cursor_press_pos.selection_type == ChatSelection::SELECTION_PROMPT &&
m_cursor_press_pos != prompt_cursor_pos) {
m_mark_begin = m_cursor_press_pos;
m_mark_end = m_cursor_press_pos;
m_prompt_marking = true;
} else if (!m_history_marking && !m_long_press &&
m_cursor_press_pos.selection_type == ChatSelection::SELECTION_HISTORY &&
m_cursor_press_pos != cursor_pos) { m_cursor_press_pos != cursor_pos) {
m_mark_begin = m_cursor_press_pos; m_mark_begin = m_cursor_press_pos;
m_mark_end = m_cursor_press_pos; m_mark_end = m_cursor_press_pos;
m_mouse_marking = true; m_history_marking = true;
} }
if (m_mouse_marking) { if (m_prompt_marking) {
m_mark_end = prompt_cursor_pos;
} else if (m_history_marking) {
m_mark_end = cursor_pos; m_mark_end = cursor_pos;
} }
} else if (event.TouchInput.Event == irr::ETIE_PRESSED_LONG) { } else if (event.TouchInput.Event == irr::ETIE_PRESSED_LONG) {
if (!m_mouse_marking) { if (!m_history_marking && ! m_prompt_marking) {
m_long_press = true; m_long_press = true;
if (m_mark_begin != m_mark_end) { if (m_mark_begin != m_mark_end) {
irr::core::stringc text = getSelectedText(); irr::core::stringc text;
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT &&
m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT) {
text = getPromptSelectedText();
} else if (m_mark_begin.selection_type == ChatSelection::SELECTION_HISTORY &&
m_mark_end.selection_type == ChatSelection::SELECTION_HISTORY) {
text = getSelectedText();
}
Environment->getOSOperator()->copyToClipboard(text.c_str()); Environment->getOSOperator()->copyToClipboard(text.c_str());
#ifdef __ANDROID__ #ifdef __ANDROID__
SDL_AndroidShowToast( SDL_AndroidShowToast(
@ -1041,20 +1423,22 @@ void GUIChatConsole::createVScrollBar()
addChild(m_vscrollbar); addChild(m_vscrollbar);
} }
void GUIChatConsole::updateVScrollBar() void GUIChatConsole::updateVScrollBar(bool force_update, bool move_bottom)
{ {
if (!m_vscrollbar) if (!m_vscrollbar)
return; return;
ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
if (m_bottom_scroll_pos != buf.getBottomScrollPos()) { if (m_bottom_scroll_pos != buf.getBottomScrollPos() || force_update) {
bool is_bottom = m_vscrollbar->getPos() == m_bottom_scroll_pos;
m_bottom_scroll_pos = buf.getBottomScrollPos(); m_bottom_scroll_pos = buf.getBottomScrollPos();
if (buf.getBottomScrollPos() > 0) { if (buf.getBottomScrollPos() > 0) {
buf.scrollAbsolute(m_bottom_scroll_pos); buf.scrollAbsolute(m_bottom_scroll_pos);
m_vscrollbar->setMax(m_bottom_scroll_pos); m_vscrollbar->setMax(m_bottom_scroll_pos);
m_vscrollbar->setPos(m_bottom_scroll_pos); if (is_bottom || move_bottom)
m_vscrollbar->setPos(m_bottom_scroll_pos);
} else { } else {
m_vscrollbar->setMax(0); m_vscrollbar->setMax(0);
m_vscrollbar->setPos(0); m_vscrollbar->setPos(0);
@ -1066,6 +1450,21 @@ void GUIChatConsole::updateVScrollBar()
m_vscrollbar->setPageSize(page_size); m_vscrollbar->setPageSize(page_size);
} }
if (buf.getDelFormatted() > 0) {
bool is_bottom = m_vscrollbar->getPos() == m_bottom_scroll_pos;
if (!is_bottom && ! move_bottom) {
s32 pos = m_vscrollbar->getPos() - buf.getDelFormatted();
if (pos >= 0)
m_vscrollbar->setPos(pos);
}
m_mark_begin.scroll -= buf.getDelFormatted();
m_mark_end.scroll -= buf.getDelFormatted();
buf.resetDelFormatted();
}
if (m_vscrollbar->getPos() != buf.getScrollPos()) { if (m_vscrollbar->getPos() != buf.getScrollPos()) {
if (buf.getScrollPos() >= 0) { if (buf.getScrollPos() >= 0) {
s32 deltaScrollY = m_vscrollbar->getPos() - buf.getScrollPos(); s32 deltaScrollY = m_vscrollbar->getPos() - buf.getScrollPos();
@ -1081,6 +1480,42 @@ void GUIChatConsole::updateVScrollBar()
} }
} }
void GUIChatConsole::onLinesModified()
{
if (m_mark_begin.selection_type == ChatSelection::SELECTION_HISTORY &&
m_mark_end.selection_type == ChatSelection::SELECTION_HISTORY) {
ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
const ChatLine& first_line = buf.getLine(0);
int first_line_index = first_line.line_index;
if (m_mark_begin.line_index < first_line_index ||
m_mark_end.line_index < first_line_index) {
m_mark_begin.reset();
m_mark_end.reset();
m_cursor_press_pos.reset();
m_history_marking = false;
m_long_press = false;
}
}
updateVScrollBar(true);
}
void GUIChatConsole::onPromptModified()
{
if (m_mark_begin.selection_type == ChatSelection::SELECTION_PROMPT)
m_mark_begin.reset();
if (m_mark_end.selection_type == ChatSelection::SELECTION_PROMPT)
m_mark_end.reset();
if (m_cursor_press_pos.selection_type == ChatSelection::SELECTION_PROMPT)
m_cursor_press_pos.reset();
if (m_prompt_marking) {
m_prompt_marking = false;
m_long_press = false;
}
}
bool GUIChatConsole::hasFocus() bool GUIChatConsole::hasFocus()
{ {
if (Environment->hasFocus(this)) if (Environment->hasFocus(this))

View File

@ -27,14 +27,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
struct ChatSelection struct ChatSelection
{ {
ChatSelection() : initialized(false), scroll(0), row(0), row_buf(0), enum SelectionType {
line(0), fragment(0), character(0), x_max(false) {}; SELECTION_NONE,
SELECTION_HISTORY,
SELECTION_PROMPT
};
ChatSelection() : selection_type(SELECTION_NONE), scroll(0), row(0),
line_index(0), line(0), fragment(0), character(0), x_max(false) {};
void reset() { void reset() {
initialized = false; selection_type = SELECTION_NONE;
scroll = 0; scroll = 0;
row = 0; row = 0;
row_buf = 0; line_index = 0;
line = 0; line = 0;
fragment = 0; fragment = 0;
character = 0; character = 0;
@ -42,29 +48,47 @@ struct ChatSelection
} }
bool operator== (const ChatSelection &other) const { bool operator== (const ChatSelection &other) const {
return (row + scroll == other.row + other.scroll && if (selection_type == SELECTION_HISTORY &&
row_buf == other.row_buf && other.selection_type == SELECTION_HISTORY) {
line == other.line && return (row + scroll == other.row + other.scroll &&
fragment == other.fragment && line_index == other.line_index &&
character == other.character && line == other.line &&
x_max == other.x_max); fragment == other.fragment &&
character == other.character &&
x_max == other.x_max);
} else {
return (scroll + character == other.scroll + other.character &&
x_max == other.x_max);
}
} }
bool operator< (const ChatSelection &other) const { bool operator< (const ChatSelection &other) const {
if (row + scroll != other.row + other.scroll) if (selection_type == SELECTION_HISTORY &&
return (row + scroll < other.row + other.scroll); other.selection_type == SELECTION_HISTORY) {
if (row_buf != other.row_buf) if (row + scroll != other.row + other.scroll)
return (row_buf < other.row_buf); return (row + scroll < other.row + other.scroll);
if (line != other.line) if (line_index != other.line_index)
return (line < other.line); return (line_index < other.line_index);
if (fragment != other.fragment) if (line != other.line)
return (fragment < other.fragment); return (line < other.line);
if (character != other.character) if (fragment != other.fragment)
return (character < other.character); return (fragment < other.fragment);
if (x_max != other.x_max) if (character != other.character)
return (x_max < other.x_max); return (character < other.character);
if (x_max != other.x_max)
return (x_max < other.x_max);
return false; return false;
} else {
if (scroll + character != other.scroll + other.character)
return (scroll + character < other.scroll + other.character);
if (x_max != other.x_max)
return (x_max < other.x_max);
return false;
}
} }
bool operator> (const ChatSelection &other) { bool operator> (const ChatSelection &other) {
@ -83,10 +107,10 @@ struct ChatSelection
return !this->operator==(other); return !this->operator==(other);
} }
bool initialized; SelectionType selection_type;
int scroll; int scroll;
int row; int row;
int row_buf; int line_index;
unsigned int line; unsigned int line;
unsigned int fragment; unsigned int fragment;
unsigned int character; unsigned int character;
@ -149,6 +173,9 @@ public:
bool getAndroidChatOpen() { return m_android_chat_open; } bool getAndroidChatOpen() { return m_android_chat_open; }
void setAndroidChatOpen(bool value) { m_android_chat_open = value; } void setAndroidChatOpen(bool value) { m_android_chat_open = value; }
void onLinesModified();
void onPromptModified();
static GUIChatConsole* getChatConsole() { return m_chat_console; } static GUIChatConsole* getChatConsole() { return m_chat_console; }
private: private:
@ -162,9 +189,14 @@ private:
void drawPrompt(); void drawPrompt();
ChatSelection getCursorPos(s32 x, s32 y); ChatSelection getCursorPos(s32 x, s32 y);
ChatSelection getPromptCursorPos(s32 x, s32 y);
ChatSelection getCurrentPromptCursorPos();
irr::core::stringc getSelectedText(); irr::core::stringc getSelectedText();
irr::core::stringc getPromptSelectedText();
void movePromptCursor(s32 x, s32 y);
void deletePromptSelection();
void createVScrollBar(); void createVScrollBar();
void updateVScrollBar(); void updateVScrollBar(bool force_update = false, bool move_bottom = false);
private: private:
static GUIChatConsole* m_chat_console; static GUIChatConsole* m_chat_console;
@ -213,7 +245,8 @@ private:
ChatSelection m_mark_begin; ChatSelection m_mark_begin;
ChatSelection m_mark_end; ChatSelection m_mark_end;
bool m_mouse_marking = false; bool m_history_marking = false;
bool m_prompt_marking = false;
bool m_long_press = false; bool m_long_press = false;
ChatSelection m_cursor_press_pos; ChatSelection m_cursor_press_pos;