| [26288] | 1 | /* Copyright (C) 2022 Wildfire Games.
|
|---|
| [27965] | 2 | * This file is part of 0 A.D.
|
|---|
| [6830] | 3 | *
|
|---|
| [27965] | 4 | * 0 A.D. is free software: you can redistribute it and/or modify
|
|---|
| [6830] | 5 | * it under the terms of the GNU General Public License as published by
|
|---|
| 6 | * the Free Software Foundation, either version 2 of the License, or
|
|---|
| 7 | * (at your option) any later version.
|
|---|
| 8 | *
|
|---|
| [27965] | 9 | * 0 A.D. is distributed in the hope that it will be useful,
|
|---|
| [6830] | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 12 | * GNU General Public License for more details.
|
|---|
| 13 | *
|
|---|
| 14 | * You should have received a copy of the GNU General Public License
|
|---|
| [27965] | 15 | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|---|
| [6830] | 16 | */
|
|---|
| 17 |
|
|---|
| [16931] | 18 | #include "precompiled.h"
|
|---|
| [1237] | 19 |
|
|---|
| 20 | #include "CInput.h"
|
|---|
| [16931] | 21 |
|
|---|
| [25607] | 22 | #include "graphics/Canvas2D.h"
|
|---|
| [14016] | 23 | #include "graphics/FontMetrics.h"
|
|---|
| [10985] | 24 | #include "graphics/TextRenderer.h"
|
|---|
| [23028] | 25 | #include "gui/CGUI.h"
|
|---|
| 26 | #include "gui/CGUIScrollBarVertical.h"
|
|---|
| [13068] | 27 | #include "lib/timer.h"
|
|---|
| [15767] | 28 | #include "lib/utf8.h"
|
|---|
| [13068] | 29 | #include "ps/ConfigDB.h"
|
|---|
| [25457] | 30 | #include "ps/CStrInternStatic.h"
|
|---|
| [18580] | 31 | #include "ps/GameSetup/Config.h"
|
|---|
| [2954] | 32 | #include "ps/Globals.h"
|
|---|
| [10985] | 33 | #include "ps/Hotkey.h"
|
|---|
| [1752] | 34 |
|
|---|
| [9646] | 35 | #include <sstream>
|
|---|
| [1394] | 36 |
|
|---|
| [10985] | 37 | extern int g_yres;
|
|---|
| 38 |
|
|---|
| [23403] | 39 | const CStr CInput::EventNameTextEdit = "TextEdit";
|
|---|
| 40 | const CStr CInput::EventNamePress = "Press";
|
|---|
| 41 | const CStr CInput::EventNameTab = "Tab";
|
|---|
| 42 |
|
|---|
| [22741] | 43 | CInput::CInput(CGUI& pGUI)
|
|---|
| [23005] | 44 | :
|
|---|
| 45 | IGUIObject(pGUI),
|
|---|
| [23020] | 46 | IGUIScrollBarOwner(*static_cast<IGUIObject*>(this)),
|
|---|
| [23005] | 47 | m_iBufferPos(-1),
|
|---|
| 48 | m_iBufferPos_Tail(-1),
|
|---|
| 49 | m_SelectingText(),
|
|---|
| 50 | m_HorizontalScroll(),
|
|---|
| 51 | m_PrevTime(),
|
|---|
| 52 | m_CursorVisState(true),
|
|---|
| 53 | m_CursorBlinkRate(0.5),
|
|---|
| 54 | m_ComposingText(),
|
|---|
| [24433] | 55 | m_GeneratedPlaceholderTextValid(false),
|
|---|
| [23005] | 56 | m_iComposedLength(),
|
|---|
| 57 | m_iComposedPos(),
|
|---|
| 58 | m_iInsertPos(),
|
|---|
| [25392] | 59 | m_BufferPosition(this, "buffer_position"),
|
|---|
| 60 | m_BufferZone(this, "buffer_zone"),
|
|---|
| 61 | m_Caption(this, "caption"),
|
|---|
| 62 | m_Font(this, "font"),
|
|---|
| 63 | m_MaskChar(this, "mask_char"),
|
|---|
| 64 | m_Mask(this, "mask"),
|
|---|
| 65 | m_MaxLength(this, "max_length"),
|
|---|
| 66 | m_MultiLine(this, "multiline"),
|
|---|
| 67 | m_Readonly(this, "readonly"),
|
|---|
| 68 | m_ScrollBar(this, "scrollbar"),
|
|---|
| 69 | m_ScrollBarStyle(this, "scrollbar_style"),
|
|---|
| 70 | m_Sprite(this, "sprite"),
|
|---|
| [25587] | 71 | m_SpriteOverlay(this, "sprite_overlay"),
|
|---|
| [25392] | 72 | m_SpriteSelectArea(this, "sprite_selectarea"),
|
|---|
| 73 | m_TextColor(this, "textcolor"),
|
|---|
| 74 | m_TextColorSelected(this, "textcolor_selected"),
|
|---|
| 75 | m_PlaceholderText(this, "placeholder_text"),
|
|---|
| 76 | m_PlaceholderColor(this, "placeholder_color")
|
|---|
| [1237] | 77 | {
|
|---|
| [15984] | 78 | CFG_GET_VAL("gui.cursorblinkrate", m_CursorBlinkRate);
|
|---|
| [13068] | 79 |
|
|---|
| [25693] | 80 | auto bar = std::make_unique<CGUIScrollBarVertical>(pGUI);
|
|---|
| [1237] | 81 | bar->SetRightAligned(true);
|
|---|
| [25693] | 82 | AddScrollBar(std::move(bar));
|
|---|
| [1237] | 83 | }
|
|---|
| 84 |
|
|---|
| 85 | CInput::~CInput()
|
|---|
| 86 | {
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| [16261] | 89 | void CInput::UpdateBufferPositionSetting()
|
|---|
| 90 | {
|
|---|
| [25392] | 91 | m_BufferPosition.Set(m_iBufferPos, false);
|
|---|
| [16261] | 92 | }
|
|---|
| 93 |
|
|---|
| [15830] | 94 | void CInput::ClearComposedText()
|
|---|
| 95 | {
|
|---|
| [25392] | 96 | m_Caption.GetMutable().erase(m_iInsertPos, m_iComposedLength);
|
|---|
| [15830] | 97 | m_iBufferPos = m_iInsertPos;
|
|---|
| [16261] | 98 | UpdateBufferPositionSetting();
|
|---|
| [15830] | 99 | m_iComposedLength = 0;
|
|---|
| 100 | m_iComposedPos = 0;
|
|---|
| 101 | }
|
|---|
| 102 |
|
|---|
| [24215] | 103 | InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
|
|---|
| [1237] | 104 | {
|
|---|
| [9362] | 105 | ENSURE(m_iBufferPos != -1);
|
|---|
| [1237] | 106 |
|
|---|
| [25392] | 107 | // Get direct access to silently mutate m_Caption.
|
|---|
| 108 | // (Messages don't currently need to be sent)
|
|---|
| 109 | CStrW& caption = m_Caption.GetMutable();
|
|---|
| 110 |
|
|---|
| [20078] | 111 | switch (ev->ev.type)
|
|---|
| [1752] | 112 | {
|
|---|
| [20078] | 113 | case SDL_HOTKEYDOWN:
|
|---|
| 114 | {
|
|---|
| [15785] | 115 | if (m_ComposingText)
|
|---|
| 116 | return IN_HANDLED;
|
|---|
| [20074] | 117 |
|
|---|
| 118 | return ManuallyHandleHotkeyEvent(ev);
|
|---|
| [1752] | 119 | }
|
|---|
| [15785] | 120 | // SDL2 has a new method of text input that better supports Unicode and CJK
|
|---|
| 121 | // see https://wiki.libsdl.org/Tutorials/TextInput
|
|---|
| [20078] | 122 | case SDL_TEXTINPUT:
|
|---|
| [15785] | 123 | {
|
|---|
| [20075] | 124 | if (m_Readonly)
|
|---|
| 125 | return IN_PASS;
|
|---|
| 126 |
|
|---|
| [15785] | 127 | // Text has been committed, either single key presses or through an IME
|
|---|
| 128 | std::wstring text = wstring_from_utf8(ev->ev.text.text);
|
|---|
| 129 |
|
|---|
| [23927] | 130 | // Check max length
|
|---|
| [25392] | 131 | if (m_MaxLength != 0 && caption.length() + text.length() > static_cast<size_t>(m_MaxLength))
|
|---|
| [23927] | 132 | return IN_HANDLED;
|
|---|
| 133 |
|
|---|
| [16931] | 134 | m_WantedX = 0.0f;
|
|---|
| [15785] | 135 |
|
|---|
| 136 | if (SelectingText())
|
|---|
| 137 | DeleteCurSelection();
|
|---|
| 138 |
|
|---|
| [15830] | 139 | if (m_ComposingText)
|
|---|
| 140 | {
|
|---|
| 141 | ClearComposedText();
|
|---|
| 142 | m_ComposingText = false;
|
|---|
| 143 | }
|
|---|
| 144 |
|
|---|
| [25392] | 145 | if (m_iBufferPos == static_cast<int>(caption.length()))
|
|---|
| 146 | caption.append(text);
|
|---|
| [15785] | 147 | else
|
|---|
| [25392] | 148 | caption.insert(m_iBufferPos, text);
|
|---|
| [15785] | 149 |
|
|---|
| 150 | UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
|
|---|
| [16244] | 151 |
|
|---|
| [15785] | 152 | m_iBufferPos += text.length();
|
|---|
| [16261] | 153 | UpdateBufferPositionSetting();
|
|---|
| [15785] | 154 | m_iBufferPos_Tail = -1;
|
|---|
| [16244] | 155 |
|
|---|
| [15785] | 156 | UpdateAutoScroll();
|
|---|
| [23403] | 157 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [15785] | 158 |
|
|---|
| 159 | return IN_HANDLED;
|
|---|
| 160 | }
|
|---|
| [20078] | 161 | case SDL_TEXTEDITING:
|
|---|
| [15785] | 162 | {
|
|---|
| [20075] | 163 | if (m_Readonly)
|
|---|
| 164 | return IN_PASS;
|
|---|
| 165 |
|
|---|
| [15785] | 166 | // Text is being composed with an IME
|
|---|
| 167 | // TODO: indicate this by e.g. underlining the uncommitted text
|
|---|
| [16931] | 168 | const char* rawText = ev->ev.edit.text;
|
|---|
| [15830] | 169 | int rawLength = strlen(rawText);
|
|---|
| 170 | std::wstring wtext = wstring_from_utf8(rawText);
|
|---|
| [16244] | 171 |
|
|---|
| [16931] | 172 | m_WantedX = 0.0f;
|
|---|
| [15785] | 173 |
|
|---|
| [15830] | 174 | if (SelectingText())
|
|---|
| [15785] | 175 | DeleteCurSelection();
|
|---|
| 176 |
|
|---|
| [15830] | 177 | // Remember cursor position when text composition begins
|
|---|
| 178 | if (!m_ComposingText)
|
|---|
| 179 | m_iInsertPos = m_iBufferPos;
|
|---|
| [15785] | 180 | else
|
|---|
| [15830] | 181 | {
|
|---|
| 182 | // Composed text is replaced each time
|
|---|
| 183 | ClearComposedText();
|
|---|
| 184 | }
|
|---|
| [15785] | 185 |
|
|---|
| [15830] | 186 | m_ComposingText = ev->ev.edit.start != 0 || rawLength != 0;
|
|---|
| 187 | if (m_ComposingText)
|
|---|
| 188 | {
|
|---|
| [25392] | 189 | caption.insert(m_iInsertPos, wtext);
|
|---|
| [15830] | 190 |
|
|---|
| 191 | // The text buffer is limited to SDL_TEXTEDITINGEVENT_TEXT_SIZE bytes, yet start
|
|---|
| 192 | // increases without limit, so don't let it advance beyond the composed text length
|
|---|
| 193 | m_iComposedLength = wtext.length();
|
|---|
| 194 | m_iComposedPos = ev->ev.edit.start < m_iComposedLength ? ev->ev.edit.start : m_iComposedLength;
|
|---|
| 195 | m_iBufferPos = m_iInsertPos + m_iComposedPos;
|
|---|
| [16244] | 196 |
|
|---|
| [15830] | 197 | // TODO: composed text selection - what does ev.edit.length do?
|
|---|
| 198 | m_iBufferPos_Tail = -1;
|
|---|
| 199 | }
|
|---|
| [16244] | 200 |
|
|---|
| [16261] | 201 | UpdateBufferPositionSetting();
|
|---|
| [15785] | 202 | UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
|
|---|
| 203 |
|
|---|
| 204 | UpdateAutoScroll();
|
|---|
| [23403] | 205 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [15785] | 206 |
|
|---|
| 207 | return IN_HANDLED;
|
|---|
| 208 | }
|
|---|
| [20078] | 209 | case SDL_KEYDOWN:
|
|---|
| [25180] | 210 | case SDL_KEYUP:
|
|---|
| [1752] | 211 | {
|
|---|
| [9646] | 212 | // Since the GUI framework doesn't handle to set settings
|
|---|
| 213 | // in Unicode (CStrW), we'll simply retrieve the actual
|
|---|
| 214 | // pointer and edit that.
|
|---|
| [20078] | 215 | SDL_Keycode keyCode = ev->ev.key.keysym.sym;
|
|---|
| [9646] | 216 |
|
|---|
| [25180] | 217 | // We have a probably printable key - we should return HANDLED so it can't trigger hotkeys.
|
|---|
| 218 | // However, if Ctrl/Meta modifiers are active, just pass it through instead,
|
|---|
| 219 | // assuming that we are indeed trying to trigger hotkeys (e.g. copy/paste).
|
|---|
| 220 | // Escape & the "cancel" hotkey are also passed through to allow closing dialogs easily.
|
|---|
| 221 | // See also similar logic in CConsole.cpp
|
|---|
| 222 | // NB: this assumes that Ctrl/GUI aren't used in the Manually* functions below,
|
|---|
| 223 | // as those code paths would obviously never be taken.
|
|---|
| 224 | if (keyCode == SDLK_ESCAPE || EventWillFireHotkey(ev, "cancel") ||
|
|---|
| [24648] | 225 | g_scancodes[SDL_SCANCODE_LCTRL] || g_scancodes[SDL_SCANCODE_RCTRL] ||
|
|---|
| [24645] | 226 | g_scancodes[SDL_SCANCODE_LGUI] || g_scancodes[SDL_SCANCODE_RGUI])
|
|---|
| [24215] | 227 | return IN_PASS;
|
|---|
| 228 |
|
|---|
| [24278] | 229 | if (m_ComposingText)
|
|---|
| 230 | return IN_HANDLED;
|
|---|
| 231 |
|
|---|
| [25180] | 232 | if (ev->ev.type == SDL_KEYDOWN)
|
|---|
| 233 | {
|
|---|
| 234 | ManuallyImmutableHandleKeyDownEvent(keyCode);
|
|---|
| 235 | ManuallyMutableHandleKeyDownEvent(keyCode);
|
|---|
| [1237] | 236 |
|
|---|
| [25180] | 237 | UpdateBufferPositionSetting();
|
|---|
| 238 | }
|
|---|
| [20074] | 239 | return IN_HANDLED;
|
|---|
| 240 | }
|
|---|
| [20078] | 241 | default:
|
|---|
| 242 | {
|
|---|
| 243 | return IN_PASS;
|
|---|
| 244 | }
|
|---|
| 245 | }
|
|---|
| [20074] | 246 | }
|
|---|
| [1237] | 247 |
|
|---|
| [23005] | 248 | void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode)
|
|---|
| [20074] | 249 | {
|
|---|
| [20075] | 250 | if (m_Readonly)
|
|---|
| 251 | return;
|
|---|
| 252 |
|
|---|
| [20074] | 253 | wchar_t cooked = 0;
|
|---|
| [1904] | 254 |
|
|---|
| [25392] | 255 | // Get direct access to silently mutate m_Caption.
|
|---|
| 256 | // (Messages don't currently need to be sent)
|
|---|
| 257 | CStrW& caption = m_Caption.GetMutable();
|
|---|
| 258 |
|
|---|
| [20074] | 259 | switch (keyCode)
|
|---|
| 260 | {
|
|---|
| 261 | case SDLK_TAB:
|
|---|
| 262 | {
|
|---|
| [23403] | 263 | SendEvent(GUIM_TAB, EventNameTab);
|
|---|
| [20091] | 264 | // Don't send a textedit event, because it should only
|
|---|
| 265 | // be sent if the GUI control changes the text
|
|---|
| [20074] | 266 | break;
|
|---|
| 267 | }
|
|---|
| 268 | case SDLK_BACKSPACE:
|
|---|
| 269 | {
|
|---|
| 270 | m_WantedX = 0.0f;
|
|---|
| [1237] | 271 |
|
|---|
| [20074] | 272 | if (SelectingText())
|
|---|
| 273 | DeleteCurSelection();
|
|---|
| 274 | else
|
|---|
| 275 | {
|
|---|
| 276 | m_iBufferPos_Tail = -1;
|
|---|
| [1904] | 277 |
|
|---|
| [25392] | 278 | if (caption.empty() || m_iBufferPos == 0)
|
|---|
| [20074] | 279 | break;
|
|---|
| [1237] | 280 |
|
|---|
| [25392] | 281 | if (m_iBufferPos == static_cast<int>(caption.length()))
|
|---|
| 282 | caption = caption.Left(static_cast<long>(caption.length()) - 1);
|
|---|
| [20074] | 283 | else
|
|---|
| [25392] | 284 | caption =
|
|---|
| 285 | caption.Left(m_iBufferPos - 1) +
|
|---|
| 286 | caption.Right(static_cast<long>(caption.length()) - m_iBufferPos);
|
|---|
| [1904] | 287 |
|
|---|
| [20074] | 288 | --m_iBufferPos;
|
|---|
| [1904] | 289 |
|
|---|
| [20074] | 290 | UpdateText(m_iBufferPos, m_iBufferPos + 1, m_iBufferPos);
|
|---|
| 291 | }
|
|---|
| [1904] | 292 |
|
|---|
| [20074] | 293 | UpdateAutoScroll();
|
|---|
| [23403] | 294 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [20074] | 295 | break;
|
|---|
| 296 | }
|
|---|
| 297 | case SDLK_DELETE:
|
|---|
| 298 | {
|
|---|
| 299 | m_WantedX = 0.0f;
|
|---|
| [1904] | 300 |
|
|---|
| [20074] | 301 | if (SelectingText())
|
|---|
| 302 | DeleteCurSelection();
|
|---|
| 303 | else
|
|---|
| 304 | {
|
|---|
| [25392] | 305 | if (caption.empty() || m_iBufferPos == static_cast<int>(caption.length()))
|
|---|
| [20074] | 306 | break;
|
|---|
| [1237] | 307 |
|
|---|
| [25392] | 308 | caption =
|
|---|
| 309 | caption.Left(m_iBufferPos) +
|
|---|
| 310 | caption.Right(static_cast<long>(caption.length()) - (m_iBufferPos + 1));
|
|---|
| [1904] | 311 |
|
|---|
| [20074] | 312 | UpdateText(m_iBufferPos, m_iBufferPos + 1, m_iBufferPos);
|
|---|
| 313 | }
|
|---|
| [1904] | 314 |
|
|---|
| [20074] | 315 | UpdateAutoScroll();
|
|---|
| [23403] | 316 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [20074] | 317 | break;
|
|---|
| 318 | }
|
|---|
| 319 | case SDLK_KP_ENTER:
|
|---|
| 320 | case SDLK_RETURN:
|
|---|
| 321 | {
|
|---|
| 322 | // 'Return' should do a Press event for single liners (e.g. submitting forms)
|
|---|
| 323 | // otherwise a '\n' character will be added.
|
|---|
| [23005] | 324 | if (!m_MultiLine)
|
|---|
| [20074] | 325 | {
|
|---|
| [23403] | 326 | SendEvent(GUIM_PRESSED, EventNamePress);
|
|---|
| [16931] | 327 | break;
|
|---|
| [20074] | 328 | }
|
|---|
| [1904] | 329 |
|
|---|
| [20074] | 330 | cooked = '\n'; // Change to '\n' and do default:
|
|---|
| [20095] | 331 | FALLTHROUGH;
|
|---|
| [20074] | 332 | }
|
|---|
| 333 | default: // Insert a character
|
|---|
| 334 | {
|
|---|
| [24278] | 335 | // Regular input is handled via SDL_TEXTINPUT, so we should ignore it here.
|
|---|
| [20074] | 336 | if (cooked == 0)
|
|---|
| 337 | return;
|
|---|
| [1904] | 338 |
|
|---|
| [23927] | 339 | // Check max length
|
|---|
| [25392] | 340 | if (m_MaxLength != 0 && caption.length() >= static_cast<size_t>(m_MaxLength))
|
|---|
| [16931] | 341 | break;
|
|---|
| [1904] | 342 |
|
|---|
| [20074] | 343 | m_WantedX = 0.0f;
|
|---|
| [1904] | 344 |
|
|---|
| [20074] | 345 | if (SelectingText())
|
|---|
| 346 | DeleteCurSelection();
|
|---|
| 347 | m_iBufferPos_Tail = -1;
|
|---|
| [1904] | 348 |
|
|---|
| [25392] | 349 | if (m_iBufferPos == static_cast<int>(caption.length()))
|
|---|
| 350 | caption += cooked;
|
|---|
| [20074] | 351 | else
|
|---|
| [25392] | 352 | caption =
|
|---|
| 353 | caption.Left(m_iBufferPos) + cooked +
|
|---|
| 354 | caption.Right(static_cast<long>(caption.length()) - m_iBufferPos);
|
|---|
| [1237] | 355 |
|
|---|
| [20074] | 356 | UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos + 1);
|
|---|
| [1904] | 357 |
|
|---|
| [20074] | 358 | ++m_iBufferPos;
|
|---|
| [1904] | 359 |
|
|---|
| [20074] | 360 | UpdateAutoScroll();
|
|---|
| [23403] | 361 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [20074] | 362 | break;
|
|---|
| 363 | }
|
|---|
| 364 | }
|
|---|
| 365 | }
|
|---|
| [1904] | 366 |
|
|---|
| [23005] | 367 | void CInput::ManuallyImmutableHandleKeyDownEvent(const SDL_Keycode keyCode)
|
|---|
| [20074] | 368 | {
|
|---|
| [24645] | 369 | bool shiftKeyPressed = g_scancodes[SDL_SCANCODE_LSHIFT] || g_scancodes[SDL_SCANCODE_RSHIFT];
|
|---|
| [1904] | 370 |
|
|---|
| [25392] | 371 | const CStrW& caption = *m_Caption;
|
|---|
| 372 |
|
|---|
| [20074] | 373 | switch (keyCode)
|
|---|
| 374 | {
|
|---|
| 375 | case SDLK_HOME:
|
|---|
| 376 | {
|
|---|
| 377 | // If there's not a selection, we should create one now
|
|---|
| 378 | if (!shiftKeyPressed)
|
|---|
| 379 | {
|
|---|
| 380 | // Make sure a selection isn't created.
|
|---|
| 381 | m_iBufferPos_Tail = -1;
|
|---|
| 382 | }
|
|---|
| 383 | else if (!SelectingText())
|
|---|
| 384 | {
|
|---|
| 385 | // Place tail at the current point:
|
|---|
| 386 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| 387 | }
|
|---|
| [1237] | 388 |
|
|---|
| [20074] | 389 | m_iBufferPos = 0;
|
|---|
| 390 | m_WantedX = 0.0f;
|
|---|
| [1904] | 391 |
|
|---|
| [20074] | 392 | UpdateAutoScroll();
|
|---|
| 393 | break;
|
|---|
| 394 | }
|
|---|
| 395 | case SDLK_END:
|
|---|
| 396 | {
|
|---|
| 397 | // If there's not a selection, we should create one now
|
|---|
| 398 | if (!shiftKeyPressed)
|
|---|
| 399 | {
|
|---|
| 400 | // Make sure a selection isn't created.
|
|---|
| 401 | m_iBufferPos_Tail = -1;
|
|---|
| 402 | }
|
|---|
| 403 | else if (!SelectingText())
|
|---|
| 404 | {
|
|---|
| 405 | // Place tail at the current point:
|
|---|
| 406 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| 407 | }
|
|---|
| [1904] | 408 |
|
|---|
| [25392] | 409 | m_iBufferPos = static_cast<long>(caption.length());
|
|---|
| [20074] | 410 | m_WantedX = 0.0f;
|
|---|
| 411 |
|
|---|
| 412 | UpdateAutoScroll();
|
|---|
| 413 | break;
|
|---|
| 414 | }
|
|---|
| 415 | /**
|
|---|
| 416 | * Conventions for Left/Right when text is selected:
|
|---|
| 417 | *
|
|---|
| 418 | * References:
|
|---|
| 419 | *
|
|---|
| 420 | * Visual Studio
|
|---|
| 421 | * Visual Studio has the 'newer' approach, used by newer versions of
|
|---|
| 422 | * things, and in newer applications. A left press will always place
|
|---|
| 423 | * the pointer on the left edge of the selection, and then of course
|
|---|
| 424 | * remove the selection. Right will do the exact same thing.
|
|---|
| 425 | * If you have the pointer on the right edge and press right, it will
|
|---|
| 426 | * in other words just remove the selection.
|
|---|
| 427 | *
|
|---|
| 428 | * Windows (eg. Notepad)
|
|---|
| 429 | * A left press always takes the pointer a step to the left and
|
|---|
| 430 | * removes the selection as if it were never there in the first place.
|
|---|
| 431 | * Right of course does the same thing but to the right.
|
|---|
| 432 | *
|
|---|
| 433 | * I chose the Visual Studio convention. Used also in Word, gtk 2.0, MSN
|
|---|
| 434 | * Messenger.
|
|---|
| 435 | */
|
|---|
| 436 | case SDLK_LEFT:
|
|---|
| 437 | {
|
|---|
| 438 | m_WantedX = 0.f;
|
|---|
| 439 |
|
|---|
| 440 | if (shiftKeyPressed || !SelectingText())
|
|---|
| [16931] | 441 | {
|
|---|
| 442 | if (!shiftKeyPressed)
|
|---|
| 443 | m_iBufferPos_Tail = -1;
|
|---|
| 444 | else if (!SelectingText())
|
|---|
| 445 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| [1904] | 446 |
|
|---|
| [20074] | 447 | if (m_iBufferPos > 0)
|
|---|
| 448 | --m_iBufferPos;
|
|---|
| 449 | }
|
|---|
| 450 | else
|
|---|
| 451 | {
|
|---|
| 452 | if (m_iBufferPos_Tail < m_iBufferPos)
|
|---|
| 453 | m_iBufferPos = m_iBufferPos_Tail;
|
|---|
| [1904] | 454 |
|
|---|
| [20074] | 455 | m_iBufferPos_Tail = -1;
|
|---|
| 456 | }
|
|---|
| [16244] | 457 |
|
|---|
| [20074] | 458 | UpdateAutoScroll();
|
|---|
| 459 | break;
|
|---|
| 460 | }
|
|---|
| 461 | case SDLK_RIGHT:
|
|---|
| 462 | {
|
|---|
| 463 | m_WantedX = 0.0f;
|
|---|
| [1904] | 464 |
|
|---|
| [20074] | 465 | if (shiftKeyPressed || !SelectingText())
|
|---|
| [16931] | 466 | {
|
|---|
| 467 | if (!shiftKeyPressed)
|
|---|
| 468 | m_iBufferPos_Tail = -1;
|
|---|
| 469 | else if (!SelectingText())
|
|---|
| 470 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| [1237] | 471 |
|
|---|
| [25392] | 472 | if (m_iBufferPos < static_cast<int>(caption.length()))
|
|---|
| [20074] | 473 | ++m_iBufferPos;
|
|---|
| 474 | }
|
|---|
| 475 | else
|
|---|
| 476 | {
|
|---|
| 477 | if (m_iBufferPos_Tail > m_iBufferPos)
|
|---|
| 478 | m_iBufferPos = m_iBufferPos_Tail;
|
|---|
| [1237] | 479 |
|
|---|
| [20074] | 480 | m_iBufferPos_Tail = -1;
|
|---|
| 481 | }
|
|---|
| [16244] | 482 |
|
|---|
| [20074] | 483 | UpdateAutoScroll();
|
|---|
| 484 | break;
|
|---|
| 485 | }
|
|---|
| 486 | /**
|
|---|
| 487 | * Conventions for Up/Down when text is selected:
|
|---|
| 488 | *
|
|---|
| 489 | * References:
|
|---|
| 490 | *
|
|---|
| 491 | * Visual Studio
|
|---|
| 492 | * Visual Studio has a very strange approach, down takes you below the
|
|---|
| 493 | * selection to the next row, and up to the one prior to the whole
|
|---|
| 494 | * selection. The weird part is that it is always aligned as the
|
|---|
| 495 | * 'pointer'. I decided this is to much work for something that is
|
|---|
| 496 | * a bit arbitrary
|
|---|
| 497 | *
|
|---|
| 498 | * Windows (eg. Notepad)
|
|---|
| 499 | * Just like with left/right, the selection is destroyed and it moves
|
|---|
| 500 | * just as if there never were a selection.
|
|---|
| 501 | *
|
|---|
| 502 | * I chose the Notepad convention even though I use the VS convention with
|
|---|
| 503 | * left/right.
|
|---|
| 504 | */
|
|---|
| 505 | case SDLK_UP:
|
|---|
| 506 | {
|
|---|
| 507 | if (!shiftKeyPressed)
|
|---|
| 508 | m_iBufferPos_Tail = -1;
|
|---|
| 509 | else if (!SelectingText())
|
|---|
| 510 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| [1904] | 511 |
|
|---|
| [20074] | 512 | std::list<SRow>::iterator current = m_CharacterPositions.begin();
|
|---|
| 513 | while (current != m_CharacterPositions.end())
|
|---|
| 514 | {
|
|---|
| 515 | if (m_iBufferPos >= current->m_ListStart &&
|
|---|
| 516 | m_iBufferPos <= current->m_ListStart + (int)current->m_ListOfX.size())
|
|---|
| 517 | break;
|
|---|
| [1904] | 518 |
|
|---|
| [16931] | 519 | ++current;
|
|---|
| 520 | }
|
|---|
| [1904] | 521 |
|
|---|
| [20074] | 522 | float pos_x;
|
|---|
| 523 | if (m_iBufferPos - current->m_ListStart == 0)
|
|---|
| 524 | pos_x = 0.f;
|
|---|
| 525 | else
|
|---|
| 526 | pos_x = current->m_ListOfX[m_iBufferPos - current->m_ListStart - 1];
|
|---|
| [1237] | 527 |
|
|---|
| [20074] | 528 | if (m_WantedX > pos_x)
|
|---|
| 529 | pos_x = m_WantedX;
|
|---|
| [1237] | 530 |
|
|---|
| [20074] | 531 | // Now change row:
|
|---|
| 532 | if (current != m_CharacterPositions.begin())
|
|---|
| [16931] | 533 | {
|
|---|
| [20074] | 534 | --current;
|
|---|
| [1394] | 535 |
|
|---|
| [20074] | 536 | // Find X-position:
|
|---|
| 537 | m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
|
|---|
| [16931] | 538 | }
|
|---|
| [20074] | 539 | // else we can't move up
|
|---|
| 540 |
|
|---|
| 541 | UpdateAutoScroll();
|
|---|
| 542 | break;
|
|---|
| 543 | }
|
|---|
| 544 | case SDLK_DOWN:
|
|---|
| 545 | {
|
|---|
| 546 | if (!shiftKeyPressed)
|
|---|
| 547 | m_iBufferPos_Tail = -1;
|
|---|
| 548 | else if (!SelectingText())
|
|---|
| 549 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| 550 |
|
|---|
| 551 | std::list<SRow>::iterator current = m_CharacterPositions.begin();
|
|---|
| 552 | while (current != m_CharacterPositions.end())
|
|---|
| [16931] | 553 | {
|
|---|
| [20074] | 554 | if (m_iBufferPos >= current->m_ListStart &&
|
|---|
| 555 | m_iBufferPos <= current->m_ListStart + (int)current->m_ListOfX.size())
|
|---|
| [16931] | 556 | break;
|
|---|
| [1904] | 557 |
|
|---|
| [20074] | 558 | ++current;
|
|---|
| 559 | }
|
|---|
| [1904] | 560 |
|
|---|
| [20074] | 561 | float pos_x;
|
|---|
| [1904] | 562 |
|
|---|
| [20074] | 563 | if (m_iBufferPos - current->m_ListStart == 0)
|
|---|
| 564 | pos_x = 0.f;
|
|---|
| 565 | else
|
|---|
| 566 | pos_x = current->m_ListOfX[m_iBufferPos - current->m_ListStart - 1];
|
|---|
| [1394] | 567 |
|
|---|
| [20074] | 568 | if (m_WantedX > pos_x)
|
|---|
| 569 | pos_x = m_WantedX;
|
|---|
| [1394] | 570 |
|
|---|
| [20074] | 571 | // Now change row:
|
|---|
| 572 | // Add first, so we can check if it's .end()
|
|---|
| 573 | ++current;
|
|---|
| 574 | if (current != m_CharacterPositions.end())
|
|---|
| 575 | {
|
|---|
| 576 | // Find X-position:
|
|---|
| 577 | m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
|
|---|
| [1752] | 578 | }
|
|---|
| [20074] | 579 | // else we can't move up
|
|---|
| [7649] | 580 |
|
|---|
| [20074] | 581 | UpdateAutoScroll();
|
|---|
| 582 | break;
|
|---|
| [1904] | 583 | }
|
|---|
| [20074] | 584 | case SDLK_PAGEUP:
|
|---|
| 585 | {
|
|---|
| 586 | GetScrollBar(0).ScrollMinusPlenty();
|
|---|
| 587 | UpdateAutoScroll();
|
|---|
| 588 | break;
|
|---|
| 589 | }
|
|---|
| 590 | case SDLK_PAGEDOWN:
|
|---|
| 591 | {
|
|---|
| 592 | GetScrollBar(0).ScrollPlusPlenty();
|
|---|
| 593 | UpdateAutoScroll();
|
|---|
| 594 | break;
|
|---|
| 595 | }
|
|---|
| 596 | default:
|
|---|
| 597 | {
|
|---|
| 598 | break;
|
|---|
| 599 | }
|
|---|
| 600 | }
|
|---|
| [1237] | 601 | }
|
|---|
| 602 |
|
|---|
| [24433] | 603 | void CInput::SetupGeneratedPlaceholderText()
|
|---|
| 604 | {
|
|---|
| [25392] | 605 | m_GeneratedPlaceholderText = CGUIText(m_pGUI, m_PlaceholderText, m_Font, 0, m_BufferZone, EAlign::LEFT, this);
|
|---|
| [24433] | 606 | m_GeneratedPlaceholderTextValid = true;
|
|---|
| 607 | }
|
|---|
| 608 |
|
|---|
| [9646] | 609 | InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
|
|---|
| 610 | {
|
|---|
| [24645] | 611 | bool shiftKeyPressed = g_scancodes[SDL_SCANCODE_LSHIFT] || g_scancodes[SDL_SCANCODE_RSHIFT];
|
|---|
| [9646] | 612 |
|
|---|
| 613 | std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
|---|
| [20075] | 614 |
|
|---|
| [25392] | 615 | // Get direct access to silently mutate m_Caption.
|
|---|
| 616 | // (Messages don't currently need to be sent)
|
|---|
| 617 | CStrW& caption = m_Caption.GetMutable();
|
|---|
| 618 |
|
|---|
| [9658] | 619 | if (hotkey == "paste")
|
|---|
| [9646] | 620 | {
|
|---|
| [20075] | 621 | if (m_Readonly)
|
|---|
| 622 | return IN_PASS;
|
|---|
| 623 |
|
|---|
| [16931] | 624 | m_WantedX = 0.0f;
|
|---|
| [9646] | 625 |
|
|---|
| [23624] | 626 | char* utf8_text = SDL_GetClipboardText();
|
|---|
| 627 | if (!utf8_text)
|
|---|
| 628 | return IN_HANDLED;
|
|---|
| [9658] | 629 |
|
|---|
| [23624] | 630 | std::wstring text = wstring_from_utf8(utf8_text);
|
|---|
| 631 | SDL_free(utf8_text);
|
|---|
| [9646] | 632 |
|
|---|
| [23927] | 633 | // Check max length
|
|---|
| [25392] | 634 | if (m_MaxLength != 0 && caption.length() + text.length() > static_cast<size_t>(m_MaxLength))
|
|---|
| [27271] | 635 | text.erase(static_cast<size_t>(m_MaxLength) - caption.length());
|
|---|
| [23927] | 636 |
|
|---|
| [23624] | 637 | if (SelectingText())
|
|---|
| 638 | DeleteCurSelection();
|
|---|
| [9646] | 639 |
|
|---|
| [25392] | 640 | if (m_iBufferPos == static_cast<int>(caption.length()))
|
|---|
| 641 | caption += text;
|
|---|
| [23624] | 642 | else
|
|---|
| [25392] | 643 | caption =
|
|---|
| 644 | caption.Left(m_iBufferPos) + text +
|
|---|
| 645 | caption.Right(static_cast<long>(caption.length()) - m_iBufferPos);
|
|---|
| [9646] | 646 |
|
|---|
| [23624] | 647 | UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
|
|---|
| [20091] | 648 |
|
|---|
| [23624] | 649 | m_iBufferPos += static_cast<int>(text.size());
|
|---|
| 650 | UpdateAutoScroll();
|
|---|
| 651 | UpdateBufferPositionSetting();
|
|---|
| [9646] | 652 |
|
|---|
| [23624] | 653 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| 654 |
|
|---|
| [9646] | 655 | return IN_HANDLED;
|
|---|
| 656 | }
|
|---|
| [9658] | 657 | else if (hotkey == "copy" || hotkey == "cut")
|
|---|
| [9646] | 658 | {
|
|---|
| [20075] | 659 | if (m_Readonly && hotkey == "cut")
|
|---|
| 660 | return IN_PASS;
|
|---|
| 661 |
|
|---|
| [16931] | 662 | m_WantedX = 0.0f;
|
|---|
| [9646] | 663 |
|
|---|
| 664 | if (SelectingText())
|
|---|
| 665 | {
|
|---|
| 666 | int virtualFrom;
|
|---|
| 667 | int virtualTo;
|
|---|
| 668 |
|
|---|
| 669 | if (m_iBufferPos_Tail >= m_iBufferPos)
|
|---|
| 670 | {
|
|---|
| 671 | virtualFrom = m_iBufferPos;
|
|---|
| 672 | virtualTo = m_iBufferPos_Tail;
|
|---|
| 673 | }
|
|---|
| 674 | else
|
|---|
| 675 | {
|
|---|
| 676 | virtualFrom = m_iBufferPos_Tail;
|
|---|
| 677 | virtualTo = m_iBufferPos;
|
|---|
| 678 | }
|
|---|
| 679 |
|
|---|
| [25392] | 680 | CStrW text = caption.Left(virtualTo).Right(virtualTo - virtualFrom);
|
|---|
| [9646] | 681 |
|
|---|
| [23624] | 682 | SDL_SetClipboardText(text.ToUTF8().c_str());
|
|---|
| [9646] | 683 |
|
|---|
| [9658] | 684 | if (hotkey == "cut")
|
|---|
| [9646] | 685 | {
|
|---|
| 686 | DeleteCurSelection();
|
|---|
| [19234] | 687 | UpdateAutoScroll();
|
|---|
| [23403] | 688 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [9646] | 689 | }
|
|---|
| 690 | }
|
|---|
| 691 |
|
|---|
| 692 | return IN_HANDLED;
|
|---|
| 693 | }
|
|---|
| [9658] | 694 | else if (hotkey == "text.delete.left")
|
|---|
| [9646] | 695 | {
|
|---|
| [20075] | 696 | if (m_Readonly)
|
|---|
| 697 | return IN_PASS;
|
|---|
| 698 |
|
|---|
| [16931] | 699 | m_WantedX = 0.0f;
|
|---|
| [9646] | 700 |
|
|---|
| 701 | if (SelectingText())
|
|---|
| 702 | DeleteCurSelection();
|
|---|
| [20088] | 703 |
|
|---|
| [25392] | 704 | if (!caption.empty() && m_iBufferPos != 0)
|
|---|
| [9646] | 705 | {
|
|---|
| 706 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| [25392] | 707 | CStrW searchString = caption.Left(m_iBufferPos);
|
|---|
| [9646] | 708 |
|
|---|
| 709 | // If we are starting in whitespace, adjust position until we get a non whitespace
|
|---|
| 710 | while (m_iBufferPos > 0)
|
|---|
| 711 | {
|
|---|
| 712 | if (!iswspace(searchString[m_iBufferPos - 1]))
|
|---|
| 713 | break;
|
|---|
| 714 |
|
|---|
| 715 | m_iBufferPos--;
|
|---|
| 716 | }
|
|---|
| [16244] | 717 |
|
|---|
| [20088] | 718 | // If we end up on a punctuation char we just delete it (treat punct like a word)
|
|---|
| [9646] | 719 | if (iswpunct(searchString[m_iBufferPos - 1]))
|
|---|
| 720 | m_iBufferPos--;
|
|---|
| 721 | else
|
|---|
| 722 | {
|
|---|
| 723 | // Now we are on a non white space character, adjust position to char after next whitespace char is found
|
|---|
| 724 | while (m_iBufferPos > 0)
|
|---|
| 725 | {
|
|---|
| 726 | if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
|
|---|
| 727 | break;
|
|---|
| 728 |
|
|---|
| 729 | m_iBufferPos--;
|
|---|
| 730 | }
|
|---|
| 731 | }
|
|---|
| 732 |
|
|---|
| [16261] | 733 | UpdateBufferPositionSetting();
|
|---|
| [9646] | 734 | DeleteCurSelection();
|
|---|
| [23403] | 735 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [9646] | 736 | }
|
|---|
| [19234] | 737 | UpdateAutoScroll();
|
|---|
| [9646] | 738 | return IN_HANDLED;
|
|---|
| [16931] | 739 | }
|
|---|
| [9658] | 740 | else if (hotkey == "text.delete.right")
|
|---|
| [9646] | 741 | {
|
|---|
| [20075] | 742 | if (m_Readonly)
|
|---|
| 743 | return IN_PASS;
|
|---|
| 744 |
|
|---|
| [16931] | 745 | m_WantedX = 0.0f;
|
|---|
| [9646] | 746 |
|
|---|
| 747 | if (SelectingText())
|
|---|
| 748 | DeleteCurSelection();
|
|---|
| [20088] | 749 |
|
|---|
| [25392] | 750 | if (!caption.empty() && m_iBufferPos < static_cast<int>(caption.length()))
|
|---|
| [9646] | 751 | {
|
|---|
| 752 | // Delete the word to the right of the cursor
|
|---|
| 753 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| 754 |
|
|---|
| 755 | // Delete chars to the right unit we hit whitespace
|
|---|
| [25392] | 756 | while (++m_iBufferPos < static_cast<int>(caption.length()))
|
|---|
| [9646] | 757 | {
|
|---|
| [25392] | 758 | if (iswspace(caption[m_iBufferPos]) || iswpunct(caption[m_iBufferPos]))
|
|---|
| [9646] | 759 | break;
|
|---|
| 760 | }
|
|---|
| 761 |
|
|---|
| 762 | // Eliminate any whitespace behind the word we just deleted
|
|---|
| [25392] | 763 | while (m_iBufferPos < static_cast<int>(caption.length()))
|
|---|
| [9646] | 764 | {
|
|---|
| [25392] | 765 | if (!iswspace(caption[m_iBufferPos]))
|
|---|
| [9646] | 766 | break;
|
|---|
| 767 |
|
|---|
| [16931] | 768 | ++m_iBufferPos;
|
|---|
| [9646] | 769 | }
|
|---|
| [16261] | 770 | UpdateBufferPositionSetting();
|
|---|
| [9646] | 771 | DeleteCurSelection();
|
|---|
| 772 | }
|
|---|
| [19234] | 773 | UpdateAutoScroll();
|
|---|
| [23403] | 774 | SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
|
|---|
| [16244] | 775 | return IN_HANDLED;
|
|---|
| [9646] | 776 | }
|
|---|
| [9658] | 777 | else if (hotkey == "text.move.left")
|
|---|
| [9646] | 778 | {
|
|---|
| [16931] | 779 | m_WantedX = 0.0f;
|
|---|
| [16244] | 780 |
|
|---|
| [9646] | 781 | if (shiftKeyPressed || !SelectingText())
|
|---|
| 782 | {
|
|---|
| 783 | if (!shiftKeyPressed)
|
|---|
| 784 | m_iBufferPos_Tail = -1;
|
|---|
| 785 | else if (!SelectingText())
|
|---|
| 786 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| 787 |
|
|---|
| [25392] | 788 | if (!caption.empty() && m_iBufferPos != 0)
|
|---|
| [9646] | 789 | {
|
|---|
| [25392] | 790 | CStrW searchString = caption.Left(m_iBufferPos);
|
|---|
| [9646] | 791 |
|
|---|
| 792 | // If we are starting in whitespace, adjust position until we get a non whitespace
|
|---|
| 793 | while (m_iBufferPos > 0)
|
|---|
| 794 | {
|
|---|
| 795 | if (!iswspace(searchString[m_iBufferPos - 1]))
|
|---|
| 796 | break;
|
|---|
| 797 |
|
|---|
| 798 | m_iBufferPos--;
|
|---|
| 799 | }
|
|---|
| [16244] | 800 |
|
|---|
| [9646] | 801 | // If we end up on a puctuation char we just select it (treat punct like a word)
|
|---|
| 802 | if (iswpunct(searchString[m_iBufferPos - 1]))
|
|---|
| 803 | m_iBufferPos--;
|
|---|
| 804 | else
|
|---|
| 805 | {
|
|---|
| 806 | // Now we are on a non white space character, adjust position to char after next whitespace char is found
|
|---|
| 807 | while (m_iBufferPos > 0)
|
|---|
| 808 | {
|
|---|
| 809 | if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
|
|---|
| 810 | break;
|
|---|
| 811 |
|
|---|
| 812 | m_iBufferPos--;
|
|---|
| 813 | }
|
|---|
| 814 | }
|
|---|
| 815 | }
|
|---|
| 816 | }
|
|---|
| 817 | else
|
|---|
| 818 | {
|
|---|
| 819 | if (m_iBufferPos_Tail < m_iBufferPos)
|
|---|
| 820 | m_iBufferPos = m_iBufferPos_Tail;
|
|---|
| 821 |
|
|---|
| 822 | m_iBufferPos_Tail = -1;
|
|---|
| 823 | }
|
|---|
| 824 |
|
|---|
| [16261] | 825 | UpdateBufferPositionSetting();
|
|---|
| [9646] | 826 | UpdateAutoScroll();
|
|---|
| 827 |
|
|---|
| 828 | return IN_HANDLED;
|
|---|
| 829 | }
|
|---|
| [9658] | 830 | else if (hotkey == "text.move.right")
|
|---|
| [9646] | 831 | {
|
|---|
| [16931] | 832 | m_WantedX = 0.0f;
|
|---|
| [9646] | 833 |
|
|---|
| 834 | if (shiftKeyPressed || !SelectingText())
|
|---|
| 835 | {
|
|---|
| 836 | if (!shiftKeyPressed)
|
|---|
| 837 | m_iBufferPos_Tail = -1;
|
|---|
| 838 | else if (!SelectingText())
|
|---|
| 839 | m_iBufferPos_Tail = m_iBufferPos;
|
|---|
| 840 |
|
|---|
| [25392] | 841 | if (!caption.empty() && m_iBufferPos < static_cast<int>(caption.length()))
|
|---|
| [9646] | 842 | {
|
|---|
| 843 | // Select chars to the right until we hit whitespace
|
|---|
| [25392] | 844 | while (++m_iBufferPos < static_cast<int>(caption.length()))
|
|---|
| [9646] | 845 | {
|
|---|
| [25392] | 846 | if (iswspace(caption[m_iBufferPos]) || iswpunct(caption[m_iBufferPos]))
|
|---|
| [9646] | 847 | break;
|
|---|
| 848 | }
|
|---|
| 849 |
|
|---|
| 850 | // Also select any whitespace following the word we just selected
|
|---|
| [25392] | 851 | while (m_iBufferPos < static_cast<int>(caption.length()))
|
|---|
| [9646] | 852 | {
|
|---|
| [25392] | 853 | if (!iswspace(caption[m_iBufferPos]))
|
|---|
| [9646] | 854 | break;
|
|---|
| 855 |
|
|---|
| [16931] | 856 | ++m_iBufferPos;
|
|---|
| [9646] | 857 | }
|
|---|
| 858 | }
|
|---|
| 859 | }
|
|---|
| 860 | else
|
|---|
| 861 | {
|
|---|
| 862 | if (m_iBufferPos_Tail > m_iBufferPos)
|
|---|
| 863 | m_iBufferPos = m_iBufferPos_Tail;
|
|---|
| 864 |
|
|---|
| 865 | m_iBufferPos_Tail = -1;
|
|---|
| [16244] | 866 | }
|
|---|
| [9646] | 867 |
|
|---|
| [16261] | 868 | UpdateBufferPositionSetting();
|
|---|
| [9646] | 869 | UpdateAutoScroll();
|
|---|
| 870 |
|
|---|
| 871 | return IN_HANDLED;
|
|---|
| 872 | }
|
|---|
| [20074] | 873 |
|
|---|
| 874 | return IN_PASS;
|
|---|
| [9646] | 875 | }
|
|---|
| 876 |
|
|---|
| [23020] | 877 | void CInput::ResetStates()
|
|---|
| 878 | {
|
|---|
| 879 | IGUIObject::ResetStates();
|
|---|
| 880 | IGUIScrollBarOwner::ResetStates();
|
|---|
| 881 | }
|
|---|
| [9646] | 882 |
|
|---|
| [16931] | 883 | void CInput::HandleMessage(SGUIMessage& Message)
|
|---|
| [1237] | 884 | {
|
|---|
| [23020] | 885 | IGUIObject::HandleMessage(Message);
|
|---|
| [1237] | 886 | IGUIScrollBarOwner::HandleMessage(Message);
|
|---|
| 887 |
|
|---|
| [25392] | 888 | // Cleans up operator[] usage.
|
|---|
| 889 | const CStrW& caption = *m_Caption;
|
|---|
| 890 |
|
|---|
| [1237] | 891 | switch (Message.type)
|
|---|
| 892 | {
|
|---|
| 893 | case GUIM_SETTINGS_UPDATED:
|
|---|
| [16931] | 894 | {
|
|---|
| [1237] | 895 | // Update scroll-bar
|
|---|
| 896 | // TODO Gee: (2004-09-01) Is this really updated each time it should?
|
|---|
| [23005] | 897 | if (m_ScrollBar &&
|
|---|
| 898 | (Message.value == "size" ||
|
|---|
| 899 | Message.value == "z" ||
|
|---|
| 900 | Message.value == "absolute"))
|
|---|
| [16244] | 901 | {
|
|---|
| [8932] | 902 | GetScrollBar(0).SetX(m_CachedActualSize.right);
|
|---|
| 903 | GetScrollBar(0).SetY(m_CachedActualSize.top);
|
|---|
| 904 | GetScrollBar(0).SetZ(GetBufferedZ());
|
|---|
| 905 | GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
|
|---|
| [1237] | 906 | }
|
|---|
| 907 |
|
|---|
| 908 | // Update scrollbar
|
|---|
| [23005] | 909 | if (Message.value == "scrollbar_style")
|
|---|
| 910 | GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle);
|
|---|
| [1237] | 911 |
|
|---|
| [23005] | 912 | if (Message.value == "buffer_position")
|
|---|
| [16261] | 913 | {
|
|---|
| [23927] | 914 | m_iBufferPos = m_MaxLength != 0 ? std::min(m_MaxLength, m_BufferPosition) : m_BufferPosition;
|
|---|
| [16261] | 915 | m_iBufferPos_Tail = -1; // position change resets selection
|
|---|
| 916 | }
|
|---|
| 917 |
|
|---|
| [23005] | 918 | if (Message.value == "size" ||
|
|---|
| 919 | Message.value == "z" ||
|
|---|
| 920 | Message.value == "font" ||
|
|---|
| 921 | Message.value == "absolute" ||
|
|---|
| 922 | Message.value == "caption" ||
|
|---|
| 923 | Message.value == "scrollbar" ||
|
|---|
| 924 | Message.value == "scrollbar_style")
|
|---|
| [1394] | 925 | {
|
|---|
| 926 | UpdateText();
|
|---|
| 927 | }
|
|---|
| 928 |
|
|---|
| [23005] | 929 | if (Message.value == "multiline")
|
|---|
| [1904] | 930 | {
|
|---|
| [23005] | 931 | if (!m_MultiLine)
|
|---|
| [1904] | 932 | GetScrollBar(0).SetLength(0.f);
|
|---|
| 933 | else
|
|---|
| [16261] | 934 | GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
|
|---|
| [1904] | 935 |
|
|---|
| 936 | UpdateText();
|
|---|
| 937 | }
|
|---|
| [20088] | 938 |
|
|---|
| [24433] | 939 | if (Message.value == "placeholder_text" ||
|
|---|
| 940 | Message.value == "size" ||
|
|---|
| 941 | Message.value == "font" ||
|
|---|
| 942 | Message.value == "z" ||
|
|---|
| 943 | Message.value == "text_valign")
|
|---|
| 944 | {
|
|---|
| 945 | m_GeneratedPlaceholderTextValid = false;
|
|---|
| 946 | }
|
|---|
| 947 |
|
|---|
| [19234] | 948 | UpdateAutoScroll();
|
|---|
| [20075] | 949 |
|
|---|
| [16931] | 950 | break;
|
|---|
| 951 | }
|
|---|
| [1237] | 952 | case GUIM_MOUSE_PRESS_LEFT:
|
|---|
| [16931] | 953 | {
|
|---|
| 954 | // Check if we're selecting the scrollbar
|
|---|
| [23005] | 955 | if (m_ScrollBar &&
|
|---|
| 956 | m_MultiLine &&
|
|---|
| [22765] | 957 | GetScrollBar(0).GetStyle())
|
|---|
| [1904] | 958 | {
|
|---|
| [25152] | 959 | if (m_pGUI.GetMousePos().X > m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Width)
|
|---|
| [1904] | 960 | break;
|
|---|
| 961 | }
|
|---|
| 962 |
|
|---|
| [15785] | 963 | if (m_ComposingText)
|
|---|
| 964 | break;
|
|---|
| 965 |
|
|---|
| [1394] | 966 | // Okay, this section is about pressing the mouse and
|
|---|
| 967 | // choosing where the point should be placed. For
|
|---|
| 968 | // instance, if we press between a and b, the point
|
|---|
| 969 | // should of course be placed accordingly. Other
|
|---|
| 970 | // special cases are handled like the input box norms.
|
|---|
| [24645] | 971 | if (g_scancodes[SDL_SCANCODE_LSHIFT] || g_scancodes[SDL_SCANCODE_RSHIFT])
|
|---|
| [1904] | 972 | m_iBufferPos = GetMouseHoveringTextPosition();
|
|---|
| 973 | else
|
|---|
| 974 | m_iBufferPos = m_iBufferPos_Tail = GetMouseHoveringTextPosition();
|
|---|
| [1237] | 975 |
|
|---|
| [1904] | 976 | m_SelectingText = true;
|
|---|
| [16244] | 977 |
|
|---|
| [1904] | 978 | UpdateAutoScroll();
|
|---|
| [1394] | 979 |
|
|---|
| [1904] | 980 | // If we immediately release the button it will just be seen as a click
|
|---|
| 981 | // for the user though.
|
|---|
| [16931] | 982 | break;
|
|---|
| 983 | }
|
|---|
| [9646] | 984 | case GUIM_MOUSE_DBLCLICK_LEFT:
|
|---|
| [16931] | 985 | {
|
|---|
| 986 | if (m_ComposingText)
|
|---|
| 987 | break;
|
|---|
| [15785] | 988 |
|
|---|
| [25392] | 989 | if (caption.empty())
|
|---|
| [16931] | 990 | break;
|
|---|
| [10011] | 991 |
|
|---|
| [16931] | 992 | m_iBufferPos = m_iBufferPos_Tail = GetMouseHoveringTextPosition();
|
|---|
| [9646] | 993 |
|
|---|
| [25392] | 994 | if (m_iBufferPos >= (int)caption.length())
|
|---|
| 995 | m_iBufferPos = m_iBufferPos_Tail = caption.length() - 1;
|
|---|
| [10011] | 996 |
|
|---|
| [16931] | 997 | // See if we are clicking over whitespace
|
|---|
| [25392] | 998 | if (iswspace(caption[m_iBufferPos]))
|
|---|
| [16931] | 999 | {
|
|---|
| 1000 | // see if we are in a section of whitespace greater than one character
|
|---|
| [25392] | 1001 | if ((m_iBufferPos + 1 < (int) caption.length() && iswspace(caption[m_iBufferPos + 1])) ||
|
|---|
| 1002 | (m_iBufferPos - 1 > 0 && iswspace(caption[m_iBufferPos - 1])))
|
|---|
| [9646] | 1003 | {
|
|---|
| [16931] | 1004 | //
|
|---|
| 1005 | // We are clicking in an area with more than one whitespace character
|
|---|
| 1006 | // so we select both the word to the left and then the word to the right
|
|---|
| 1007 | //
|
|---|
| 1008 | // [1] First the left
|
|---|
| 1009 | // skip the whitespace
|
|---|
| 1010 | while (m_iBufferPos > 0)
|
|---|
| [9646] | 1011 | {
|
|---|
| [25392] | 1012 | if (!iswspace(caption[m_iBufferPos - 1]))
|
|---|
| [16931] | 1013 | break;
|
|---|
| [9646] | 1014 |
|
|---|
| [16931] | 1015 | m_iBufferPos--;
|
|---|
| 1016 | }
|
|---|
| 1017 | // now go until we hit white space or punctuation
|
|---|
| 1018 | while (m_iBufferPos > 0)
|
|---|
| 1019 | {
|
|---|
| [25392] | 1020 | if (iswspace(caption[m_iBufferPos - 1]))
|
|---|
| [16931] | 1021 | break;
|
|---|
| [9646] | 1022 |
|
|---|
| [16931] | 1023 | m_iBufferPos--;
|
|---|
| [9646] | 1024 |
|
|---|
| [25392] | 1025 | if (iswpunct(caption[m_iBufferPos]))
|
|---|
| [10011] | 1026 | break;
|
|---|
| [16931] | 1027 | }
|
|---|
| [10011] | 1028 |
|
|---|
| [16931] | 1029 | // [2] Then the right
|
|---|
| 1030 | // go right until we are not in whitespace
|
|---|
| [25392] | 1031 | while (++m_iBufferPos_Tail < static_cast<int>(caption.length()))
|
|---|
| [9646] | 1032 | {
|
|---|
| [25392] | 1033 | if (!iswspace(caption[m_iBufferPos_Tail]))
|
|---|
| [10011] | 1034 | break;
|
|---|
| [16931] | 1035 | }
|
|---|
| [10011] | 1036 |
|
|---|
| [25392] | 1037 | if (m_iBufferPos_Tail == static_cast<int>(caption.length()))
|
|---|
| [16931] | 1038 | break;
|
|---|
| [9646] | 1039 |
|
|---|
| [16931] | 1040 | // now go to the right until we hit whitespace or punctuation
|
|---|
| [25392] | 1041 | while (++m_iBufferPos_Tail < static_cast<int>(caption.length()))
|
|---|
| [16931] | 1042 | {
|
|---|
| [25392] | 1043 | if (iswspace(caption[m_iBufferPos_Tail]) || iswpunct(caption[m_iBufferPos_Tail]))
|
|---|
| [16931] | 1044 | break;
|
|---|
| [9646] | 1045 | }
|
|---|
| 1046 | }
|
|---|
| 1047 | else
|
|---|
| 1048 | {
|
|---|
| [16931] | 1049 | // single whitespace so select word to the right
|
|---|
| [25392] | 1050 | while (++m_iBufferPos_Tail < static_cast<int>(caption.length()))
|
|---|
| [9646] | 1051 | {
|
|---|
| [25392] | 1052 | if (!iswspace(caption[m_iBufferPos_Tail]))
|
|---|
| [9646] | 1053 | break;
|
|---|
| [16931] | 1054 | }
|
|---|
| [9646] | 1055 |
|
|---|
| [25392] | 1056 | if (m_iBufferPos_Tail == static_cast<int>(caption.length()))
|
|---|
| [16931] | 1057 | break;
|
|---|
| [9646] | 1058 |
|
|---|
| [16931] | 1059 | // Don't include the leading whitespace
|
|---|
| 1060 | m_iBufferPos = m_iBufferPos_Tail;
|
|---|
| 1061 |
|
|---|
| 1062 | // now go to the right until we hit whitespace or punctuation
|
|---|
| [25392] | 1063 | while (++m_iBufferPos_Tail < static_cast<int>(caption.length()))
|
|---|
| [9646] | 1064 | {
|
|---|
| [25392] | 1065 | if (iswspace(caption[m_iBufferPos_Tail]) || iswpunct(caption[m_iBufferPos_Tail]))
|
|---|
| [9646] | 1066 | break;
|
|---|
| 1067 | }
|
|---|
| 1068 | }
|
|---|
| 1069 | }
|
|---|
| [16931] | 1070 | else
|
|---|
| 1071 | {
|
|---|
| 1072 | // clicked on non-whitespace so select current word
|
|---|
| 1073 | // go until we hit white space or punctuation
|
|---|
| 1074 | while (m_iBufferPos > 0)
|
|---|
| 1075 | {
|
|---|
| [25392] | 1076 | if (iswspace(caption[m_iBufferPos - 1]))
|
|---|
| [16931] | 1077 | break;
|
|---|
| 1078 |
|
|---|
| 1079 | m_iBufferPos--;
|
|---|
| 1080 |
|
|---|
| [25392] | 1081 | if (iswpunct(caption[m_iBufferPos]))
|
|---|
| [16931] | 1082 | break;
|
|---|
| 1083 | }
|
|---|
| 1084 | // go to the right until we hit whitespace or punctuation
|
|---|
| [25392] | 1085 | while (++m_iBufferPos_Tail < static_cast<int>(caption.length()))
|
|---|
| 1086 | if (iswspace(caption[m_iBufferPos_Tail]) || iswpunct(caption[m_iBufferPos_Tail]))
|
|---|
| [16931] | 1087 | break;
|
|---|
| 1088 | }
|
|---|
| [19234] | 1089 | UpdateAutoScroll();
|
|---|
| [9646] | 1090 | break;
|
|---|
| [16931] | 1091 | }
|
|---|
| [1904] | 1092 | case GUIM_MOUSE_RELEASE_LEFT:
|
|---|
| [20088] | 1093 | {
|
|---|
| [1904] | 1094 | if (m_SelectingText)
|
|---|
| 1095 | m_SelectingText = false;
|
|---|
| 1096 | break;
|
|---|
| [20088] | 1097 | }
|
|---|
| [1904] | 1098 | case GUIM_MOUSE_MOTION:
|
|---|
| [20088] | 1099 | {
|
|---|
| [1904] | 1100 | // If we just pressed down and started to move before releasing
|
|---|
| 1101 | // this is one way of selecting larger portions of text.
|
|---|
| 1102 | if (m_SelectingText)
|
|---|
| 1103 | {
|
|---|
| 1104 | // Actually, first we need to re-check that the mouse button is
|
|---|
| 1105 | // really pressed (it can be released while outside the control.
|
|---|
| [2934] | 1106 | if (!g_mouse_buttons[SDL_BUTTON_LEFT])
|
|---|
| [1904] | 1107 | m_SelectingText = false;
|
|---|
| 1108 | else
|
|---|
| 1109 | m_iBufferPos = GetMouseHoveringTextPosition();
|
|---|
| 1110 | UpdateAutoScroll();
|
|---|
| [1394] | 1111 | }
|
|---|
| 1112 | break;
|
|---|
| [20088] | 1113 | }
|
|---|
| [1237] | 1114 | case GUIM_LOAD:
|
|---|
| [16931] | 1115 | {
|
|---|
| 1116 | GetScrollBar(0).SetX(m_CachedActualSize.right);
|
|---|
| 1117 | GetScrollBar(0).SetY(m_CachedActualSize.top);
|
|---|
| 1118 | GetScrollBar(0).SetZ(GetBufferedZ());
|
|---|
| 1119 | GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
|
|---|
| [23005] | 1120 | GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle);
|
|---|
| [1237] | 1121 |
|
|---|
| [1904] | 1122 | UpdateText();
|
|---|
| [19234] | 1123 | UpdateAutoScroll();
|
|---|
| [20075] | 1124 |
|
|---|
| [1904] | 1125 | break;
|
|---|
| [16931] | 1126 | }
|
|---|
| [1237] | 1127 | case GUIM_GOT_FOCUS:
|
|---|
| [20088] | 1128 | {
|
|---|
| [7908] | 1129 | m_iBufferPos = 0;
|
|---|
| [13068] | 1130 | m_PrevTime = 0.0;
|
|---|
| 1131 | m_CursorVisState = false;
|
|---|
| [15785] | 1132 |
|
|---|
| [25186] | 1133 | ResetActiveHotkeys();
|
|---|
| 1134 |
|
|---|
| [15785] | 1135 | // Tell the IME where to draw the candidate list
|
|---|
| 1136 | SDL_Rect rect;
|
|---|
| [25143] | 1137 | rect.h = m_CachedActualSize.GetSize().Height;
|
|---|
| 1138 | rect.w = m_CachedActualSize.GetSize().Width;
|
|---|
| [25152] | 1139 | rect.x = m_CachedActualSize.TopLeft().X;
|
|---|
| 1140 | rect.y = m_CachedActualSize.TopLeft().Y;
|
|---|
| [15785] | 1141 | SDL_SetTextInputRect(&rect);
|
|---|
| [15830] | 1142 | SDL_StartTextInput();
|
|---|
| [1237] | 1143 | break;
|
|---|
| [20088] | 1144 | }
|
|---|
| [1237] | 1145 | case GUIM_LOST_FOCUS:
|
|---|
| [20088] | 1146 | {
|
|---|
| [15785] | 1147 | if (m_ComposingText)
|
|---|
| 1148 | {
|
|---|
| 1149 | // Simulate a final text editing event to clear the composition
|
|---|
| 1150 | SDL_Event_ evt;
|
|---|
| 1151 | evt.ev.type = SDL_TEXTEDITING;
|
|---|
| 1152 | evt.ev.edit.length = 0;
|
|---|
| 1153 | evt.ev.edit.start = 0;
|
|---|
| 1154 | evt.ev.edit.text[0] = 0;
|
|---|
| [24215] | 1155 | ManuallyHandleKeys(&evt);
|
|---|
| [15785] | 1156 | }
|
|---|
| [15830] | 1157 | SDL_StopTextInput();
|
|---|
| [15785] | 1158 |
|
|---|
| [1237] | 1159 | m_iBufferPos = -1;
|
|---|
| [1904] | 1160 | m_iBufferPos_Tail = -1;
|
|---|
| [1237] | 1161 | break;
|
|---|
| [20088] | 1162 | }
|
|---|
| [1237] | 1163 | default:
|
|---|
| [20088] | 1164 | {
|
|---|
| [1237] | 1165 | break;
|
|---|
| 1166 | }
|
|---|
| [20088] | 1167 | }
|
|---|
| [16261] | 1168 | UpdateBufferPositionSetting();
|
|---|
| [1237] | 1169 | }
|
|---|
| 1170 |
|
|---|
| [8932] | 1171 | void CInput::UpdateCachedSize()
|
|---|
| 1172 | {
|
|---|
| 1173 | // If an ancestor's size changed, this will let us intercept the change and
|
|---|
| 1174 | // update our scrollbar positions
|
|---|
| 1175 |
|
|---|
| 1176 | IGUIObject::UpdateCachedSize();
|
|---|
| 1177 |
|
|---|
| [23005] | 1178 | if (m_ScrollBar)
|
|---|
| [8932] | 1179 | {
|
|---|
| 1180 | GetScrollBar(0).SetX(m_CachedActualSize.right);
|
|---|
| 1181 | GetScrollBar(0).SetY(m_CachedActualSize.top);
|
|---|
| 1182 | GetScrollBar(0).SetZ(GetBufferedZ());
|
|---|
| 1183 | GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
|
|---|
| 1184 | }
|
|---|
| [24433] | 1185 |
|
|---|
| 1186 | m_GeneratedPlaceholderTextValid = false;
|
|---|
| [8932] | 1187 | }
|
|---|
| 1188 |
|
|---|
| [26870] | 1189 | void CInput::DrawContent(CCanvas2D& canvas)
|
|---|
| [1237] | 1190 | {
|
|---|
| [13068] | 1191 | if (m_CursorBlinkRate > 0.0)
|
|---|
| 1192 | {
|
|---|
| 1193 | // check if the cursor visibility state needs to be changed
|
|---|
| 1194 | double currTime = timer_Time();
|
|---|
| [20088] | 1195 | if (currTime - m_PrevTime >= m_CursorBlinkRate)
|
|---|
| [13068] | 1196 | {
|
|---|
| 1197 | m_CursorVisState = !m_CursorVisState;
|
|---|
| 1198 | m_PrevTime = currTime;
|
|---|
| 1199 | }
|
|---|
| 1200 | }
|
|---|
| 1201 | else
|
|---|
| 1202 | // should always be visible
|
|---|
| 1203 | m_CursorVisState = true;
|
|---|
| 1204 |
|
|---|
| [25392] | 1205 | CStrIntern font_name(m_Font->ToUTF8());
|
|---|
| [16931] | 1206 |
|
|---|
| 1207 | wchar_t mask_char = L'*';
|
|---|
| [25392] | 1208 | if (m_Mask && m_MaskChar->length() > 0)
|
|---|
| 1209 | mask_char = (*m_MaskChar)[0];
|
|---|
| [22765] | 1210 |
|
|---|
| [25591] | 1211 | m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize);
|
|---|
| [1237] | 1212 |
|
|---|
| [16931] | 1213 | float scroll = 0.f;
|
|---|
| [23005] | 1214 | if (m_ScrollBar && m_MultiLine)
|
|---|
| [16931] | 1215 | scroll = GetScrollBar(0).GetPos();
|
|---|
| 1216 |
|
|---|
| 1217 | CFontMetrics font(font_name);
|
|---|
| 1218 |
|
|---|
| 1219 | // These are useful later.
|
|---|
| 1220 | int VirtualFrom, VirtualTo;
|
|---|
| [1237] | 1221 |
|
|---|
| [16931] | 1222 | if (m_iBufferPos_Tail >= m_iBufferPos)
|
|---|
| 1223 | {
|
|---|
| 1224 | VirtualFrom = m_iBufferPos;
|
|---|
| 1225 | VirtualTo = m_iBufferPos_Tail;
|
|---|
| 1226 | }
|
|---|
| 1227 | else
|
|---|
| 1228 | {
|
|---|
| 1229 | VirtualFrom = m_iBufferPos_Tail;
|
|---|
| 1230 | VirtualTo = m_iBufferPos;
|
|---|
| 1231 | }
|
|---|
| [1237] | 1232 |
|
|---|
| [16931] | 1233 | // Get the height of this font.
|
|---|
| 1234 | float h = (float)font.GetHeight();
|
|---|
| 1235 | float ls = (float)font.GetLineSpacing();
|
|---|
| [1237] | 1236 |
|
|---|
| [25606] | 1237 | CTextRenderer textRenderer;
|
|---|
| [25645] | 1238 | textRenderer.SetCurrentFont(font_name);
|
|---|
| [1237] | 1239 |
|
|---|
| [16931] | 1240 | textRenderer.Translate(
|
|---|
| [23005] | 1241 | (float)(int)(m_CachedActualSize.left) + m_BufferZone,
|
|---|
| [26870] | 1242 | (float)(int)(m_CachedActualSize.top + h) + m_BufferZone);
|
|---|
| [2539] | 1243 |
|
|---|
| [16931] | 1244 | // U+FE33: PRESENTATION FORM FOR VERTICAL LOW LINE
|
|---|
| 1245 | // (sort of like a | which is aligned to the left of most characters)
|
|---|
| [2539] | 1246 |
|
|---|
| [23005] | 1247 | float buffered_y = -scroll + m_BufferZone;
|
|---|
| [2539] | 1248 |
|
|---|
| [16931] | 1249 | // When selecting larger areas, we need to draw a rectangle box
|
|---|
| 1250 | // around it, and this is to keep track of where the box
|
|---|
| 1251 | // started, because we need to follow the iteration until we
|
|---|
| 1252 | // reach the end, before we can actually draw it.
|
|---|
| 1253 | bool drawing_box = false;
|
|---|
| 1254 | float box_x = 0.f;
|
|---|
| [2539] | 1255 |
|
|---|
| [16931] | 1256 | float x_pointer = 0.f;
|
|---|
| 1257 |
|
|---|
| 1258 | // If we have a selecting box (i.e. when you have selected letters, not just when
|
|---|
| 1259 | // the pointer is between two letters) we need to process all letters once
|
|---|
| 1260 | // before we do it the second time and render all the text. We can't do it
|
|---|
| 1261 | // in the same loop because text will have been drawn, so it will disappear when
|
|---|
| 1262 | // drawn behind the text that has already been drawn. Confusing, well it's necessary
|
|---|
| 1263 | // (I think).
|
|---|
| 1264 |
|
|---|
| 1265 | if (SelectingText())
|
|---|
| 1266 | {
|
|---|
| 1267 | // Now m_iBufferPos_Tail can be of both sides of m_iBufferPos,
|
|---|
| 1268 | // just like you can select from right to left, as you can
|
|---|
| 1269 | // left to right. Is there a difference? Yes, the pointer
|
|---|
| 1270 | // be placed accordingly, so that if you select shift and
|
|---|
| 1271 | // expand this selection, it will expand on appropriate side.
|
|---|
| 1272 | // Anyway, since the drawing procedure needs "To" to be
|
|---|
| 1273 | // greater than from, we need virtual values that might switch
|
|---|
| 1274 | // place.
|
|---|
| [24268] | 1275 | int virtualFrom = 0;
|
|---|
| 1276 | int virtualTo = 0;
|
|---|
| [16931] | 1277 |
|
|---|
| [1904] | 1278 | if (m_iBufferPos_Tail >= m_iBufferPos)
|
|---|
| 1279 | {
|
|---|
| [24268] | 1280 | virtualFrom = m_iBufferPos;
|
|---|
| 1281 | virtualTo = m_iBufferPos_Tail;
|
|---|
| [1904] | 1282 | }
|
|---|
| 1283 | else
|
|---|
| 1284 | {
|
|---|
| [24268] | 1285 | virtualFrom = m_iBufferPos_Tail;
|
|---|
| 1286 | virtualTo = m_iBufferPos;
|
|---|
| [1904] | 1287 | }
|
|---|
| 1288 |
|
|---|
| [1237] | 1289 |
|
|---|
| [16931] | 1290 | bool done = false;
|
|---|
| 1291 | for (std::list<SRow>::const_iterator it = m_CharacterPositions.begin();
|
|---|
| [26870] | 1292 | it != m_CharacterPositions.end();
|
|---|
| 1293 | ++it, buffered_y += ls, x_pointer = 0.f)
|
|---|
| [1237] | 1294 | {
|
|---|
| [23005] | 1295 | if (m_MultiLine && buffered_y > m_CachedActualSize.GetHeight())
|
|---|
| [16931] | 1296 | break;
|
|---|
| [1394] | 1297 |
|
|---|
| [16931] | 1298 | // We might as well use 'i' here to iterate, because we need it
|
|---|
| 1299 | // (often compared against ints, so don't make it size_t)
|
|---|
| [26870] | 1300 | for (int i = 0; i < (int)it->m_ListOfX.size() + 2; ++i)
|
|---|
| [1237] | 1301 | {
|
|---|
| [24268] | 1302 | if (it->m_ListStart + i == virtualFrom)
|
|---|
| [16931] | 1303 | {
|
|---|
| 1304 | // we won't actually draw it now, because we don't
|
|---|
| 1305 | // know the width of each glyph to that position.
|
|---|
| 1306 | // we need to go along with the iteration, and
|
|---|
| 1307 | // make a mark where the box started:
|
|---|
| 1308 | drawing_box = true; // will turn false when finally rendered.
|
|---|
| [1904] | 1309 |
|
|---|
| [16931] | 1310 | // Get current x position
|
|---|
| 1311 | box_x = x_pointer;
|
|---|
| [1394] | 1312 | }
|
|---|
| 1313 |
|
|---|
| [26870] | 1314 | const bool at_end = (i == (int)it->m_ListOfX.size() + 1);
|
|---|
| [1904] | 1315 |
|
|---|
| [24268] | 1316 | if (drawing_box && (it->m_ListStart + i == virtualTo || at_end))
|
|---|
| [16931] | 1317 | {
|
|---|
| 1318 | // Depending on if it's just a row change, or if it's
|
|---|
| 1319 | // the end of the select box, do slightly different things.
|
|---|
| 1320 | if (at_end)
|
|---|
| [1904] | 1321 | {
|
|---|
| [24268] | 1322 | if (it->m_ListStart + i != virtualFrom)
|
|---|
| [16931] | 1323 | // and actually add a white space! yes, this is done in any common input
|
|---|
| [23005] | 1324 | x_pointer += font.GetCharacterWidth(L' ');
|
|---|
| [16931] | 1325 | }
|
|---|
| 1326 | else
|
|---|
| 1327 | {
|
|---|
| 1328 | drawing_box = false;
|
|---|
| 1329 | done = true;
|
|---|
| 1330 | }
|
|---|
| [1904] | 1331 |
|
|---|
| [16931] | 1332 | CRect rect;
|
|---|
| 1333 | // Set 'rect' depending on if it's a multiline control, or a one-line control
|
|---|
| [23005] | 1334 | if (m_MultiLine)
|
|---|
| [16931] | 1335 | {
|
|---|
| [20088] | 1336 | rect = CRect(
|
|---|
| [23005] | 1337 | m_CachedActualSize.left + box_x + m_BufferZone,
|
|---|
| [20088] | 1338 | m_CachedActualSize.top + buffered_y + (h - ls) / 2,
|
|---|
| [23005] | 1339 | m_CachedActualSize.left + x_pointer + m_BufferZone,
|
|---|
| [20088] | 1340 | m_CachedActualSize.top + buffered_y + (h + ls) / 2);
|
|---|
| [1904] | 1341 |
|
|---|
| [16931] | 1342 | if (rect.bottom < m_CachedActualSize.top)
|
|---|
| 1343 | continue;
|
|---|
| [1904] | 1344 |
|
|---|
| [16931] | 1345 | if (rect.top < m_CachedActualSize.top)
|
|---|
| 1346 | rect.top = m_CachedActualSize.top;
|
|---|
| [1904] | 1347 |
|
|---|
| [16931] | 1348 | if (rect.bottom > m_CachedActualSize.bottom)
|
|---|
| 1349 | rect.bottom = m_CachedActualSize.bottom;
|
|---|
| 1350 | }
|
|---|
| 1351 | else // if one-line
|
|---|
| 1352 | {
|
|---|
| [20088] | 1353 | rect = CRect(
|
|---|
| [23005] | 1354 | m_CachedActualSize.left + box_x + m_BufferZone - m_HorizontalScroll,
|
|---|
| [20088] | 1355 | m_CachedActualSize.top + buffered_y + (h - ls) / 2,
|
|---|
| [23005] | 1356 | m_CachedActualSize.left + x_pointer + m_BufferZone - m_HorizontalScroll,
|
|---|
| [20088] | 1357 | m_CachedActualSize.top + buffered_y + (h + ls) / 2);
|
|---|
| [1904] | 1358 |
|
|---|
| [16931] | 1359 | if (rect.left < m_CachedActualSize.left)
|
|---|
| 1360 | rect.left = m_CachedActualSize.left;
|
|---|
| [1904] | 1361 |
|
|---|
| [16931] | 1362 | if (rect.right > m_CachedActualSize.right)
|
|---|
| 1363 | rect.right = m_CachedActualSize.right;
|
|---|
| [1904] | 1364 | }
|
|---|
| 1365 |
|
|---|
| [25591] | 1366 | m_pGUI.DrawSprite(m_SpriteSelectArea, canvas, rect);
|
|---|
| [1904] | 1367 | }
|
|---|
| 1368 |
|
|---|
| [16931] | 1369 | if (i < (int)it->m_ListOfX.size())
|
|---|
| [1904] | 1370 | {
|
|---|
| [23005] | 1371 | if (!m_Mask)
|
|---|
| [25392] | 1372 | x_pointer += font.GetCharacterWidth((*m_Caption)[it->m_ListStart + i]);
|
|---|
| [16931] | 1373 | else
|
|---|
| [23005] | 1374 | x_pointer += font.GetCharacterWidth(mask_char);
|
|---|
| [1904] | 1375 | }
|
|---|
| [1394] | 1376 | }
|
|---|
| [16931] | 1377 |
|
|---|
| 1378 | if (done)
|
|---|
| 1379 | break;
|
|---|
| 1380 |
|
|---|
| 1381 | // If we're about to draw a box, and all of a sudden changes
|
|---|
| 1382 | // line, we need to draw that line's box, and then reset
|
|---|
| 1383 | // the box drawing to the beginning of the new line.
|
|---|
| 1384 | if (drawing_box)
|
|---|
| 1385 | box_x = 0.f;
|
|---|
| [1904] | 1386 | }
|
|---|
| [16931] | 1387 | }
|
|---|
| [1394] | 1388 |
|
|---|
| [16931] | 1389 | // Reset some from previous run
|
|---|
| 1390 | buffered_y = -scroll;
|
|---|
| [16244] | 1391 |
|
|---|
| [16931] | 1392 | // Setup initial color (then it might change and change back, when drawing selected area)
|
|---|
| [25645] | 1393 | textRenderer.SetCurrentColor(m_TextColor);
|
|---|
| [1904] | 1394 |
|
|---|
| [16931] | 1395 | bool using_selected_color = false;
|
|---|
| [16244] | 1396 |
|
|---|
| [16931] | 1397 | for (std::list<SRow>::const_iterator it = m_CharacterPositions.begin();
|
|---|
| [26870] | 1398 | it != m_CharacterPositions.end();
|
|---|
| 1399 | ++it, buffered_y += ls)
|
|---|
| [16931] | 1400 | {
|
|---|
| [23005] | 1401 | if (buffered_y + m_BufferZone >= -ls || !m_MultiLine)
|
|---|
| [1904] | 1402 | {
|
|---|
| [23005] | 1403 | if (m_MultiLine && buffered_y + m_BufferZone > m_CachedActualSize.GetHeight())
|
|---|
| [16931] | 1404 | break;
|
|---|
| [1237] | 1405 |
|
|---|
| [25648] | 1406 | const CVector2D savedTranslate = textRenderer.GetTranslate();
|
|---|
| [16244] | 1407 |
|
|---|
| [16931] | 1408 | // Text must always be drawn in integer values. So we have to convert scroll
|
|---|
| [23005] | 1409 | if (m_MultiLine)
|
|---|
| [25645] | 1410 | textRenderer.Translate(0.f, -(float)(int)scroll);
|
|---|
| [16931] | 1411 | else
|
|---|
| [25645] | 1412 | textRenderer.Translate(-(float)(int)m_HorizontalScroll, 0.f);
|
|---|
| [1394] | 1413 |
|
|---|
| [16931] | 1414 | // We might as well use 'i' here, because we need it
|
|---|
| 1415 | // (often compared against ints, so don't make it size_t)
|
|---|
| [26870] | 1416 | for (int i = 0; i < (int)it->m_ListOfX.size() + 1; ++i)
|
|---|
| [16931] | 1417 | {
|
|---|
| [23005] | 1418 | if (!m_MultiLine && i < (int)it->m_ListOfX.size())
|
|---|
| [1904] | 1419 | {
|
|---|
| [23005] | 1420 | if (it->m_ListOfX[i] - m_HorizontalScroll < -m_BufferZone)
|
|---|
| [1904] | 1421 | {
|
|---|
| [16931] | 1422 | // We still need to translate the OpenGL matrix
|
|---|
| 1423 | if (i == 0)
|
|---|
| [25645] | 1424 | textRenderer.Translate(it->m_ListOfX[i], 0.f);
|
|---|
| [14098] | 1425 | else
|
|---|
| [26870] | 1426 | textRenderer.Translate(it->m_ListOfX[i] - it->m_ListOfX[i - 1], 0.f);
|
|---|
| [1904] | 1427 |
|
|---|
| [16931] | 1428 | continue;
|
|---|
| [1904] | 1429 | }
|
|---|
| 1430 | }
|
|---|
| 1431 |
|
|---|
| [16931] | 1432 | // End of selected area, change back color
|
|---|
| [20088] | 1433 | if (SelectingText() && it->m_ListStart + i == VirtualTo)
|
|---|
| [1904] | 1434 | {
|
|---|
| [16931] | 1435 | using_selected_color = false;
|
|---|
| [25645] | 1436 | textRenderer.SetCurrentColor(m_TextColor);
|
|---|
| [16931] | 1437 | }
|
|---|
| [1904] | 1438 |
|
|---|
| [16931] | 1439 | // selecting only one, then we need only to draw a cursor.
|
|---|
| [25976] | 1440 | if (i != (int)it->m_ListOfX.size() && it->m_ListStart + i == m_iBufferPos && m_CursorVisState && !m_Readonly)
|
|---|
| [16931] | 1441 | textRenderer.Put(0.0f, 0.0f, L"_");
|
|---|
| 1442 |
|
|---|
| 1443 | // Drawing selected area
|
|---|
| 1444 | if (SelectingText() &&
|
|---|
| [26870] | 1445 | it->m_ListStart + i >= VirtualFrom &&
|
|---|
| 1446 | it->m_ListStart + i < VirtualTo &&
|
|---|
| 1447 | !using_selected_color)
|
|---|
| [16931] | 1448 | {
|
|---|
| 1449 | using_selected_color = true;
|
|---|
| [25645] | 1450 | textRenderer.SetCurrentColor(m_TextColorSelected);
|
|---|
| [1904] | 1451 | }
|
|---|
| 1452 |
|
|---|
| [16931] | 1453 | if (i != (int)it->m_ListOfX.size())
|
|---|
| 1454 | {
|
|---|
| [23005] | 1455 | if (!m_Mask)
|
|---|
| [25392] | 1456 | textRenderer.PrintfAdvance(L"%lc", (*m_Caption)[it->m_ListStart + i]);
|
|---|
| [16931] | 1457 | else
|
|---|
| 1458 | textRenderer.PrintfAdvance(L"%lc", mask_char);
|
|---|
| 1459 | }
|
|---|
| 1460 |
|
|---|
| 1461 | // check it's now outside a one-liner, then we'll break
|
|---|
| [23005] | 1462 | if (!m_MultiLine && i < (int)it->m_ListOfX.size() &&
|
|---|
| [26870] | 1463 | it->m_ListOfX[i] - m_HorizontalScroll > m_CachedActualSize.GetWidth() - m_BufferZone)
|
|---|
| [16931] | 1464 | break;
|
|---|
| [1904] | 1465 | }
|
|---|
| [10985] | 1466 |
|
|---|
| [16931] | 1467 | if (it->m_ListStart + (int)it->m_ListOfX.size() == m_iBufferPos)
|
|---|
| 1468 | {
|
|---|
| [25645] | 1469 | textRenderer.SetCurrentColor(m_TextColor);
|
|---|
| [25976] | 1470 | if (m_CursorVisState && !m_Readonly)
|
|---|
| [16931] | 1471 | textRenderer.PutAdvance(L"_");
|
|---|
| 1472 |
|
|---|
| 1473 | if (using_selected_color)
|
|---|
| [25645] | 1474 | textRenderer.SetCurrentColor(m_TextColorSelected);
|
|---|
| [16931] | 1475 | }
|
|---|
| 1476 |
|
|---|
| [25648] | 1477 | textRenderer.ResetTranslate(savedTranslate);
|
|---|
| [1237] | 1478 | }
|
|---|
| 1479 |
|
|---|
| [25645] | 1480 | textRenderer.Translate(0.f, ls);
|
|---|
| [16931] | 1481 | }
|
|---|
| [1237] | 1482 |
|
|---|
| [25607] | 1483 | canvas.DrawText(textRenderer);
|
|---|
| [26870] | 1484 | }
|
|---|
| [10985] | 1485 |
|
|---|
| [26870] | 1486 | void CInput::Draw(CCanvas2D& canvas)
|
|---|
| 1487 | {
|
|---|
| 1488 | // We'll have to setup clipping manually, since we're doing the rendering manually.
|
|---|
| 1489 | CRect cliparea(m_CachedActualSize);
|
|---|
| 1490 |
|
|---|
| 1491 | // First we'll figure out the clipping area, which is the cached actual size
|
|---|
| 1492 | // substracted by an optional scrollbar
|
|---|
| 1493 | if (m_ScrollBar)
|
|---|
| 1494 | {
|
|---|
| 1495 | // substract scrollbar from cliparea
|
|---|
| 1496 | if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
|
|---|
| 1497 | cliparea.right <= GetScrollBar(0).GetOuterRect().right)
|
|---|
| 1498 | cliparea.right = GetScrollBar(0).GetOuterRect().left;
|
|---|
| 1499 |
|
|---|
| 1500 | if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
|
|---|
| 1501 | cliparea.left < GetScrollBar(0).GetOuterRect().right)
|
|---|
| 1502 | cliparea.left = GetScrollBar(0).GetOuterRect().right;
|
|---|
| 1503 | }
|
|---|
| 1504 |
|
|---|
| 1505 | const bool isClipped = cliparea != CRect();
|
|---|
| 1506 | if (isClipped)
|
|---|
| 1507 | {
|
|---|
| 1508 | if (cliparea.GetWidth() <= 0.0f || cliparea.GetHeight() <= 0.0f)
|
|---|
| 1509 | return;
|
|---|
| [27798] | 1510 | CCanvas2D::ScopedScissor scopedScissor(canvas, cliparea);
|
|---|
| 1511 | DrawContent(canvas);
|
|---|
| [26870] | 1512 | }
|
|---|
| [27798] | 1513 | else
|
|---|
| 1514 | DrawContent(canvas);
|
|---|
| [26870] | 1515 |
|
|---|
| [25392] | 1516 | if (m_Caption->empty() && !m_PlaceholderText->GetRawString().empty())
|
|---|
| [25591] | 1517 | DrawPlaceholderText(canvas, cliparea);
|
|---|
| [25587] | 1518 |
|
|---|
| 1519 | // Draw scrollbars on top of the content
|
|---|
| 1520 | if (m_ScrollBar && m_MultiLine)
|
|---|
| [25591] | 1521 | IGUIScrollBarOwner::Draw(canvas);
|
|---|
| [25587] | 1522 |
|
|---|
| 1523 | // Draw the overlays last
|
|---|
| [25591] | 1524 | m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize);
|
|---|
| [1237] | 1525 | }
|
|---|
| [1394] | 1526 |
|
|---|
| [25591] | 1527 | void CInput::DrawPlaceholderText(CCanvas2D& canvas, const CRect& clipping)
|
|---|
| [24433] | 1528 | {
|
|---|
| 1529 | if (!m_GeneratedPlaceholderTextValid)
|
|---|
| 1530 | SetupGeneratedPlaceholderText();
|
|---|
| 1531 |
|
|---|
| [25591] | 1532 | m_GeneratedPlaceholderText.Draw(m_pGUI, canvas, m_PlaceholderColor, m_CachedActualSize.TopLeft(), clipping);
|
|---|
| [24433] | 1533 | }
|
|---|
| 1534 |
|
|---|
| [1394] | 1535 | void CInput::UpdateText(int from, int to_before, int to_after)
|
|---|
| 1536 | {
|
|---|
| [25392] | 1537 | CStrW& caption = m_Caption.GetMutable();
|
|---|
| [23927] | 1538 |
|
|---|
| [25392] | 1539 | if (m_MaxLength != 0 && caption.length() > static_cast<size_t>(m_MaxLength))
|
|---|
| [27271] | 1540 | caption.erase(m_MaxLength);
|
|---|
| [1394] | 1541 |
|
|---|
| [25392] | 1542 | CStrIntern font_name(m_Font->ToUTF8());
|
|---|
| 1543 |
|
|---|
| [14098] | 1544 | wchar_t mask_char = L'*';
|
|---|
| [25392] | 1545 | if (m_Mask && m_MaskChar->length() > 0)
|
|---|
| 1546 | mask_char = (*m_MaskChar)[0];
|
|---|
| [14098] | 1547 |
|
|---|
| [7664] | 1548 | // Ensure positions are valid after caption changes
|
|---|
| [25392] | 1549 | m_iBufferPos = std::min(m_iBufferPos, static_cast<int>(caption.size()));
|
|---|
| 1550 | m_iBufferPos_Tail = std::min(m_iBufferPos_Tail, static_cast<int>(caption.size()));
|
|---|
| [16261] | 1551 | UpdateBufferPositionSetting();
|
|---|
| [7664] | 1552 |
|
|---|
| [13539] | 1553 | if (font_name.empty())
|
|---|
| [1904] | 1554 | {
|
|---|
| [20088] | 1555 | // Destroy everything stored, there's no font, so there can be no data.
|
|---|
| [1904] | 1556 | m_CharacterPositions.clear();
|
|---|
| 1557 | return;
|
|---|
| 1558 | }
|
|---|
| 1559 |
|
|---|
| [1394] | 1560 | SRow row;
|
|---|
| 1561 | row.m_ListStart = 0;
|
|---|
| [16244] | 1562 |
|
|---|
| [2585] | 1563 | int to = 0; // make sure it's initialized
|
|---|
| [1394] | 1564 |
|
|---|
| 1565 | if (to_before == -1)
|
|---|
| [25392] | 1566 | to = static_cast<int>(caption.length());
|
|---|
| [1394] | 1567 |
|
|---|
| [14016] | 1568 | CFontMetrics font(font_name);
|
|---|
| [1394] | 1569 |
|
|---|
| [6226] | 1570 | std::list<SRow>::iterator current_line;
|
|---|
| [1394] | 1571 |
|
|---|
| 1572 | // Used to ... TODO
|
|---|
| 1573 | int check_point_row_start = -1;
|
|---|
| 1574 | int check_point_row_end = -1;
|
|---|
| 1575 |
|
|---|
| 1576 | // Reset
|
|---|
| 1577 | if (from == 0 && to_before == -1)
|
|---|
| 1578 | {
|
|---|
| 1579 | m_CharacterPositions.clear();
|
|---|
| 1580 | current_line = m_CharacterPositions.begin();
|
|---|
| 1581 | }
|
|---|
| 1582 | else
|
|---|
| 1583 | {
|
|---|
| [9362] | 1584 | ENSURE(to_before != -1);
|
|---|
| [1394] | 1585 |
|
|---|
| [16931] | 1586 | std::list<SRow>::iterator destroy_row_from;
|
|---|
| 1587 | std::list<SRow>::iterator destroy_row_to;
|
|---|
| 1588 | // Used to check if the above has been set to anything,
|
|---|
| [1904] | 1589 | // previously a comparison like:
|
|---|
| [6226] | 1590 | // destroy_row_from == std::list<SRow>::iterator()
|
|---|
| [1904] | 1591 | // ... was used, but it didn't work with GCC.
|
|---|
| [16931] | 1592 | bool destroy_row_from_used = false;
|
|---|
| 1593 | bool destroy_row_to_used = false;
|
|---|
| [1394] | 1594 |
|
|---|
| 1595 | // Iterate, and remove everything between 'from' and 'to_before'
|
|---|
| 1596 | // actually remove the entire lines they are on, it'll all have
|
|---|
| 1597 | // to be redone. And when going along, we'll delete a row at a time
|
|---|
| 1598 | // when continuing to see how much more after 'to' we need to remake.
|
|---|
| [16931] | 1599 | int i = 0;
|
|---|
| 1600 | for (std::list<SRow>::iterator it = m_CharacterPositions.begin();
|
|---|
| 1601 | it != m_CharacterPositions.end();
|
|---|
| 1602 | ++it, ++i)
|
|---|
| [1394] | 1603 | {
|
|---|
| [20088] | 1604 | if (!destroy_row_from_used && it->m_ListStart > from)
|
|---|
| [1394] | 1605 | {
|
|---|
| 1606 | // Destroy the previous line, and all to 'to_before'
|
|---|
| 1607 | destroy_row_from = it;
|
|---|
| 1608 | --destroy_row_from;
|
|---|
| 1609 |
|
|---|
| [1904] | 1610 | destroy_row_from_used = true;
|
|---|
| 1611 |
|
|---|
| [1394] | 1612 | // For the rare case that we might remove characters to a word
|
|---|
| [1904] | 1613 | // so that it suddenly fits on the previous row,
|
|---|
| [16931] | 1614 | // we need to by standards re-do the whole previous line too
|
|---|
| [1904] | 1615 | // (if one exists)
|
|---|
| [1394] | 1616 | if (destroy_row_from != m_CharacterPositions.begin())
|
|---|
| 1617 | --destroy_row_from;
|
|---|
| 1618 | }
|
|---|
| 1619 |
|
|---|
| [20088] | 1620 | if (!destroy_row_to_used && it->m_ListStart > to_before)
|
|---|
| [1394] | 1621 | {
|
|---|
| 1622 | destroy_row_to = it;
|
|---|
| [1904] | 1623 | destroy_row_to_used = true;
|
|---|
| 1624 |
|
|---|
| [1394] | 1625 | // If it isn't the last row, we'll add another row to delete,
|
|---|
| [16931] | 1626 | // just so we can see if the last restorted line is
|
|---|
| [1394] | 1627 | // identical to what it was before. If it isn't, then we'll
|
|---|
| 1628 | // have to continue.
|
|---|
| 1629 | // 'check_point_row_start' is where we store how the that
|
|---|
| 1630 | // line looked.
|
|---|
| 1631 | if (destroy_row_to != m_CharacterPositions.end())
|
|---|
| 1632 | {
|
|---|
| 1633 | check_point_row_start = destroy_row_to->m_ListStart;
|
|---|
| [1908] | 1634 | check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
|
|---|
| [1394] | 1635 | if (destroy_row_to->m_ListOfX.empty())
|
|---|
| 1636 | ++check_point_row_end;
|
|---|
| 1637 | }
|
|---|
| 1638 |
|
|---|
| 1639 | ++destroy_row_to;
|
|---|
| 1640 | break;
|
|---|
| 1641 | }
|
|---|
| 1642 | }
|
|---|
| 1643 |
|
|---|
| [20088] | 1644 | if (!destroy_row_from_used)
|
|---|
| [1394] | 1645 | {
|
|---|
| 1646 | destroy_row_from = m_CharacterPositions.end();
|
|---|
| 1647 | --destroy_row_from;
|
|---|
| 1648 |
|
|---|
| [1904] | 1649 | // As usual, let's destroy another row back
|
|---|
| 1650 | if (destroy_row_from != m_CharacterPositions.begin())
|
|---|
| 1651 | --destroy_row_from;
|
|---|
| 1652 |
|
|---|
| [1394] | 1653 | current_line = destroy_row_from;
|
|---|
| 1654 | }
|
|---|
| 1655 |
|
|---|
| [20088] | 1656 | if (!destroy_row_to_used)
|
|---|
| [1394] | 1657 | {
|
|---|
| [9646] | 1658 | destroy_row_to = m_CharacterPositions.end();
|
|---|
| [1394] | 1659 | check_point_row_start = -1;
|
|---|
| 1660 | }
|
|---|
| 1661 |
|
|---|
| 1662 | // set 'from' to the row we'll destroy from
|
|---|
| 1663 | // and 'to' to the row we'll destroy to
|
|---|
| 1664 | from = destroy_row_from->m_ListStart;
|
|---|
| [16244] | 1665 |
|
|---|
| [1394] | 1666 | if (destroy_row_to != m_CharacterPositions.end())
|
|---|
| [9646] | 1667 | to = destroy_row_to->m_ListStart; // notice it will iterate [from, to), so it will never reach to.
|
|---|
| [1394] | 1668 | else
|
|---|
| [25392] | 1669 | to = static_cast<int>(caption.length());
|
|---|
| [1394] | 1670 |
|
|---|
| 1671 |
|
|---|
| 1672 | // Setup the first row
|
|---|
| 1673 | row.m_ListStart = destroy_row_from->m_ListStart;
|
|---|
| 1674 |
|
|---|
| [6226] | 1675 | std::list<SRow>::iterator temp_it = destroy_row_to;
|
|---|
| [1394] | 1676 | --temp_it;
|
|---|
| 1677 |
|
|---|
| [13539] | 1678 | current_line = m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
|
|---|
| [16244] | 1679 |
|
|---|
| [1394] | 1680 | // If there has been a change in number of characters
|
|---|
| 1681 | // we need to change all m_ListStart that comes after
|
|---|
| 1682 | // the interval we just destroyed. We'll change all
|
|---|
| 1683 | // values with the delta change of the string length.
|
|---|
| 1684 | int delta = to_after - to_before;
|
|---|
| 1685 | if (delta != 0)
|
|---|
| 1686 | {
|
|---|
| [13539] | 1687 | for (std::list<SRow>::iterator it = current_line;
|
|---|
| [16931] | 1688 | it != m_CharacterPositions.end();
|
|---|
| 1689 | ++it)
|
|---|
| [1394] | 1690 | it->m_ListStart += delta;
|
|---|
| 1691 |
|
|---|
| 1692 | // Update our check point too!
|
|---|
| 1693 | check_point_row_start += delta;
|
|---|
| 1694 | check_point_row_end += delta;
|
|---|
| 1695 |
|
|---|
| [25392] | 1696 | if (to != static_cast<int>(caption.length()))
|
|---|
| [1394] | 1697 | to += delta;
|
|---|
| 1698 | }
|
|---|
| 1699 | }
|
|---|
| [16244] | 1700 |
|
|---|
| [16931] | 1701 | int last_word_started = from;
|
|---|
| [1394] | 1702 | float x_pos = 0.f;
|
|---|
| 1703 |
|
|---|
| 1704 | //if (to_before != -1)
|
|---|
| 1705 | // return;
|
|---|
| 1706 |
|
|---|
| [16931] | 1707 | for (int i = from; i < to; ++i)
|
|---|
| [1394] | 1708 | {
|
|---|
| [25392] | 1709 | if (caption[i] == L'\n' && m_MultiLine)
|
|---|
| [1394] | 1710 | {
|
|---|
| [25392] | 1711 | if (i == to-1 && to != static_cast<int>(caption.length()))
|
|---|
| [1394] | 1712 | break; // it will be added outside
|
|---|
| [16244] | 1713 |
|
|---|
| [16931] | 1714 | current_line = m_CharacterPositions.insert(current_line, row);
|
|---|
| [1394] | 1715 | ++current_line;
|
|---|
| 1716 |
|
|---|
| 1717 | // Setup the next row:
|
|---|
| 1718 | row.m_ListOfX.clear();
|
|---|
| 1719 | row.m_ListStart = i+1;
|
|---|
| 1720 | x_pos = 0.f;
|
|---|
| 1721 | }
|
|---|
| 1722 | else
|
|---|
| 1723 | {
|
|---|
| [25392] | 1724 | if (caption[i] == L' '/* || TODO Gee (2004-10-13): the '-' disappears, fix.
|
|---|
| 1725 | caption[i] == L'-'*/)
|
|---|
| [1394] | 1726 | last_word_started = i+1;
|
|---|
| 1727 |
|
|---|
| [23005] | 1728 | if (!m_Mask)
|
|---|
| [25392] | 1729 | x_pos += font.GetCharacterWidth(caption[i]);
|
|---|
| [14098] | 1730 | else
|
|---|
| [23005] | 1731 | x_pos += font.GetCharacterWidth(mask_char);
|
|---|
| [1394] | 1732 |
|
|---|
| [23005] | 1733 | if (x_pos >= GetTextAreaWidth() && m_MultiLine)
|
|---|
| [1394] | 1734 | {
|
|---|
| 1735 | // The following decides whether it will word-wrap a word,
|
|---|
| 1736 | // or if it's only one word on the line, where it has to
|
|---|
| 1737 | // break the word apart.
|
|---|
| 1738 | if (last_word_started == row.m_ListStart)
|
|---|
| 1739 | {
|
|---|
| 1740 | last_word_started = i;
|
|---|
| 1741 | row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started));
|
|---|
| [16931] | 1742 | //row.m_ListOfX.push_back(x_pos);
|
|---|
| [1394] | 1743 | //continue;
|
|---|
| 1744 | }
|
|---|
| 1745 | else
|
|---|
| 1746 | {
|
|---|
| 1747 | // regular word-wrap
|
|---|
| 1748 | row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started+1));
|
|---|
| 1749 | }
|
|---|
| [16244] | 1750 |
|
|---|
| [1394] | 1751 | // Now, create a new line:
|
|---|
| 1752 | // notice: when we enter a newline, you can stand with the cursor
|
|---|
| 1753 | // both before and after that character, being on different
|
|---|
| 1754 | // rows. With automatic word-wrapping, that is not possible. Which
|
|---|
| 1755 | // is intuitively correct.
|
|---|
| 1756 |
|
|---|
| [16931] | 1757 | current_line = m_CharacterPositions.insert(current_line, row);
|
|---|
| [1394] | 1758 | ++current_line;
|
|---|
| 1759 |
|
|---|
| 1760 | // Setup the next row:
|
|---|
| 1761 | row.m_ListOfX.clear();
|
|---|
| 1762 | row.m_ListStart = last_word_started;
|
|---|
| 1763 |
|
|---|
| [16931] | 1764 | i = last_word_started-1;
|
|---|
| [1394] | 1765 |
|
|---|
| 1766 | x_pos = 0.f;
|
|---|
| 1767 | }
|
|---|
| 1768 | else
|
|---|
| 1769 | // Get width of this character:
|
|---|
| [16931] | 1770 | row.m_ListOfX.push_back(x_pos);
|
|---|
| [1394] | 1771 | }
|
|---|
| 1772 |
|
|---|
| 1773 | // Check if it's the last iteration, and we're not revising the whole string
|
|---|
| 1774 | // because in that case, more word-wrapping might be needed.
|
|---|
| 1775 | // also check if the current line isn't the end
|
|---|
| 1776 | if (to_before != -1 && i == to-1 && current_line != m_CharacterPositions.end())
|
|---|
| 1777 | {
|
|---|
| [16931] | 1778 | // check all rows and see if any existing
|
|---|
| [1904] | 1779 | if (row.m_ListStart != check_point_row_start)
|
|---|
| [1394] | 1780 | {
|
|---|
| [16931] | 1781 | std::list<SRow>::iterator destroy_row_from;
|
|---|
| 1782 | std::list<SRow>::iterator destroy_row_to;
|
|---|
| 1783 | // Are used to check if the above has been set to anything,
|
|---|
| [1904] | 1784 | // previously a comparison like:
|
|---|
| [6226] | 1785 | // destroy_row_from == std::list<SRow>::iterator()
|
|---|
| [1904] | 1786 | // was used, but it didn't work with GCC.
|
|---|
| [16931] | 1787 | bool destroy_row_from_used = false;
|
|---|
| 1788 | bool destroy_row_to_used = false;
|
|---|
| [1394] | 1789 |
|
|---|
| [1904] | 1790 | // Iterate, and remove everything between 'from' and 'to_before'
|
|---|
| 1791 | // actually remove the entire lines they are on, it'll all have
|
|---|
| 1792 | // to be redone. And when going along, we'll delete a row at a time
|
|---|
| 1793 | // when continuing to see how much more after 'to' we need to remake.
|
|---|
| [24268] | 1794 |
|
|---|
| [16931] | 1795 | for (std::list<SRow>::iterator it = m_CharacterPositions.begin();
|
|---|
| 1796 | it != m_CharacterPositions.end();
|
|---|
| [24268] | 1797 | ++it)
|
|---|
| [1394] | 1798 | {
|
|---|
| [20088] | 1799 | if (!destroy_row_from_used && it->m_ListStart > check_point_row_start)
|
|---|
| [1904] | 1800 | {
|
|---|
| 1801 | // Destroy the previous line, and all to 'to_before'
|
|---|
| 1802 | //if (i >= 2)
|
|---|
| 1803 | // destroy_row_from = it-2;
|
|---|
| 1804 | //else
|
|---|
| 1805 | // destroy_row_from = it-1;
|
|---|
| 1806 | destroy_row_from = it;
|
|---|
| 1807 | destroy_row_from_used = true;
|
|---|
| 1808 | //--destroy_row_from;
|
|---|
| 1809 | }
|
|---|
| [1394] | 1810 |
|
|---|
| [20088] | 1811 | if (!destroy_row_to_used && it->m_ListStart > check_point_row_end)
|
|---|
| [1904] | 1812 | {
|
|---|
| 1813 | destroy_row_to = it;
|
|---|
| 1814 | destroy_row_to_used = true;
|
|---|
| [1394] | 1815 |
|
|---|
| [1904] | 1816 | // If it isn't the last row, we'll add another row to delete,
|
|---|
| [16931] | 1817 | // just so we can see if the last restorted line is
|
|---|
| [1904] | 1818 | // identical to what it was before. If it isn't, then we'll
|
|---|
| 1819 | // have to continue.
|
|---|
| 1820 | // 'check_point_row_start' is where we store how the that
|
|---|
| 1821 | // line looked.
|
|---|
| 1822 | if (destroy_row_to != m_CharacterPositions.end())
|
|---|
| [1394] | 1823 | {
|
|---|
| [1904] | 1824 | check_point_row_start = destroy_row_to->m_ListStart;
|
|---|
| [1908] | 1825 | check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
|
|---|
| [1904] | 1826 | if (destroy_row_to->m_ListOfX.empty())
|
|---|
| 1827 | ++check_point_row_end;
|
|---|
| [1394] | 1828 | }
|
|---|
| [1904] | 1829 | else
|
|---|
| 1830 | check_point_row_start = check_point_row_end = -1;
|
|---|
| [1394] | 1831 |
|
|---|
| [1904] | 1832 | ++destroy_row_to;
|
|---|
| 1833 | break;
|
|---|
| 1834 | }
|
|---|
| 1835 | }
|
|---|
| [1394] | 1836 |
|
|---|
| [20088] | 1837 | if (!destroy_row_from_used)
|
|---|
| [1904] | 1838 | {
|
|---|
| 1839 | destroy_row_from = m_CharacterPositions.end();
|
|---|
| 1840 | --destroy_row_from;
|
|---|
| [1394] | 1841 |
|
|---|
| [1904] | 1842 | current_line = destroy_row_from;
|
|---|
| 1843 | }
|
|---|
| [1394] | 1844 |
|
|---|
| [20088] | 1845 | if (!destroy_row_to_used)
|
|---|
| [1904] | 1846 | {
|
|---|
| 1847 | destroy_row_to = m_CharacterPositions.end();
|
|---|
| 1848 | check_point_row_start = check_point_row_end = -1;
|
|---|
| 1849 | }
|
|---|
| [1394] | 1850 |
|
|---|
| [1904] | 1851 | if (destroy_row_to != m_CharacterPositions.end())
|
|---|
| 1852 | to = destroy_row_to->m_ListStart; // notice it will iterate [from, to[, so it will never reach to.
|
|---|
| 1853 | else
|
|---|
| [25392] | 1854 | to = static_cast<int>(caption.length());
|
|---|
| [1394] | 1855 |
|
|---|
| 1856 |
|
|---|
| [1904] | 1857 | // Set current line, new rows will be added before current_line, so
|
|---|
| 1858 | // we'll choose the destroy_row_to, because it won't be deleted
|
|---|
| 1859 | // in the coming erase.
|
|---|
| 1860 | current_line = destroy_row_to;
|
|---|
| [1394] | 1861 |
|
|---|
| [1904] | 1862 | m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
|
|---|
| 1863 | }
|
|---|
| 1864 | // else, the for loop will end naturally.
|
|---|
| 1865 | }
|
|---|
| 1866 | }
|
|---|
| 1867 | // This is kind of special, when we renew a some lines, then the last
|
|---|
| 1868 | // one will sometimes end with a space (' '), that really should
|
|---|
| 1869 | // be omitted when word-wrapping. So we'll check if the last row
|
|---|
| 1870 | // we'll add has got the same value as the next row.
|
|---|
| 1871 | if (current_line != m_CharacterPositions.end())
|
|---|
| 1872 | {
|
|---|
| [2585] | 1873 | if (row.m_ListStart + (int)row.m_ListOfX.size() == current_line->m_ListStart)
|
|---|
| [16931] | 1874 | row.m_ListOfX.resize(row.m_ListOfX.size()-1);
|
|---|
| [1904] | 1875 | }
|
|---|
| [1394] | 1876 |
|
|---|
| [1904] | 1877 | // add the final row (even if empty)
|
|---|
| [8932] | 1878 | m_CharacterPositions.insert(current_line, row);
|
|---|
| [1394] | 1879 |
|
|---|
| [23005] | 1880 | if (m_ScrollBar)
|
|---|
| [1904] | 1881 | {
|
|---|
| [23005] | 1882 | GetScrollBar(0).SetScrollRange(m_CharacterPositions.size() * font.GetLineSpacing() + m_BufferZone * 2.f);
|
|---|
| [8932] | 1883 | GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight());
|
|---|
| [1904] | 1884 | }
|
|---|
| 1885 | }
|
|---|
| 1886 |
|
|---|
| [16931] | 1887 | int CInput::GetMouseHoveringTextPosition() const
|
|---|
| [1904] | 1888 | {
|
|---|
| 1889 | if (m_CharacterPositions.empty())
|
|---|
| 1890 | return 0;
|
|---|
| 1891 |
|
|---|
| 1892 | // Return position
|
|---|
| [9646] | 1893 | int retPosition;
|
|---|
| [1904] | 1894 |
|
|---|
| [16931] | 1895 | std::list<SRow>::const_iterator current = m_CharacterPositions.begin();
|
|---|
| [1904] | 1896 |
|
|---|
| [25152] | 1897 | CVector2D mouse = m_pGUI.GetMousePos();
|
|---|
| [1904] | 1898 |
|
|---|
| [23005] | 1899 | if (m_MultiLine)
|
|---|
| [1904] | 1900 | {
|
|---|
| [16931] | 1901 | float scroll = 0.f;
|
|---|
| [23005] | 1902 | if (m_ScrollBar)
|
|---|
| [16931] | 1903 | scroll = GetScrollBarPos(0);
|
|---|
| [1904] | 1904 |
|
|---|
| 1905 | // Now get the height of the font.
|
|---|
| 1906 | // TODO: Get the real font
|
|---|
| [25392] | 1907 | CFontMetrics font(CStrIntern(m_Font->ToUTF8()));
|
|---|
| [1908] | 1908 | float spacing = (float)font.GetLineSpacing();
|
|---|
| [1904] | 1909 |
|
|---|
| 1910 | // Change mouse position relative to text.
|
|---|
| 1911 | mouse -= m_CachedActualSize.TopLeft();
|
|---|
| [25152] | 1912 | mouse.X -= m_BufferZone;
|
|---|
| 1913 | mouse.Y += scroll - m_BufferZone;
|
|---|
| [1904] | 1914 |
|
|---|
| [25152] | 1915 | int row = (int)((mouse.Y) / spacing);
|
|---|
| [1904] | 1916 |
|
|---|
| 1917 | if (row < 0)
|
|---|
| 1918 | row = 0;
|
|---|
| 1919 |
|
|---|
| [1908] | 1920 | if (row > (int)m_CharacterPositions.size()-1)
|
|---|
| 1921 | row = (int)m_CharacterPositions.size()-1;
|
|---|
| [1904] | 1922 |
|
|---|
| [6226] | 1923 | // TODO Gee (2004-11-21): Okay, I need a 'std::list' for some reasons, but I would really like to
|
|---|
| [1904] | 1924 | // be able to get the specific element here. This is hopefully a temporary hack.
|
|---|
| 1925 |
|
|---|
| [16931] | 1926 | for (int i = 0; i < row; ++i)
|
|---|
| [1904] | 1927 | ++current;
|
|---|
| [1394] | 1928 | }
|
|---|
| [1904] | 1929 | else
|
|---|
| 1930 | {
|
|---|
| 1931 | // current is already set to begin,
|
|---|
| 1932 | // but we'll change the mouse.x to fit our horizontal scrolling
|
|---|
| 1933 | mouse -= m_CachedActualSize.TopLeft();
|
|---|
| [25152] | 1934 | mouse.X -= m_BufferZone - m_HorizontalScroll;
|
|---|
| [1904] | 1935 | // mouse.y is moot
|
|---|
| 1936 | }
|
|---|
| [1394] | 1937 |
|
|---|
| [9646] | 1938 | retPosition = current->m_ListStart;
|
|---|
| [16244] | 1939 |
|
|---|
| [1904] | 1940 | // Okay, now loop through the glyphs to find the appropriate X position
|
|---|
| 1941 | float dummy;
|
|---|
| [25152] | 1942 | retPosition += GetXTextPosition(current, mouse.X, dummy);
|
|---|
| [1904] | 1943 |
|
|---|
| [9646] | 1944 | return retPosition;
|
|---|
| [1394] | 1945 | }
|
|---|
| [1904] | 1946 |
|
|---|
| 1947 | // Does not process horizontal scrolling, 'x' must be modified before inputted.
|
|---|
| [16931] | 1948 | int CInput::GetXTextPosition(const std::list<SRow>::const_iterator& current, const float& x, float& wanted) const
|
|---|
| [1904] | 1949 | {
|
|---|
| [16931] | 1950 | int ret = 0;
|
|---|
| 1951 | float previous = 0.f;
|
|---|
| 1952 | int i = 0;
|
|---|
| [1904] | 1953 |
|
|---|
| [16931] | 1954 | for (std::vector<float>::const_iterator it = current->m_ListOfX.begin();
|
|---|
| 1955 | it != current->m_ListOfX.end();
|
|---|
| 1956 | ++it, ++i)
|
|---|
| [1904] | 1957 | {
|
|---|
| 1958 | if (*it >= x)
|
|---|
| 1959 | {
|
|---|
| 1960 | if (x - previous >= *it - x)
|
|---|
| [9646] | 1961 | ret += i+1;
|
|---|
| [1904] | 1962 | else
|
|---|
| [9646] | 1963 | ret += i;
|
|---|
| [1904] | 1964 |
|
|---|
| 1965 | break;
|
|---|
| 1966 | }
|
|---|
| 1967 | previous = *it;
|
|---|
| 1968 | }
|
|---|
| 1969 | // If a position wasn't found, we will assume the last
|
|---|
| 1970 | // character of that line.
|
|---|
| [2585] | 1971 | if (i == (int)current->m_ListOfX.size())
|
|---|
| [1904] | 1972 | {
|
|---|
| [9646] | 1973 | ret += i;
|
|---|
| [1904] | 1974 | wanted = x;
|
|---|
| 1975 | }
|
|---|
| [16931] | 1976 | else
|
|---|
| 1977 | wanted = 0.f;
|
|---|
| [1904] | 1978 |
|
|---|
| [9646] | 1979 | return ret;
|
|---|
| [1904] | 1980 | }
|
|---|
| 1981 |
|
|---|
| 1982 | void CInput::DeleteCurSelection()
|
|---|
| 1983 | {
|
|---|
| [9646] | 1984 | int virtualFrom;
|
|---|
| 1985 | int virtualTo;
|
|---|
| [1904] | 1986 |
|
|---|
| 1987 | if (m_iBufferPos_Tail >= m_iBufferPos)
|
|---|
| 1988 | {
|
|---|
| [9646] | 1989 | virtualFrom = m_iBufferPos;
|
|---|
| 1990 | virtualTo = m_iBufferPos_Tail;
|
|---|
| [1904] | 1991 | }
|
|---|
| 1992 | else
|
|---|
| 1993 | {
|
|---|
| [9646] | 1994 | virtualFrom = m_iBufferPos_Tail;
|
|---|
| 1995 | virtualTo = m_iBufferPos;
|
|---|
| [1904] | 1996 | }
|
|---|
| 1997 |
|
|---|
| [25392] | 1998 | // Silently change.
|
|---|
| 1999 | m_Caption.Set(m_Caption->Left(virtualFrom) + m_Caption->Right(static_cast<long>(m_Caption->length()) - virtualTo),
|
|---|
| 2000 | false);
|
|---|
| [1904] | 2001 |
|
|---|
| [9646] | 2002 | UpdateText(virtualFrom, virtualTo, virtualFrom);
|
|---|
| [1904] | 2003 |
|
|---|
| 2004 | // Remove selection
|
|---|
| 2005 | m_iBufferPos_Tail = -1;
|
|---|
| [9646] | 2006 | m_iBufferPos = virtualFrom;
|
|---|
| [16261] | 2007 | UpdateBufferPositionSetting();
|
|---|
| [1904] | 2008 | }
|
|---|
| 2009 |
|
|---|
| 2010 | bool CInput::SelectingText() const
|
|---|
| 2011 | {
|
|---|
| 2012 | return m_iBufferPos_Tail != -1 &&
|
|---|
| 2013 | m_iBufferPos_Tail != m_iBufferPos;
|
|---|
| 2014 | }
|
|---|
| 2015 |
|
|---|
| 2016 | float CInput::GetTextAreaWidth()
|
|---|
| 2017 | {
|
|---|
| [23005] | 2018 | if (m_ScrollBar && GetScrollBar(0).GetStyle())
|
|---|
| 2019 | return m_CachedActualSize.GetWidth() - m_BufferZone * 2.f - GetScrollBar(0).GetStyle()->m_Width;
|
|---|
| [1904] | 2020 |
|
|---|
| [23005] | 2021 | return m_CachedActualSize.GetWidth() - m_BufferZone * 2.f;
|
|---|
| [1904] | 2022 | }
|
|---|
| 2023 |
|
|---|
| 2024 | void CInput::UpdateAutoScroll()
|
|---|
| 2025 | {
|
|---|
| 2026 | // Autoscrolling up and down
|
|---|
| [23005] | 2027 | if (m_MultiLine)
|
|---|
| [1904] | 2028 | {
|
|---|
| [23005] | 2029 | if (!m_ScrollBar)
|
|---|
| [1904] | 2030 | return;
|
|---|
| 2031 |
|
|---|
| [22765] | 2032 | const float scroll = GetScrollBar(0).GetPos();
|
|---|
| [16244] | 2033 |
|
|---|
| [1904] | 2034 | // Now get the height of the font.
|
|---|
| 2035 | // TODO: Get the real font
|
|---|
| [25392] | 2036 | CFontMetrics font(CStrIntern(m_Font->ToUTF8()));
|
|---|
| [1908] | 2037 | float spacing = (float)font.GetLineSpacing();
|
|---|
| 2038 | //float height = font.GetHeight();
|
|---|
| [1904] | 2039 |
|
|---|
| [6226] | 2040 | // TODO Gee (2004-11-21): Okay, I need a 'std::list' for some reasons, but I would really like to
|
|---|
| [1904] | 2041 | // be able to get the specific element here. This is hopefully a temporary hack.
|
|---|
| 2042 |
|
|---|
| [6226] | 2043 | std::list<SRow>::iterator current = m_CharacterPositions.begin();
|
|---|
| [16931] | 2044 | int row = 0;
|
|---|
| [1904] | 2045 | while (current != m_CharacterPositions.end())
|
|---|
| 2046 | {
|
|---|
| 2047 | if (m_iBufferPos >= current->m_ListStart &&
|
|---|
| [20074] | 2048 | m_iBufferPos <= current->m_ListStart + (int)current->m_ListOfX.size())
|
|---|
| [1904] | 2049 | break;
|
|---|
| [16244] | 2050 |
|
|---|
| [1904] | 2051 | ++current;
|
|---|
| 2052 | ++row;
|
|---|
| 2053 | }
|
|---|
| 2054 |
|
|---|
| 2055 | // If scrolling down
|
|---|
| [23005] | 2056 | if (-scroll + static_cast<float>(row + 1) * spacing + m_BufferZone * 2.f > m_CachedActualSize.GetHeight())
|
|---|
| [1904] | 2057 | {
|
|---|
| [23005] | 2058 | // Scroll so the selected row is shown completely, also with m_BufferZone length to the edge.
|
|---|
| 2059 | GetScrollBar(0).SetPos(static_cast<float>(row + 1) * spacing - m_CachedActualSize.GetHeight() + m_BufferZone * 2.f);
|
|---|
| [1904] | 2060 | }
|
|---|
| 2061 | // If scrolling up
|
|---|
| [16931] | 2062 | else if (-scroll + (float)row * spacing < 0.f)
|
|---|
| [1904] | 2063 | {
|
|---|
| [23005] | 2064 | // Scroll so the selected row is shown completely, also with m_BufferZone length to the edge.
|
|---|
| [1904] | 2065 | GetScrollBar(0).SetPos((float)row * spacing);
|
|---|
| 2066 | }
|
|---|
| 2067 | }
|
|---|
| 2068 | else // autoscrolling left and right
|
|---|
| 2069 | {
|
|---|
| 2070 | // Get X position of position:
|
|---|
| 2071 | if (m_CharacterPositions.empty())
|
|---|
| 2072 | return;
|
|---|
| 2073 |
|
|---|
| 2074 | float x_position = 0.f;
|
|---|
| 2075 | float x_total = 0.f;
|
|---|
| 2076 | if (!m_CharacterPositions.begin()->m_ListOfX.empty())
|
|---|
| 2077 | {
|
|---|
| 2078 |
|
|---|
| 2079 | // Get position of m_iBufferPos
|
|---|
| [1908] | 2080 | if ((int)m_CharacterPositions.begin()->m_ListOfX.size() >= m_iBufferPos &&
|
|---|
| [19579] | 2081 | m_iBufferPos > 0)
|
|---|
| [1904] | 2082 | x_position = m_CharacterPositions.begin()->m_ListOfX[m_iBufferPos-1];
|
|---|
| 2083 |
|
|---|
| 2084 | // Get complete length:
|
|---|
| [16931] | 2085 | x_total = m_CharacterPositions.begin()->m_ListOfX[m_CharacterPositions.begin()->m_ListOfX.size()-1];
|
|---|
| [1904] | 2086 | }
|
|---|
| 2087 |
|
|---|
| 2088 | // Check if outside to the right
|
|---|
| [23005] | 2089 | if (x_position - m_HorizontalScroll + m_BufferZone * 2.f > m_CachedActualSize.GetWidth())
|
|---|
| 2090 | m_HorizontalScroll = x_position - m_CachedActualSize.GetWidth() + m_BufferZone * 2.f;
|
|---|
| [1904] | 2091 |
|
|---|
| 2092 | // Check if outside to the left
|
|---|
| 2093 | if (x_position - m_HorizontalScroll < 0.f)
|
|---|
| 2094 | m_HorizontalScroll = x_position;
|
|---|
| 2095 |
|
|---|
| 2096 | // Check if the text doesn't even fill up to the right edge even though scrolling is done.
|
|---|
| 2097 | if (m_HorizontalScroll != 0.f &&
|
|---|
| [23005] | 2098 | x_total - m_HorizontalScroll + m_BufferZone * 2.f < m_CachedActualSize.GetWidth())
|
|---|
| 2099 | m_HorizontalScroll = x_total - m_CachedActualSize.GetWidth() + m_BufferZone * 2.f;
|
|---|
| [1904] | 2100 |
|
|---|
| 2101 | // Now this is the fail-safe, if x_total isn't even the length of the control,
|
|---|
| 2102 | // remove all scrolling
|
|---|
| [23005] | 2103 | if (x_total + m_BufferZone * 2.f < m_CachedActualSize.GetWidth())
|
|---|
| [1904] | 2104 | m_HorizontalScroll = 0.f;
|
|---|
| 2105 | }
|
|---|
| [1908] | 2106 | }
|
|---|