2013-08-09 08:01:20 +08:00
|
|
|
// Aseprite UI Library
|
2017-02-07 04:58:55 +08:00
|
|
|
// Copyright (C) 2001-2017 David Capello
|
2010-09-28 06:18:17 +08:00
|
|
|
//
|
2014-03-30 07:08:05 +08:00
|
|
|
// This file is released under the terms of the MIT license.
|
|
|
|
// Read LICENSE.txt for more information.
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2009-07-13 04:29:16 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2009-07-13 04:29:16 +08:00
|
|
|
|
2013-03-31 00:11:49 +08:00
|
|
|
#include "ui/entry.h"
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-09-04 20:07:23 +08:00
|
|
|
#include "base/bind.h"
|
2013-10-15 06:58:11 +08:00
|
|
|
#include "base/string.h"
|
2016-04-29 10:26:20 +08:00
|
|
|
#include "clip/clip.h"
|
2017-02-07 04:58:55 +08:00
|
|
|
#include "she/draw_text.h"
|
2014-06-23 05:53:14 +08:00
|
|
|
#include "she/font.h"
|
2016-11-18 05:07:00 +08:00
|
|
|
#include "she/system.h"
|
2012-06-18 09:49:58 +08:00
|
|
|
#include "ui/manager.h"
|
2014-09-01 09:51:17 +08:00
|
|
|
#include "ui/menu.h"
|
2012-06-18 09:49:58 +08:00
|
|
|
#include "ui/message.h"
|
2015-12-04 08:50:05 +08:00
|
|
|
#include "ui/size_hint_event.h"
|
2012-06-18 09:49:58 +08:00
|
|
|
#include "ui/system.h"
|
|
|
|
#include "ui/theme.h"
|
|
|
|
#include "ui/widget.h"
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-09-01 09:51:17 +08:00
|
|
|
#include <cctype>
|
2013-10-15 06:58:11 +08:00
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdio>
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-06-18 09:02:54 +08:00
|
|
|
namespace ui {
|
|
|
|
|
2016-12-29 21:22:39 +08:00
|
|
|
Entry::Entry(const std::size_t maxsize, const char* format, ...)
|
2013-04-04 09:07:24 +08:00
|
|
|
: Widget(kEntryWidget)
|
2012-07-06 12:06:00 +08:00
|
|
|
, m_timer(500, this)
|
2014-09-01 09:51:17 +08:00
|
|
|
, m_maxsize(maxsize)
|
|
|
|
, m_caret(0)
|
|
|
|
, m_scroll(0)
|
|
|
|
, m_select(0)
|
|
|
|
, m_hidden(false)
|
|
|
|
, m_state(false)
|
|
|
|
, m_readonly(false)
|
|
|
|
, m_recent_focused(false)
|
|
|
|
, m_lock_selection(false)
|
2016-11-18 05:07:00 +08:00
|
|
|
, m_translate_dead_keys(true)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2016-06-30 05:52:09 +08:00
|
|
|
enableFlags(CTRL_RIGHT_CLICK);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2009-05-31 05:22:52 +08:00
|
|
|
// formatted string
|
2017-02-22 05:05:23 +08:00
|
|
|
char buf[4096]; // TODO buffer overflow
|
2007-09-19 07:57:02 +08:00
|
|
|
if (format) {
|
|
|
|
va_list ap;
|
2008-01-04 07:22:04 +08:00
|
|
|
va_start(ap, format);
|
|
|
|
vsprintf(buf, format, ap);
|
|
|
|
va_end(ap);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2009-05-31 05:22:52 +08:00
|
|
|
// empty string
|
2007-09-19 07:57:02 +08:00
|
|
|
else {
|
2013-10-15 06:58:11 +08:00
|
|
|
buf[0] = 0;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2014-09-01 09:51:17 +08:00
|
|
|
// TODO support for text alignment and multi-line
|
2015-06-24 01:00:00 +08:00
|
|
|
// widget->align = LEFT | MIDDLE;
|
2010-12-09 01:28:13 +08:00
|
|
|
setText(buf);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-04-26 22:09:59 +08:00
|
|
|
setFocusStop(true);
|
2011-02-15 20:00:29 +08:00
|
|
|
initTheme();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
Entry::~Entry()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-12-29 21:22:39 +08:00
|
|
|
void Entry::setMaxTextLength(const std::size_t maxsize)
|
|
|
|
{
|
|
|
|
m_maxsize = maxsize;
|
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
bool Entry::isReadOnly() const
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2010-12-09 01:28:13 +08:00
|
|
|
return m_readonly;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::setReadOnly(bool state)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2010-12-09 01:28:13 +08:00
|
|
|
m_readonly = state;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::showCaret()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2010-12-09 01:28:13 +08:00
|
|
|
m_hidden = false;
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2010-12-09 01:28:13 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::hideCaret()
|
|
|
|
{
|
|
|
|
m_hidden = true;
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2017-02-21 21:48:25 +08:00
|
|
|
int Entry::lastCaretPos() const
|
|
|
|
{
|
2017-02-22 05:05:23 +08:00
|
|
|
return int(m_boxes.size()-1);
|
2017-02-21 21:48:25 +08:00
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::setCaretPos(int pos)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2017-02-08 06:05:47 +08:00
|
|
|
gfx::Size caretSize = theme()->getEntryCaretSize(this);
|
2017-02-21 21:48:25 +08:00
|
|
|
int textlen = lastCaretPos();
|
2016-03-14 23:39:45 +08:00
|
|
|
m_caret = MID(0, pos, textlen);
|
2017-02-08 06:05:47 +08:00
|
|
|
m_scroll = MID(0, m_scroll, textlen);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
// Backward scroll
|
2017-02-08 06:05:47 +08:00
|
|
|
if (m_caret < m_scroll)
|
2010-12-09 01:28:13 +08:00
|
|
|
m_scroll = m_caret;
|
2013-10-15 06:58:11 +08:00
|
|
|
// Forward scroll
|
2017-02-08 06:05:47 +08:00
|
|
|
else if (m_caret > m_scroll) {
|
2017-02-22 05:05:23 +08:00
|
|
|
int xLimit = bounds().x2() - border().right();
|
2017-02-08 06:05:47 +08:00
|
|
|
while (m_caret > m_scroll) {
|
2017-02-22 05:05:23 +08:00
|
|
|
int segmentWidth = 0;
|
|
|
|
for (int j=m_scroll; j<m_caret; ++j)
|
|
|
|
segmentWidth += m_boxes[j].width;
|
|
|
|
|
|
|
|
int x = bounds().x + border().left() + segmentWidth + caretSize.w;
|
|
|
|
if (x < xLimit)
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
2017-02-22 05:05:23 +08:00
|
|
|
else
|
2017-02-08 06:05:47 +08:00
|
|
|
++m_scroll;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2016-03-14 23:39:45 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-04-08 00:12:01 +08:00
|
|
|
m_timer.start();
|
2010-12-09 01:28:13 +08:00
|
|
|
m_state = true;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2017-02-21 21:48:25 +08:00
|
|
|
void Entry::setCaretToEnd()
|
|
|
|
{
|
|
|
|
int end = lastCaretPos();
|
|
|
|
selectText(end, end);
|
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::selectText(int from, int to)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2017-02-21 21:48:25 +08:00
|
|
|
int end = lastCaretPos();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_select = from;
|
|
|
|
setCaretPos(from); // to move scroll
|
|
|
|
setCaretPos((to >= 0)? to: end+to+1);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2014-02-09 05:58:28 +08:00
|
|
|
void Entry::selectAllText()
|
|
|
|
{
|
|
|
|
selectText(0, -1);
|
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::deselectText()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2010-12-09 01:28:13 +08:00
|
|
|
m_select = -1;
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
std::string Entry::selectedText() const
|
|
|
|
{
|
|
|
|
int selbeg, selend;
|
|
|
|
getEntryThemeInfo(nullptr, nullptr, nullptr, &selbeg, &selend);
|
|
|
|
|
|
|
|
if (selbeg >= 0 && selend >= 0) {
|
|
|
|
ASSERT(selbeg < int(m_boxes.size()));
|
|
|
|
ASSERT(selend < int(m_boxes.size()));
|
|
|
|
return text().substr(m_boxes[selbeg].from,
|
|
|
|
m_boxes[selend].to - m_boxes[selbeg].from);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
2013-03-30 03:20:32 +08:00
|
|
|
void Entry::setSuffix(const std::string& suffix)
|
|
|
|
{
|
|
|
|
m_suffix = suffix;
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2016-11-18 05:07:00 +08:00
|
|
|
void Entry::setTranslateDeadKeys(bool state)
|
|
|
|
{
|
|
|
|
m_translate_dead_keys = state;
|
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::getEntryThemeInfo(int* scroll, int* caret, int* state,
|
2017-02-22 05:05:23 +08:00
|
|
|
int* selbeg, int* selend) const
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2010-12-09 01:28:13 +08:00
|
|
|
if (scroll) *scroll = m_scroll;
|
|
|
|
if (caret) *caret = m_caret;
|
|
|
|
if (state) *state = !m_hidden && m_state;
|
|
|
|
|
|
|
|
if ((m_select >= 0) &&
|
|
|
|
(m_caret != m_select)) {
|
|
|
|
*selbeg = MIN(m_caret, m_select);
|
|
|
|
*selend = MAX(m_caret, m_select)-1;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
*selbeg = -1;
|
|
|
|
*selend = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-01 02:08:18 +08:00
|
|
|
gfx::Rect Entry::getEntryTextBounds() const
|
|
|
|
{
|
|
|
|
return onGetEntryTextBounds();
|
|
|
|
}
|
|
|
|
|
2011-04-03 00:14:07 +08:00
|
|
|
bool Entry::onProcessMessage(Message* msg)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2013-07-29 08:17:07 +08:00
|
|
|
switch (msg->type()) {
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kTimerMessage:
|
2013-07-29 08:17:07 +08:00
|
|
|
if (hasFocus() && static_cast<TimerMessage*>(msg)->timer() == &m_timer) {
|
2012-01-06 06:45:03 +08:00
|
|
|
// Blinking caret
|
|
|
|
m_state = m_state ? false: true;
|
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kFocusEnterMessage:
|
2012-04-08 00:12:01 +08:00
|
|
|
m_timer.start();
|
2008-01-04 07:22:04 +08:00
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_state = true;
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-09-01 09:51:17 +08:00
|
|
|
if (m_lock_selection) {
|
|
|
|
m_lock_selection = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
selectAllText();
|
|
|
|
m_recent_focused = true;
|
|
|
|
}
|
2016-11-18 05:07:00 +08:00
|
|
|
|
|
|
|
// Start processing dead keys
|
|
|
|
if (m_translate_dead_keys)
|
|
|
|
she::instance()->setTranslateDeadKeys(true);
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kFocusLeaveMessage:
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-04-08 00:12:01 +08:00
|
|
|
m_timer.stop();
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2014-09-01 09:51:17 +08:00
|
|
|
if (!m_lock_selection)
|
|
|
|
deselectText();
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_recent_focused = false;
|
2016-11-18 05:07:00 +08:00
|
|
|
|
|
|
|
// Stop processing dead keys
|
|
|
|
if (m_translate_dead_keys)
|
|
|
|
she::instance()->setTranslateDeadKeys(false);
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kKeyDownMessage:
|
2013-07-29 08:17:07 +08:00
|
|
|
if (hasFocus() && !isReadOnly()) {
|
2012-01-06 06:45:03 +08:00
|
|
|
// Command to execute
|
2014-09-01 09:51:17 +08:00
|
|
|
EntryCmd cmd = EntryCmd::NoOp;
|
2013-07-29 08:17:07 +08:00
|
|
|
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
|
|
|
|
KeyScancode scancode = keymsg->scancode();
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-07-29 08:17:07 +08:00
|
|
|
switch (scancode) {
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
case kKeyLeft:
|
2015-05-11 23:12:13 +08:00
|
|
|
if (msg->ctrlPressed() || msg->altPressed())
|
2012-01-06 06:45:03 +08:00
|
|
|
cmd = EntryCmd::BackwardWord;
|
2015-05-11 23:12:13 +08:00
|
|
|
else if (msg->cmdPressed())
|
|
|
|
cmd = EntryCmd::BeginningOfLine;
|
2012-01-06 06:45:03 +08:00
|
|
|
else
|
|
|
|
cmd = EntryCmd::BackwardChar;
|
|
|
|
break;
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
case kKeyRight:
|
2015-05-11 23:12:13 +08:00
|
|
|
if (msg->ctrlPressed() || msg->altPressed())
|
2012-01-06 06:45:03 +08:00
|
|
|
cmd = EntryCmd::ForwardWord;
|
2015-05-11 23:12:13 +08:00
|
|
|
else if (msg->cmdPressed())
|
|
|
|
cmd = EntryCmd::EndOfLine;
|
2012-01-06 06:45:03 +08:00
|
|
|
else
|
|
|
|
cmd = EntryCmd::ForwardChar;
|
|
|
|
break;
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
case kKeyHome:
|
2012-01-06 06:45:03 +08:00
|
|
|
cmd = EntryCmd::BeginningOfLine;
|
|
|
|
break;
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
case kKeyEnd:
|
2012-01-06 06:45:03 +08:00
|
|
|
cmd = EntryCmd::EndOfLine;
|
|
|
|
break;
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
case kKeyDel:
|
2013-07-29 08:17:07 +08:00
|
|
|
if (msg->shiftPressed())
|
2012-01-06 06:45:03 +08:00
|
|
|
cmd = EntryCmd::Cut;
|
2015-08-21 10:31:46 +08:00
|
|
|
else if (msg->ctrlPressed())
|
|
|
|
cmd = EntryCmd::DeleteForwardToEndOfLine;
|
2012-01-06 06:45:03 +08:00
|
|
|
else
|
|
|
|
cmd = EntryCmd::DeleteForward;
|
|
|
|
break;
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
case kKeyInsert:
|
2013-07-29 08:17:07 +08:00
|
|
|
if (msg->shiftPressed())
|
2012-01-06 06:45:03 +08:00
|
|
|
cmd = EntryCmd::Paste;
|
2013-07-29 08:17:07 +08:00
|
|
|
else if (msg->ctrlPressed())
|
2012-01-06 06:45:03 +08:00
|
|
|
cmd = EntryCmd::Copy;
|
|
|
|
break;
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
case kKeyBackspace:
|
2015-08-21 10:31:46 +08:00
|
|
|
if (msg->ctrlPressed())
|
|
|
|
cmd = EntryCmd::DeleteBackwardWord;
|
|
|
|
else
|
|
|
|
cmd = EntryCmd::DeleteBackward;
|
2012-01-06 06:45:03 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-11-22 21:34:00 +08:00
|
|
|
// Map common macOS/Windows shortcuts for Cut/Copy/Paste/Select all
|
2014-08-24 01:51:28 +08:00
|
|
|
#if defined __APPLE__
|
|
|
|
if (msg->onlyCmdPressed())
|
|
|
|
#else
|
|
|
|
if (msg->onlyCtrlPressed())
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
switch (scancode) {
|
|
|
|
case kKeyX: cmd = EntryCmd::Cut; break;
|
|
|
|
case kKeyC: cmd = EntryCmd::Copy; break;
|
|
|
|
case kKeyV: cmd = EntryCmd::Paste; break;
|
2015-05-07 05:11:29 +08:00
|
|
|
case kKeyA: cmd = EntryCmd::SelectAll; break;
|
2014-08-24 01:51:28 +08:00
|
|
|
}
|
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-11-18 05:07:00 +08:00
|
|
|
if (cmd == EntryCmd::NoOp) {
|
|
|
|
if (keymsg->unicodeChar() >= 32) {
|
|
|
|
executeCmd(EntryCmd::InsertChar, keymsg->unicodeChar(),
|
|
|
|
(msg->shiftPressed()) ? true: false);
|
|
|
|
|
|
|
|
// Select dead-key
|
|
|
|
if (keymsg->isDeadKey()) {
|
2017-02-22 05:05:23 +08:00
|
|
|
if (lastCaretPos() < m_maxsize)
|
2016-11-29 22:08:29 +08:00
|
|
|
selectText(m_caret-1, m_caret);
|
2016-11-18 05:07:00 +08:00
|
|
|
}
|
2016-11-21 23:28:42 +08:00
|
|
|
return true;
|
|
|
|
}
|
2016-11-22 21:34:00 +08:00
|
|
|
// Consume all key down of modifiers only, e.g. so the user
|
|
|
|
// can press first "Ctrl" key, and then "Ctrl+C"
|
|
|
|
// combination.
|
|
|
|
else if (keymsg->scancode() >= kKeyFirstModifierScancode) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-11-21 23:28:42 +08:00
|
|
|
else {
|
|
|
|
break; // Propagate to manager
|
2016-11-18 05:07:00 +08:00
|
|
|
}
|
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
executeCmd(cmd, keymsg->unicodeChar(),
|
2013-07-29 08:17:07 +08:00
|
|
|
(msg->shiftPressed()) ? true: false);
|
2012-01-06 06:45:03 +08:00
|
|
|
return true;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseDownMessage:
|
2013-07-29 08:17:07 +08:00
|
|
|
captureMouse();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseMoveMessage:
|
2013-07-29 08:17:07 +08:00
|
|
|
if (hasCapture()) {
|
|
|
|
bool is_dirty = false;
|
2017-02-08 06:05:47 +08:00
|
|
|
int c = getCaretFromMouse(static_cast<MouseMessage*>(msg));
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2017-02-08 06:05:47 +08:00
|
|
|
if (static_cast<MouseMessage*>(msg)->left() || !isPosInSelection(c)) {
|
|
|
|
// Move caret
|
|
|
|
if (m_caret != c) {
|
|
|
|
setCaretPos(c);
|
2012-01-06 06:45:03 +08:00
|
|
|
is_dirty = true;
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2014-09-01 09:51:17 +08:00
|
|
|
// Move selection
|
|
|
|
if (m_recent_focused) {
|
|
|
|
m_recent_focused = false;
|
|
|
|
m_select = m_caret;
|
|
|
|
}
|
|
|
|
else if (msg->type() == kMouseDownMessage)
|
|
|
|
m_select = m_caret;
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Show the caret
|
|
|
|
if (is_dirty) {
|
2012-04-08 00:12:01 +08:00
|
|
|
m_timer.start();
|
2012-01-06 06:45:03 +08:00
|
|
|
m_state = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseUpMessage:
|
2014-09-01 09:51:17 +08:00
|
|
|
if (hasCapture()) {
|
2013-07-29 08:17:07 +08:00
|
|
|
releaseMouse();
|
2014-09-01 09:51:17 +08:00
|
|
|
|
|
|
|
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
|
|
|
if (mouseMsg->right()) {
|
|
|
|
// This flag is disabled in kFocusEnterMessage message handler.
|
|
|
|
m_lock_selection = true;
|
|
|
|
|
|
|
|
showEditPopupMenu(mouseMsg->position());
|
|
|
|
requestFocus();
|
|
|
|
}
|
|
|
|
}
|
2009-08-18 02:00:38 +08:00
|
|
|
return true;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kDoubleClickMessage:
|
2010-12-09 01:28:13 +08:00
|
|
|
forwardWord();
|
|
|
|
m_select = m_caret;
|
|
|
|
backwardWord();
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2009-08-18 02:00:38 +08:00
|
|
|
return true;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseEnterMessage:
|
|
|
|
case kMouseLeaveMessage:
|
2014-09-01 09:51:17 +08:00
|
|
|
// TODO theme stuff
|
2013-07-29 08:17:07 +08:00
|
|
|
if (isEnabled())
|
2012-01-06 06:45:03 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
return Widget::onProcessMessage(msg);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2015-12-04 08:50:05 +08:00
|
|
|
void Entry::onSizeHint(SizeHintEvent& ev)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2010-12-09 01:28:13 +08:00
|
|
|
int w =
|
2017-02-08 06:05:47 +08:00
|
|
|
+ font()->textLength("w") * MIN(m_maxsize, 6)
|
2014-11-26 09:33:45 +08:00
|
|
|
+ 2*guiscale()
|
2015-06-24 06:20:49 +08:00
|
|
|
+ border().width();
|
2009-05-31 05:22:52 +08:00
|
|
|
|
2014-09-01 01:17:49 +08:00
|
|
|
w = MIN(w, ui::display_w()/2);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
int h =
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
+ font()->height()
|
2015-06-24 06:20:49 +08:00
|
|
|
+ border().height();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-12-04 08:50:05 +08:00
|
|
|
ev.setSizeHint(w, h);
|
2010-12-09 01:28:13 +08:00
|
|
|
}
|
|
|
|
|
2011-02-12 20:32:57 +08:00
|
|
|
void Entry::onPaint(PaintEvent& ev)
|
|
|
|
{
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
theme()->paintEntry(ev);
|
2011-02-12 20:32:57 +08:00
|
|
|
}
|
|
|
|
|
2013-03-30 11:35:25 +08:00
|
|
|
void Entry::onSetText()
|
|
|
|
{
|
|
|
|
Widget::onSetText();
|
2017-02-22 05:05:23 +08:00
|
|
|
recalcCharBoxes(text());
|
2013-03-30 11:35:25 +08:00
|
|
|
|
2017-02-21 21:48:25 +08:00
|
|
|
int textlen = lastCaretPos();
|
2016-03-14 23:39:45 +08:00
|
|
|
if (m_caret >= 0 && m_caret > textlen)
|
|
|
|
m_caret = textlen;
|
2013-03-30 11:35:25 +08:00
|
|
|
}
|
|
|
|
|
2015-09-23 03:22:47 +08:00
|
|
|
void Entry::onChange()
|
2010-12-09 01:28:13 +08:00
|
|
|
{
|
2015-09-23 03:22:47 +08:00
|
|
|
Change();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2015-12-01 02:08:18 +08:00
|
|
|
gfx::Rect Entry::onGetEntryTextBounds() const
|
|
|
|
{
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
gfx::Rect bounds = clientBounds();
|
2015-12-01 02:08:18 +08:00
|
|
|
bounds.x += border().left();
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
bounds.y += bounds.h/2 - textHeight()/2;
|
2015-12-01 02:08:18 +08:00
|
|
|
bounds.w -= border().width();
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
bounds.h = textHeight();
|
2015-12-01 02:08:18 +08:00
|
|
|
return bounds;
|
|
|
|
}
|
|
|
|
|
2013-07-29 08:17:07 +08:00
|
|
|
int Entry::getCaretFromMouse(MouseMessage* mousemsg)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2017-02-08 06:05:47 +08:00
|
|
|
int mouseX = mousemsg->position().x;
|
|
|
|
if (mouseX < bounds().x+border().left()) {
|
|
|
|
// Scroll to the left
|
|
|
|
return MAX(0, m_scroll-1);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
int lastPos = lastCaretPos();
|
|
|
|
int i = MIN(m_scroll, lastPos);
|
|
|
|
for (; i<lastPos; ++i) {
|
|
|
|
int segmentWidth = 0;
|
|
|
|
for (int j=m_scroll; j<i; ++j)
|
|
|
|
segmentWidth += m_boxes[j].width;
|
2017-02-08 06:05:47 +08:00
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
int x = bounds().x + border().left() + segmentWidth;
|
2017-02-08 06:05:47 +08:00
|
|
|
|
|
|
|
if (mouseX > bounds().x2() - border().right()) {
|
|
|
|
if (x >= bounds().x2() - border().right()) {
|
|
|
|
// Scroll to the right
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (x > mouseX) {
|
|
|
|
// Previous char is the selected one
|
|
|
|
if (i > m_scroll)
|
|
|
|
--i;
|
|
|
|
break;
|
|
|
|
}
|
2013-10-15 06:58:11 +08:00
|
|
|
}
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
return MID(0, i, lastPos);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2014-09-01 09:51:17 +08:00
|
|
|
void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed)
|
2009-08-10 05:24:32 +08:00
|
|
|
{
|
2017-02-22 05:05:23 +08:00
|
|
|
std::string text = this->text();
|
2009-08-10 05:24:32 +08:00
|
|
|
int c, selbeg, selend;
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
getEntryThemeInfo(NULL, NULL, NULL, &selbeg, &selend);
|
2009-08-10 05:24:32 +08:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
2010-08-12 10:42:03 +08:00
|
|
|
case EntryCmd::NoOp:
|
|
|
|
break;
|
|
|
|
|
2009-08-10 05:24:32 +08:00
|
|
|
case EntryCmd::InsertChar:
|
|
|
|
// delete the entire selection
|
|
|
|
if (selbeg >= 0) {
|
2017-02-22 05:05:23 +08:00
|
|
|
text.erase(m_boxes[selbeg].from,
|
|
|
|
m_boxes[selend].to - m_boxes[selbeg].from);
|
2009-08-10 05:24:32 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
m_caret = selbeg;
|
2017-02-08 06:05:47 +08:00
|
|
|
|
|
|
|
// We set the caret to the beginning of the erased selection,
|
|
|
|
// needed to show the first inserted character in case
|
|
|
|
// m_scroll > m_caret. E.g. we select all text and insert a
|
|
|
|
// new character to replace the whole text, the new inserted
|
|
|
|
// character makes m_caret=1, so m_scroll will be 1 too, but
|
|
|
|
// we need to make m_scroll=0 to show the new inserted char.)
|
|
|
|
// In this way, we first ensure a m_scroll value enough to
|
|
|
|
// show the new inserted character.
|
2017-02-22 05:05:23 +08:00
|
|
|
recalcCharBoxes(text);
|
2017-02-08 06:05:47 +08:00
|
|
|
setCaretPos(m_caret);
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
// Convert the unicode character -> wstring -> utf-8 string -> insert the utf-8 string
|
|
|
|
if (lastCaretPos() < m_maxsize) {
|
|
|
|
ASSERT((std::size_t)m_caret <= lastCaretPos());
|
|
|
|
|
|
|
|
std::wstring unicodeStr;
|
|
|
|
unicodeStr.push_back(unicodeChar);
|
|
|
|
|
|
|
|
text.insert(m_boxes[m_caret].from,
|
|
|
|
base::to_utf8(unicodeStr));
|
|
|
|
++m_caret;
|
2013-03-30 11:35:25 +08:00
|
|
|
}
|
2009-08-10 05:24:32 +08:00
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_select = -1;
|
2009-08-10 05:24:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::BackwardChar:
|
|
|
|
case EntryCmd::BackwardWord:
|
|
|
|
// selection
|
|
|
|
if (shift_pressed) {
|
2012-01-06 06:45:03 +08:00
|
|
|
if (m_select < 0)
|
|
|
|
m_select = m_caret;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
else
|
2012-01-06 06:45:03 +08:00
|
|
|
m_select = -1;
|
2009-08-10 05:24:32 +08:00
|
|
|
|
|
|
|
// backward word
|
|
|
|
if (cmd == EntryCmd::BackwardWord) {
|
2012-01-06 06:45:03 +08:00
|
|
|
backwardWord();
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
// backward char
|
2010-12-09 01:28:13 +08:00
|
|
|
else if (m_caret > 0) {
|
2012-01-06 06:45:03 +08:00
|
|
|
m_caret--;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::ForwardChar:
|
|
|
|
case EntryCmd::ForwardWord:
|
|
|
|
// selection
|
|
|
|
if (shift_pressed) {
|
2012-01-06 06:45:03 +08:00
|
|
|
if (m_select < 0)
|
|
|
|
m_select = m_caret;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
else
|
2012-01-06 06:45:03 +08:00
|
|
|
m_select = -1;
|
2009-08-10 05:24:32 +08:00
|
|
|
|
|
|
|
// forward word
|
|
|
|
if (cmd == EntryCmd::ForwardWord) {
|
2012-01-06 06:45:03 +08:00
|
|
|
forwardWord();
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
// forward char
|
2010-12-09 01:28:13 +08:00
|
|
|
else if (m_caret < (int)text.size()) {
|
2012-01-06 06:45:03 +08:00
|
|
|
m_caret++;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::BeginningOfLine:
|
|
|
|
// selection
|
|
|
|
if (shift_pressed) {
|
2012-01-06 06:45:03 +08:00
|
|
|
if (m_select < 0)
|
|
|
|
m_select = m_caret;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
else
|
2012-01-06 06:45:03 +08:00
|
|
|
m_select = -1;
|
2009-08-10 05:24:32 +08:00
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_caret = 0;
|
2009-08-10 05:24:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::EndOfLine:
|
|
|
|
// selection
|
|
|
|
if (shift_pressed) {
|
2012-01-06 06:45:03 +08:00
|
|
|
if (m_select < 0)
|
|
|
|
m_select = m_caret;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
else
|
2012-01-06 06:45:03 +08:00
|
|
|
m_select = -1;
|
2009-08-10 05:24:32 +08:00
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_caret = text.size();
|
2009-08-10 05:24:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::DeleteForward:
|
|
|
|
case EntryCmd::Cut:
|
|
|
|
// delete the entire selection
|
|
|
|
if (selbeg >= 0) {
|
2012-01-06 06:45:03 +08:00
|
|
|
// *cut* text!
|
2017-02-22 05:05:23 +08:00
|
|
|
if (cmd == EntryCmd::Cut)
|
|
|
|
clip::set_text(selectedText());
|
2009-08-10 05:24:32 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
// remove text
|
2017-02-22 05:05:23 +08:00
|
|
|
text.erase(m_boxes[selbeg].from,
|
|
|
|
m_boxes[selend].to - m_boxes[selbeg].from);
|
2009-08-10 05:24:32 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
m_caret = selbeg;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
// delete the next character
|
|
|
|
else {
|
2012-01-06 06:45:03 +08:00
|
|
|
if (m_caret < (int)text.size())
|
2017-02-22 05:05:23 +08:00
|
|
|
text.erase(m_boxes[m_caret].from,
|
|
|
|
m_boxes[m_caret].to - m_boxes[m_caret].from);
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_select = -1;
|
2009-08-10 05:24:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::Paste: {
|
2016-04-29 10:26:20 +08:00
|
|
|
std::string clipboard;
|
|
|
|
if (clip::get_text(clipboard)) {
|
2012-01-06 06:45:03 +08:00
|
|
|
// delete the entire selection
|
|
|
|
if (selbeg >= 0) {
|
2017-02-22 05:05:23 +08:00
|
|
|
text.erase(m_boxes[selbeg].from,
|
|
|
|
m_boxes[selend].to - m_boxes[selbeg].from);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
m_caret = selbeg;
|
|
|
|
m_select = -1;
|
|
|
|
}
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
// Paste text
|
|
|
|
recalcCharBoxes(text);
|
|
|
|
int oldBoxes = m_boxes.size();
|
|
|
|
|
|
|
|
text.insert(m_boxes[m_caret].from, clipboard);
|
|
|
|
|
|
|
|
// Remove extra chars that do not fit in m_maxsize
|
|
|
|
recalcCharBoxes(text);
|
|
|
|
if (lastCaretPos() > m_maxsize) {
|
|
|
|
text.erase(m_boxes[m_maxsize].from,
|
|
|
|
text.size() - m_boxes[m_maxsize].from);
|
|
|
|
recalcCharBoxes(text);
|
2013-10-15 06:58:11 +08:00
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
int newBoxes = m_boxes.size();
|
|
|
|
setCaretPos(m_caret+(newBoxes - oldBoxes));
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case EntryCmd::Copy:
|
2017-02-22 05:05:23 +08:00
|
|
|
if (selbeg >= 0)
|
|
|
|
clip::set_text(selectedText());
|
2009-08-10 05:24:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::DeleteBackward:
|
|
|
|
// delete the entire selection
|
|
|
|
if (selbeg >= 0) {
|
2017-02-22 05:05:23 +08:00
|
|
|
text.erase(m_boxes[selbeg].from,
|
|
|
|
m_boxes[selend].to - m_boxes[selbeg].from);
|
2009-08-10 05:24:32 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
m_caret = selbeg;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
// delete the previous character
|
|
|
|
else {
|
2017-02-22 05:05:23 +08:00
|
|
|
if (m_caret > 0) {
|
|
|
|
--m_caret;
|
|
|
|
text.erase(m_boxes[m_caret].from,
|
|
|
|
m_boxes[m_caret].to - m_boxes[m_caret].from);
|
|
|
|
}
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
m_select = -1;
|
2009-08-10 05:24:32 +08:00
|
|
|
break;
|
2015-05-07 05:11:29 +08:00
|
|
|
|
2015-08-21 10:31:46 +08:00
|
|
|
case EntryCmd::DeleteBackwardWord:
|
|
|
|
m_select = m_caret;
|
|
|
|
backwardWord();
|
2017-02-22 05:05:23 +08:00
|
|
|
if (m_caret < m_select) {
|
|
|
|
text.erase(m_boxes[m_caret].from,
|
|
|
|
m_boxes[m_select].to - m_boxes[m_caret].from);
|
|
|
|
}
|
2015-08-21 10:31:46 +08:00
|
|
|
m_select = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EntryCmd::DeleteForwardToEndOfLine:
|
2017-02-22 05:05:23 +08:00
|
|
|
text.erase(m_boxes[m_caret].from);
|
2015-08-21 10:31:46 +08:00
|
|
|
break;
|
|
|
|
|
2015-05-07 05:11:29 +08:00
|
|
|
case EntryCmd::SelectAll:
|
|
|
|
selectAllText();
|
|
|
|
break;
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
if (text != this->text()) {
|
|
|
|
setText(text);
|
2015-09-23 03:22:47 +08:00
|
|
|
onChange();
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
setCaretPos(m_caret);
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2009-08-10 05:24:32 +08:00
|
|
|
}
|
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
#define IS_WORD_CHAR(ch) \
|
2014-06-23 05:53:14 +08:00
|
|
|
(!((!ch) || (std::isspace(ch)) || \
|
2014-09-21 09:54:20 +08:00
|
|
|
((ch) == '/') || ((ch) == '\\')))
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::forwardWord()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2017-02-22 05:05:23 +08:00
|
|
|
int textlen = lastCaretPos();
|
2007-09-19 07:57:02 +08:00
|
|
|
int ch;
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
for (; m_caret < textlen; ++m_caret) {
|
|
|
|
ch = m_boxes[m_caret].codepoint;
|
2013-10-15 06:58:11 +08:00
|
|
|
if (IS_WORD_CHAR(ch))
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
for (; m_caret < textlen; ++m_caret) {
|
|
|
|
ch = m_boxes[m_caret].codepoint;
|
2008-01-04 07:22:04 +08:00
|
|
|
if (!IS_WORD_CHAR(ch)) {
|
2013-10-15 06:58:11 +08:00
|
|
|
++m_caret;
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
void Entry::backwardWord()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
|
|
|
int ch;
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
for (--m_caret; m_caret >= 0; --m_caret) {
|
2017-02-22 05:05:23 +08:00
|
|
|
ch = m_boxes[m_caret].codepoint;
|
2008-01-04 07:22:04 +08:00
|
|
|
if (IS_WORD_CHAR(ch))
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
for (; m_caret >= 0; --m_caret) {
|
2017-02-22 05:05:23 +08:00
|
|
|
ch = m_boxes[m_caret].codepoint;
|
2008-01-04 07:22:04 +08:00
|
|
|
if (!IS_WORD_CHAR(ch)) {
|
2013-10-15 06:58:11 +08:00
|
|
|
++m_caret;
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-09 01:28:13 +08:00
|
|
|
if (m_caret < 0)
|
|
|
|
m_caret = 0;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2012-06-18 09:02:54 +08:00
|
|
|
|
2014-09-01 09:51:17 +08:00
|
|
|
bool Entry::isPosInSelection(int pos)
|
|
|
|
{
|
2017-02-22 05:05:23 +08:00
|
|
|
return (pos >= MIN(m_caret, m_select) &&
|
|
|
|
pos <= MAX(m_caret, m_select));
|
2014-09-01 09:51:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::showEditPopupMenu(const gfx::Point& pt)
|
|
|
|
{
|
|
|
|
Menu menu;
|
|
|
|
MenuItem cut("Cut");
|
|
|
|
MenuItem copy("Copy");
|
|
|
|
MenuItem paste("Paste");
|
|
|
|
menu.addChild(&cut);
|
|
|
|
menu.addChild(©);
|
|
|
|
menu.addChild(&paste);
|
2015-12-05 02:17:42 +08:00
|
|
|
cut.Click.connect(base::Bind(&Entry::executeCmd, this, EntryCmd::Cut, 0, false));
|
|
|
|
copy.Click.connect(base::Bind(&Entry::executeCmd, this, EntryCmd::Copy, 0, false));
|
|
|
|
paste.Click.connect(base::Bind(&Entry::executeCmd, this, EntryCmd::Paste, 0, false));
|
2014-09-01 09:51:17 +08:00
|
|
|
|
|
|
|
if (isReadOnly()) {
|
|
|
|
cut.setEnabled(false);
|
|
|
|
paste.setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.showPopup(pt);
|
|
|
|
}
|
|
|
|
|
2017-02-22 05:05:23 +08:00
|
|
|
class Entry::CalcBoxesTextDelegate : public she::DrawTextDelegate {
|
|
|
|
public:
|
|
|
|
CalcBoxesTextDelegate(const int end) : m_end(end) {
|
|
|
|
}
|
|
|
|
|
|
|
|
const Entry::CharBoxes& boxes() const { return m_boxes; }
|
|
|
|
|
|
|
|
void preProcessChar(const int index,
|
|
|
|
const int codepoint,
|
|
|
|
gfx::Color& fg, gfx::Color& bg) override {
|
|
|
|
if (!m_boxes.empty())
|
|
|
|
m_boxes.back().to = index;
|
|
|
|
|
|
|
|
m_box = CharBox();
|
|
|
|
m_box.codepoint = codepoint;
|
|
|
|
m_box.from = index;
|
|
|
|
m_box.to = m_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool preDrawChar(const gfx::Rect& charBounds) override {
|
|
|
|
m_box.width = charBounds.w;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void postDrawChar(const gfx::Rect& charBounds) override {
|
|
|
|
m_boxes.push_back(m_box);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Entry::CharBox m_box;
|
|
|
|
Entry::CharBoxes m_boxes;
|
|
|
|
int m_end;
|
|
|
|
};
|
|
|
|
|
|
|
|
void Entry::recalcCharBoxes(const std::string& text)
|
|
|
|
{
|
|
|
|
int lastTextIndex = int(text.size());
|
|
|
|
CalcBoxesTextDelegate delegate(lastTextIndex);
|
|
|
|
she::draw_text(nullptr, font(),
|
|
|
|
base::utf8_const_iterator(text.begin()),
|
|
|
|
base::utf8_const_iterator(text.end()),
|
|
|
|
gfx::ColorNone, gfx::ColorNone, 0, 0, &delegate);
|
|
|
|
m_boxes = delegate.boxes();
|
|
|
|
|
|
|
|
// A last box for the last position
|
|
|
|
CharBox box;
|
|
|
|
box.codepoint = 0;
|
|
|
|
box.from = box.to = lastTextIndex;
|
|
|
|
m_boxes.push_back(box);
|
|
|
|
}
|
|
|
|
|
2012-06-18 09:02:54 +08:00
|
|
|
} // namespace ui
|