Skip to content

Commit 58a91fd

Browse files
committed
TextBox: edit selected text with Shift key
1 parent cfa4af5 commit 58a91fd

File tree

2 files changed

+99
-15
lines changed

2 files changed

+99
-15
lines changed

src/Gui/TextBox.cpp

+94-15
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ namespace gui
1111
TextBox::TextBox(float width):
1212
m_box(Box::Input),
1313
m_cursorPos(0),
14-
m_maxLength(256)
14+
m_maxLength(256),
15+
m_selectionFirst(0),
16+
m_selectionLast(0)
1517
{
1618
m_box.setSize(width, Theme::getBoxHeight());
1719

@@ -74,6 +76,8 @@ void TextBox::setCursor(size_t index)
7476
if (index <= m_text.getString().getSize())
7577
{
7678
m_cursorPos = index;
79+
m_selectionFirst = index;
80+
m_selectionLast = index;
7781

7882
float padding = Theme::borderSize + Theme::PADDING;
7983
m_cursor.setPosition(m_text.findCharacterPos(index).x, padding);
@@ -123,11 +127,59 @@ void TextBox::onKeyPressed(const sf::Event::KeyEvent& key)
123127
switch (key.code)
124128
{
125129
case sf::Keyboard::Left:
126-
setCursor(m_cursorPos - 1);
130+
if (key.shift)
131+
{
132+
if (m_cursorPos == m_selectionLast)
133+
{
134+
// Extend selection to left
135+
if (m_selectionFirst > 0)
136+
setSelectedText(m_selectionFirst - 1, m_selectionLast);
137+
}
138+
else
139+
{
140+
// Shrink selection to right
141+
setSelectedText(m_selectionFirst, m_selectionLast - 1);
142+
}
143+
}
144+
else if (m_selectedText.isEmpty())
145+
{
146+
// Move cursor to the left
147+
setCursor(m_cursorPos - 1);
148+
}
149+
else
150+
{
151+
// Clear selection, move cursor to the left
152+
setCursor(m_selectionFirst);
153+
clearSelectedText();
154+
}
127155
break;
128156

129157
case sf::Keyboard::Right:
130-
setCursor(m_cursorPos + 1);
158+
if (key.shift)
159+
{
160+
if (m_cursorPos == m_selectionFirst)
161+
{
162+
// Extend selection to right
163+
if (m_selectionLast < m_text.getString().getSize())
164+
setSelectedText(m_selectionFirst, m_selectionLast + 1);
165+
}
166+
else
167+
{
168+
// Shrink selection to left
169+
setSelectedText(m_selectionFirst + 1, m_selectionLast);
170+
}
171+
}
172+
else if (m_selectedText.isEmpty())
173+
{
174+
// Move cursor to the right
175+
setCursor(m_cursorPos + 1);
176+
}
177+
else
178+
{
179+
// Clear selection, move cursor to the right
180+
setCursor(m_selectionLast);
181+
clearSelectedText();
182+
}
131183
break;
132184

133185
case sf::Keyboard::BackSpace:
@@ -165,11 +217,27 @@ void TextBox::onKeyPressed(const sf::Event::KeyEvent& key)
165217
break;
166218

167219
case sf::Keyboard::Home:
168-
setCursor(0);
220+
if (key.shift)
221+
{
222+
// Shift+Home: select from start to cursor
223+
setSelectedText(0, m_cursorPos);
224+
}
225+
else
226+
{
227+
setCursor(0);
228+
}
169229
break;
170230

171231
case sf::Keyboard::End:
172-
setCursor(m_text.getString().getSize());
232+
if (key.shift)
233+
{
234+
// Shift+End: select from cursor to end
235+
setSelectedText(m_cursorPos, m_text.getString().getSize());
236+
}
237+
else
238+
{
239+
setCursor(m_text.getString().getSize());
240+
}
173241
break;
174242

175243
case sf::Keyboard::Return:
@@ -269,7 +337,6 @@ void TextBox::onMouseReleased(float x, float)
269337
if (glyphPos.x <= x)
270338
{
271339
setSelectedText(m_cursorPos, i);
272-
setCursor(i);
273340
break;
274341
}
275342
}
@@ -319,7 +386,7 @@ void TextBox::onStateChanged(State state)
319386
// Discard selection when focus is lost
320387
if (state != State::StateFocused)
321388
{
322-
setSelectedText(0, 0);
389+
clearSelectedText();
323390
}
324391
}
325392

@@ -356,8 +423,8 @@ void TextBox::draw(sf::RenderTarget& target, sf::RenderStates states) const
356423

357424
glDisable(GL_SCISSOR_TEST);
358425

359-
// Show cursor if focused
360-
if (isFocused())
426+
// Show cursor if focused and no selection
427+
if (isFocused() && m_selectedText.isEmpty())
361428
{
362429
// Make it blink
363430
float timer = m_cursorTimer.getElapsedTime().asSeconds();
@@ -378,17 +445,29 @@ void TextBox::setSelectedText(size_t from, size_t to)
378445
{
379446
if (from != to)
380447
{
381-
m_selectionLast = std::max(from, to);
382-
m_selectionFirst = std::min(from, to);
383-
m_selectedText = m_text.getString().substring(m_selectionFirst, m_selectionLast - m_selectionFirst);
448+
size_t selectionLast = std::max(from, to);
449+
size_t selectionFirst = std::min(from, to);
450+
if (selectionFirst != m_selectionFirst || selectionLast != m_selectionLast)
451+
{
452+
m_selectionFirst = selectionFirst;
453+
m_selectionLast = selectionLast;
454+
m_selectedText = m_text.getString().substring(m_selectionFirst, m_selectionLast - m_selectionFirst);
455+
}
384456
}
385457
else
386458
{
387-
m_selectionFirst = m_selectionLast = 0;
388-
m_selectedText.clear();
459+
clearSelectedText();
389460
}
390461
}
391462

463+
464+
void TextBox::clearSelectedText()
465+
{
466+
m_selectionFirst = m_selectionLast = m_cursorPos;
467+
m_selectedText.clear();
468+
}
469+
470+
392471
const sf::String& TextBox::getSelectedText() const
393472
{
394473
return m_selectedText;
@@ -403,7 +482,7 @@ void TextBox::deleteSelectedText()
403482
sf::String str = m_text.getString();
404483
str.erase(m_selectionFirst, m_selectionLast - m_selectionFirst);
405484
setCursor(m_selectionFirst);
406-
setSelectedText(0, 0);
485+
clearSelectedText();
407486
m_text.setString(str);
408487
}
409488
}

src/Gui/TextBox.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ class TextBox: public Widget
5151
*/
5252
const sf::String& getSelectedText() const;
5353

54+
/**
55+
* Cancel the text selection range, if any
56+
*/
57+
void clearSelectedText();
58+
5459
/**
5560
* Set placeholder text
5661
*/

0 commit comments

Comments
 (0)