all repos — fluxbox @ 58b50fb786a15fb1740a3d900e271d0baa6b5482

custom fork of the fluxbox windowmanager

Allow text selection

FbTk::TextBox now acts like any contemporary input field ;-)
Thomas Lübking thomas.luebking@gmail.com
commit

58b50fb786a15fb1740a3d900e271d0baa6b5482

parent

2e8766174eaa11e7f370ba5927ed38e18efced11

2 files changed, 151 insertions(+), 62 deletions(-)

jump to
M src/FbTk/TextBox.ccsrc/FbTk/TextBox.cc

@@ -53,7 +53,8 @@ m_text(text),

m_gc(0), m_cursor_pos(0), m_start_pos(0), - m_end_pos(0) { + m_end_pos(0), + m_select_pos(-1) { FbTk::EventManager::instance()->add(*this, *this); }

@@ -128,6 +129,9 @@ }

} void TextBox::backspace() { + if (hasSelection()) + return deleteForward(); + if (m_start_pos || cursorPosition()) { FbString t = text(); t.erase(m_start_pos + cursorPosition() - 1, 1);

@@ -141,15 +145,27 @@ }

} void TextBox::deleteForward() { - if (m_start_pos + m_cursor_pos < m_end_pos) { + std::string::size_type pos = m_start_pos + m_cursor_pos; + int length = 1; + if (hasSelection()) { + pos = std::min(m_start_pos + m_cursor_pos, m_select_pos); + length = std::max(m_start_pos + m_cursor_pos, m_select_pos) - pos; + m_cursor_pos = pos - m_start_pos; + } + if (pos < m_end_pos) { FbString t = text(); - t.erase(m_start_pos + m_cursor_pos, 1); + t.erase(pos, length); m_text.setLogical(t); adjustEndPos(); } + if (length > 1) + adjustStartPos(); } void TextBox::insertText(const std::string &val) { + if (hasSelection()) + deleteForward(); + FbString t = text(); t.insert(m_start_pos + cursorPosition(), val); m_text.setLogical(t);

@@ -168,20 +184,48 @@ }

} void TextBox::clear() { + Display *dpy = FbTk::App::instance()->display(); FbWindow::clear(); // center text by default int center_pos = (height() + font().ascent())/2; if (gc() == 0) - setGC(DefaultGC(FbTk::App::instance()->display(), screenNumber())); + setGC(DefaultGC(dpy, screenNumber())); - font().drawText(*this, screenNumber(), - gc(), + + int cursor_pos = font().textWidth(m_text.visual().c_str() + m_start_pos, m_cursor_pos); + + font().drawText(*this, screenNumber(), gc(), m_text.visual().c_str() + m_start_pos, - m_end_pos - m_start_pos, - 0, center_pos); // pos + m_end_pos - m_start_pos, -1, center_pos); // pos + + if (hasSelection()) { + int select_pos = m_select_pos <= m_start_pos ? 0 : + font().textWidth(m_text.visual().c_str() + m_start_pos, + m_select_pos - m_start_pos); + int start = std::max(m_start_pos, std::min(m_start_pos + m_cursor_pos, m_select_pos)); + int length = std::max(m_start_pos + m_cursor_pos, m_select_pos) - start; + int x = std::min(select_pos, cursor_pos); + int width = std::abs(select_pos - cursor_pos); + + XGCValues backup; + XGetGCValues(dpy, gc(), GCForeground|GCBackground, &backup); + XSetForeground(dpy, gc(), backup.foreground); + + fillRectangle(gc(), x, 0, width, height()); + + XColor c; + c.pixel = backup.foreground; + XQueryColor(dpy, DefaultColormap(dpy, screenNumber()), &c); + XSetForeground(dpy, gc(), c.red + c.green + c.blue > 0x17ffe ? + BlackPixel(dpy, screenNumber()) : + WhitePixel(dpy, screenNumber())); + font().drawText(*this, screenNumber(), gc(), + m_text.visual().c_str() + start, length, x, center_pos); // pos + XSetForeground(dpy, gc(), backup.foreground); + } + // draw cursor position - int cursor_pos = font().textWidth(m_text.visual().c_str() + m_start_pos, m_cursor_pos) + 1; drawLine(gc(), cursor_pos, center_pos, cursor_pos, center_pos - font().height()); }

@@ -231,12 +275,52 @@ XLookupString(&event, keychar, 1, &ks, 0);

// a modifier key by itself doesn't do anything if (IsModifierKey(ks)) return; - if (FbTk::KeyUtil::instance().isolateModifierMask(event.state)) { // handle keybindings with state - if ((event.state & ControlMask) == ControlMask) { + + if (m_select_pos == -1 && (event.state & ShiftMask) == ShiftMask) { + m_select_pos = m_cursor_pos + m_start_pos; + } + + if ((event.state & ControlMask) == ControlMask) { - switch (ks) { - case XK_Left: { + switch (ks) { + case XK_Left: { + unsigned int pos = findEmptySpaceLeft(); + if (pos < m_start_pos){ + m_start_pos = pos; + m_cursor_pos = 0; + } else if (m_start_pos > 0) { + m_cursor_pos = pos - m_start_pos; + } else { + m_cursor_pos = pos; + } + adjustPos(); + } + break; + case XK_Right: + if (!m_text.logical().empty() && m_cursor_pos < m_text.logical().size()){ + unsigned int pos = findEmptySpaceRight(); + if (pos > m_start_pos) + pos -= m_start_pos; + else + pos = 0; + if (m_start_pos + pos <= m_end_pos) + m_cursor_pos = pos; + else if (m_end_pos < text().size()) { + m_cursor_pos = pos; + m_end_pos = pos; + } + + adjustPos(); + + } + break; + + case XK_BackSpace: { unsigned int pos = findEmptySpaceLeft(); + FbString t = text(); + t.erase(pos, m_cursor_pos - pos + m_start_pos); + m_text.setLogical(t); + if (pos < m_start_pos){ m_start_pos = pos; m_cursor_pos = 0;

@@ -247,54 +331,17 @@ m_cursor_pos = pos;

} adjustPos(); } - break; - case XK_Right: - if (!m_text.logical().empty() && m_cursor_pos < m_text.logical().size()){ - unsigned int pos = findEmptySpaceRight(); - if (pos > m_start_pos) - pos -= m_start_pos; - else - pos = 0; - if (m_start_pos + pos <= m_end_pos) - m_cursor_pos = pos; - else if (m_end_pos < text().size()) { - m_cursor_pos = pos; - m_end_pos = pos; - } - - adjustPos(); - - } - break; - - case XK_BackSpace: { - unsigned int pos = findEmptySpaceLeft(); - FbString t = text(); - t.erase(pos, m_cursor_pos - pos + m_start_pos); - m_text.setLogical(t); - - if (pos < m_start_pos){ - m_start_pos = pos; - m_cursor_pos = 0; - } else if (m_start_pos > 0) { - m_cursor_pos = pos - m_start_pos; - } else { - m_cursor_pos = pos; - } - adjustPos(); - } - break; - case XK_Delete: { - if (text().empty() || m_cursor_pos >= text().size()) - break; - unsigned int pos = findEmptySpaceRight(); - FbString t = text(); - t.erase(m_cursor_pos + m_start_pos, pos - (m_cursor_pos + m_start_pos)); - m_text.setLogical(t); - adjustPos(); - } - break; + break; + case XK_Delete: { + if (text().empty() || m_cursor_pos >= text().size()) + break; + unsigned int pos = findEmptySpaceRight(); + FbString t = text(); + t.erase(m_cursor_pos + m_start_pos, pos - (m_cursor_pos + m_start_pos)); + m_text.setLogical(t); + adjustPos(); } + break; } } else { // no state

@@ -358,6 +405,8 @@ std::string val;

val += keychar[0]; insertText(val); } + if ((event.state & ShiftMask) != ShiftMask) + m_select_pos = -1; clear(); }

@@ -369,6 +418,7 @@ }

void TextBox::adjustEndPos() { m_end_pos = text().size(); + m_start_pos = std::min(m_start_pos, m_end_pos); int text_width = font().textWidth(text().c_str() + m_start_pos, m_end_pos - m_start_pos); while (text_width > static_cast<signed>(width())) { m_end_pos--;

@@ -381,7 +431,7 @@

const char* visual = m_text.visual().c_str(); int text_width = font().textWidth(visual, m_end_pos); - if (text_width < static_cast<signed>(width())) + if (m_cursor_pos > -1 && text_width < static_cast<signed>(width())) return; int start_pos = 0;

@@ -440,4 +490,39 @@ else

adjustStartPos(); } + + +void TextBox::select(std::string::size_type pos, int length) +{ + if (length < 0) { + length = -length; + pos = pos >= length ? pos - length : 0; + } + + if (length > 0 && pos < text().size()) { + m_select_pos = pos; + pos = std::min(text().size(), pos + length); + + if (pos > m_start_pos) + pos -= m_start_pos; + else + pos = 0; + if (m_start_pos + pos <= m_end_pos) + m_cursor_pos = pos; + else if (m_end_pos < text().size()) { + m_cursor_pos = pos; + m_end_pos = pos; + } + + adjustPos(); + } else { + m_select_pos = -1; + } + clear(); +} + +void TextBox::selectAll() { + select(0, m_text.visual().size()); +} + } // end namespace FbTk
M src/FbTk/TextBox.hhsrc/FbTk/TextBox.hh

@@ -65,6 +65,10 @@ GC gc() const { return m_gc; }

int cursorPosition() const { return m_cursor_pos; } int textStartPos() const { return m_start_pos; } + bool hasSelection() const { return m_select_pos != -1 && m_select_pos != m_cursor_pos + m_start_pos; } + void select(std::string::size_type pos, int length); + void selectAll(); + unsigned int findEmptySpaceLeft(); unsigned int findEmptySpaceRight();

@@ -77,7 +81,7 @@

const FbTk::Font *m_font; BiDiString m_text; GC m_gc; - std::string::size_type m_cursor_pos, m_start_pos, m_end_pos; + std::string::size_type m_cursor_pos, m_start_pos, m_end_pos, m_select_pos; }; } // end namespace FbTk