aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lübking <thomas.luebking@gmail.com>2016-04-24 05:57:08 (GMT)
committerMathias Gumz <akira@fluxbox.org>2016-04-25 17:04:13 (GMT)
commit58b50fb786a15fb1740a3d900e271d0baa6b5482 (patch)
tree74641aec921d24ccc47f32b268dec1f8e61261f6
parent2e8766174eaa11e7f370ba5927ed38e18efced11 (diff)
downloadfluxbox-58b50fb786a15fb1740a3d900e271d0baa6b5482.zip
fluxbox-58b50fb786a15fb1740a3d900e271d0baa6b5482.tar.bz2
Allow text selection
FbTk::TextBox now acts like any contemporary input field ;-)
-rw-r--r--src/FbTk/TextBox.cc207
-rw-r--r--src/FbTk/TextBox.hh6
2 files changed, 151 insertions, 62 deletions
diff --git a/src/FbTk/TextBox.cc b/src/FbTk/TextBox.cc
index 74771a6..43b8573 100644
--- a/src/FbTk/TextBox.cc
+++ b/src/FbTk/TextBox.cc
@@ -53,7 +53,8 @@ TextBox::TextBox(int screen_num,
53 m_gc(0), 53 m_gc(0),
54 m_cursor_pos(0), 54 m_cursor_pos(0),
55 m_start_pos(0), 55 m_start_pos(0),
56 m_end_pos(0) { 56 m_end_pos(0),
57 m_select_pos(-1) {
57 58
58 FbTk::EventManager::instance()->add(*this, *this); 59 FbTk::EventManager::instance()->add(*this, *this);
59} 60}
@@ -128,6 +129,9 @@ void TextBox::cursorBackward() {
128} 129}
129 130
130void TextBox::backspace() { 131void TextBox::backspace() {
132 if (hasSelection())
133 return deleteForward();
134
131 if (m_start_pos || cursorPosition()) { 135 if (m_start_pos || cursorPosition()) {
132 FbString t = text(); 136 FbString t = text();
133 t.erase(m_start_pos + cursorPosition() - 1, 1); 137 t.erase(m_start_pos + cursorPosition() - 1, 1);
@@ -141,15 +145,27 @@ void TextBox::backspace() {
141} 145}
142 146
143void TextBox::deleteForward() { 147void TextBox::deleteForward() {
144 if (m_start_pos + m_cursor_pos < m_end_pos) { 148 std::string::size_type pos = m_start_pos + m_cursor_pos;
149 int length = 1;
150 if (hasSelection()) {
151 pos = std::min(m_start_pos + m_cursor_pos, m_select_pos);
152 length = std::max(m_start_pos + m_cursor_pos, m_select_pos) - pos;
153 m_cursor_pos = pos - m_start_pos;
154 }
155 if (pos < m_end_pos) {
145 FbString t = text(); 156 FbString t = text();
146 t.erase(m_start_pos + m_cursor_pos, 1); 157 t.erase(pos, length);
147 m_text.setLogical(t); 158 m_text.setLogical(t);
148 adjustEndPos(); 159 adjustEndPos();
149 } 160 }
161 if (length > 1)
162 adjustStartPos();
150} 163}
151 164
152void TextBox::insertText(const std::string &val) { 165void TextBox::insertText(const std::string &val) {
166 if (hasSelection())
167 deleteForward();
168
153 FbString t = text(); 169 FbString t = text();
154 t.insert(m_start_pos + cursorPosition(), val); 170 t.insert(m_start_pos + cursorPosition(), val);
155 m_text.setLogical(t); 171 m_text.setLogical(t);
@@ -168,20 +184,48 @@ void TextBox::killToEnd() {
168} 184}
169 185
170void TextBox::clear() { 186void TextBox::clear() {
187 Display *dpy = FbTk::App::instance()->display();
171 FbWindow::clear(); 188 FbWindow::clear();
172 // center text by default 189 // center text by default
173 int center_pos = (height() + font().ascent())/2; 190 int center_pos = (height() + font().ascent())/2;
174 if (gc() == 0) 191 if (gc() == 0)
175 setGC(DefaultGC(FbTk::App::instance()->display(), screenNumber())); 192 setGC(DefaultGC(dpy, screenNumber()));
193
176 194
177 font().drawText(*this, screenNumber(), 195 int cursor_pos = font().textWidth(m_text.visual().c_str() + m_start_pos, m_cursor_pos);
178 gc(), 196
197 font().drawText(*this, screenNumber(), gc(),
179 m_text.visual().c_str() + m_start_pos, 198 m_text.visual().c_str() + m_start_pos,
180 m_end_pos - m_start_pos, 199 m_end_pos - m_start_pos, -1, center_pos); // pos
181 0, center_pos); // pos 200
201 if (hasSelection()) {
202 int select_pos = m_select_pos <= m_start_pos ? 0 :
203 font().textWidth(m_text.visual().c_str() + m_start_pos,
204 m_select_pos - m_start_pos);
205 int start = std::max(m_start_pos, std::min(m_start_pos + m_cursor_pos, m_select_pos));
206 int length = std::max(m_start_pos + m_cursor_pos, m_select_pos) - start;
207 int x = std::min(select_pos, cursor_pos);
208 int width = std::abs(select_pos - cursor_pos);
209
210 XGCValues backup;
211 XGetGCValues(dpy, gc(), GCForeground|GCBackground, &backup);
212 XSetForeground(dpy, gc(), backup.foreground);
213
214 fillRectangle(gc(), x, 0, width, height());
215
216 XColor c;
217 c.pixel = backup.foreground;
218 XQueryColor(dpy, DefaultColormap(dpy, screenNumber()), &c);
219 XSetForeground(dpy, gc(), c.red + c.green + c.blue > 0x17ffe ?
220 BlackPixel(dpy, screenNumber()) :
221 WhitePixel(dpy, screenNumber()));
222 font().drawText(*this, screenNumber(), gc(),
223 m_text.visual().c_str() + start, length, x, center_pos); // pos
224 XSetForeground(dpy, gc(), backup.foreground);
225 }
226
182 227
183 // draw cursor position 228 // draw cursor position
184 int cursor_pos = font().textWidth(m_text.visual().c_str() + m_start_pos, m_cursor_pos) + 1;
185 drawLine(gc(), cursor_pos, center_pos, cursor_pos, center_pos - font().height()); 229 drawLine(gc(), cursor_pos, center_pos, cursor_pos, center_pos - font().height());
186} 230}
187 231
@@ -231,12 +275,52 @@ void TextBox::keyPressEvent(XKeyEvent &event) {
231 // a modifier key by itself doesn't do anything 275 // a modifier key by itself doesn't do anything
232 if (IsModifierKey(ks)) return; 276 if (IsModifierKey(ks)) return;
233 277
234 if (FbTk::KeyUtil::instance().isolateModifierMask(event.state)) { // handle keybindings with state
235 if ((event.state & ControlMask) == ControlMask) {
236 278
237 switch (ks) { 279 if (m_select_pos == -1 && (event.state & ShiftMask) == ShiftMask) {
238 case XK_Left: { 280 m_select_pos = m_cursor_pos + m_start_pos;
281 }
282
283 if ((event.state & ControlMask) == ControlMask) {
284
285 switch (ks) {
286 case XK_Left: {
287 unsigned int pos = findEmptySpaceLeft();
288 if (pos < m_start_pos){
289 m_start_pos = pos;
290 m_cursor_pos = 0;
291 } else if (m_start_pos > 0) {
292 m_cursor_pos = pos - m_start_pos;
293 } else {
294 m_cursor_pos = pos;
295 }
296 adjustPos();
297 }
298 break;
299 case XK_Right:
300 if (!m_text.logical().empty() && m_cursor_pos < m_text.logical().size()){
301 unsigned int pos = findEmptySpaceRight();
302 if (pos > m_start_pos)
303 pos -= m_start_pos;
304 else
305 pos = 0;
306 if (m_start_pos + pos <= m_end_pos)
307 m_cursor_pos = pos;
308 else if (m_end_pos < text().size()) {
309 m_cursor_pos = pos;
310 m_end_pos = pos;
311 }
312
313 adjustPos();
314
315 }
316 break;
317
318 case XK_BackSpace: {
239 unsigned int pos = findEmptySpaceLeft(); 319 unsigned int pos = findEmptySpaceLeft();
320 FbString t = text();
321 t.erase(pos, m_cursor_pos - pos + m_start_pos);
322 m_text.setLogical(t);
323
240 if (pos < m_start_pos){ 324 if (pos < m_start_pos){
241 m_start_pos = pos; 325 m_start_pos = pos;
242 m_cursor_pos = 0; 326 m_cursor_pos = 0;
@@ -247,54 +331,17 @@ void TextBox::keyPressEvent(XKeyEvent &event) {
247 } 331 }
248 adjustPos(); 332 adjustPos();
249 } 333 }
250 break; 334 break;
251 case XK_Right: 335 case XK_Delete: {
252 if (!m_text.logical().empty() && m_cursor_pos < m_text.logical().size()){ 336 if (text().empty() || m_cursor_pos >= text().size())
253 unsigned int pos = findEmptySpaceRight(); 337 break;
254 if (pos > m_start_pos) 338 unsigned int pos = findEmptySpaceRight();
255 pos -= m_start_pos; 339 FbString t = text();
256 else 340 t.erase(m_cursor_pos + m_start_pos, pos - (m_cursor_pos + m_start_pos));
257 pos = 0; 341 m_text.setLogical(t);
258 if (m_start_pos + pos <= m_end_pos) 342 adjustPos();
259 m_cursor_pos = pos;
260 else if (m_end_pos < text().size()) {
261 m_cursor_pos = pos;
262 m_end_pos = pos;
263 }
264
265 adjustPos();
266
267 }
268 break;
269
270 case XK_BackSpace: {
271 unsigned int pos = findEmptySpaceLeft();
272 FbString t = text();
273 t.erase(pos, m_cursor_pos - pos + m_start_pos);
274 m_text.setLogical(t);
275
276 if (pos < m_start_pos){
277 m_start_pos = pos;
278 m_cursor_pos = 0;
279 } else if (m_start_pos > 0) {
280 m_cursor_pos = pos - m_start_pos;
281 } else {
282 m_cursor_pos = pos;
283 }
284 adjustPos();
285 }
286 break;
287 case XK_Delete: {
288 if (text().empty() || m_cursor_pos >= text().size())
289 break;
290 unsigned int pos = findEmptySpaceRight();
291 FbString t = text();
292 t.erase(m_cursor_pos + m_start_pos, pos - (m_cursor_pos + m_start_pos));
293 m_text.setLogical(t);
294 adjustPos();
295 }
296 break;
297 } 343 }
344 break;
298 } 345 }
299 346
300 } else { // no state 347 } else { // no state
@@ -358,6 +405,8 @@ void TextBox::keyPressEvent(XKeyEvent &event) {
358 val += keychar[0]; 405 val += keychar[0];
359 insertText(val); 406 insertText(val);
360 } 407 }
408 if ((event.state & ShiftMask) != ShiftMask)
409 m_select_pos = -1;
361 clear(); 410 clear();
362} 411}
363 412
@@ -369,6 +418,7 @@ void TextBox::setCursorPosition(int pos) {
369 418
370void TextBox::adjustEndPos() { 419void TextBox::adjustEndPos() {
371 m_end_pos = text().size(); 420 m_end_pos = text().size();
421 m_start_pos = std::min(m_start_pos, m_end_pos);
372 int text_width = font().textWidth(text().c_str() + m_start_pos, m_end_pos - m_start_pos); 422 int text_width = font().textWidth(text().c_str() + m_start_pos, m_end_pos - m_start_pos);
373 while (text_width > static_cast<signed>(width())) { 423 while (text_width > static_cast<signed>(width())) {
374 m_end_pos--; 424 m_end_pos--;
@@ -381,7 +431,7 @@ void TextBox::adjustStartPos() {
381 const char* visual = m_text.visual().c_str(); 431 const char* visual = m_text.visual().c_str();
382 432
383 int text_width = font().textWidth(visual, m_end_pos); 433 int text_width = font().textWidth(visual, m_end_pos);
384 if (text_width < static_cast<signed>(width())) 434 if (m_cursor_pos > -1 && text_width < static_cast<signed>(width()))
385 return; 435 return;
386 436
387 int start_pos = 0; 437 int start_pos = 0;
@@ -440,4 +490,39 @@ void TextBox::adjustPos(){
440 adjustStartPos(); 490 adjustStartPos();
441 491
442} 492}
493
494
495void TextBox::select(std::string::size_type pos, int length)
496{
497 if (length < 0) {
498 length = -length;
499 pos = pos >= length ? pos - length : 0;
500 }
501
502 if (length > 0 && pos < text().size()) {
503 m_select_pos = pos;
504 pos = std::min(text().size(), pos + length);
505
506 if (pos > m_start_pos)
507 pos -= m_start_pos;
508 else
509 pos = 0;
510 if (m_start_pos + pos <= m_end_pos)
511 m_cursor_pos = pos;
512 else if (m_end_pos < text().size()) {
513 m_cursor_pos = pos;
514 m_end_pos = pos;
515 }
516
517 adjustPos();
518 } else {
519 m_select_pos = -1;
520 }
521 clear();
522}
523
524void TextBox::selectAll() {
525 select(0, m_text.visual().size());
526}
527
443} // end namespace FbTk 528} // end namespace FbTk
diff --git a/src/FbTk/TextBox.hh b/src/FbTk/TextBox.hh
index 40a7c3c..2c1ef9d 100644
--- a/src/FbTk/TextBox.hh
+++ b/src/FbTk/TextBox.hh
@@ -65,6 +65,10 @@ public:
65 int cursorPosition() const { return m_cursor_pos; } 65 int cursorPosition() const { return m_cursor_pos; }
66 int textStartPos() const { return m_start_pos; } 66 int textStartPos() const { return m_start_pos; }
67 67
68 bool hasSelection() const { return m_select_pos != -1 && m_select_pos != m_cursor_pos + m_start_pos; }
69 void select(std::string::size_type pos, int length);
70 void selectAll();
71
68 unsigned int findEmptySpaceLeft(); 72 unsigned int findEmptySpaceLeft();
69 unsigned int findEmptySpaceRight(); 73 unsigned int findEmptySpaceRight();
70 74
@@ -77,7 +81,7 @@ private:
77 const FbTk::Font *m_font; 81 const FbTk::Font *m_font;
78 BiDiString m_text; 82 BiDiString m_text;
79 GC m_gc; 83 GC m_gc;
80 std::string::size_type m_cursor_pos, m_start_pos, m_end_pos; 84 std::string::size_type m_cursor_pos, m_start_pos, m_end_pos, m_select_pos;
81}; 85};
82 86
83} // end namespace FbTk 87} // end namespace FbTk