Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "views/controls/textfield/textfield_view.h" | |
| 6 | |
| 7 namespace views { | |
| 8 | |
| 9 const char TextfieldView::kViewClassName[] = "views/TextfieldView"; | |
| 10 | |
| 11 TextfieldView::TextfieldView() { | |
| 12 Init(L""); | |
| 13 } | |
| 14 | |
| 15 TextfieldView::TextfieldView(const std::wstring& text) { | |
| 16 Init(text); | |
| 17 } | |
| 18 | |
| 19 TextfieldView::TextfieldView(TextfieldModel* model) { | |
| 20 Init(L""); | |
| 21 model_.reset(model); | |
| 22 } | |
| 23 | |
| 24 void TextfieldView::Init(const std::wstring& text) { | |
| 25 controller_.reset(NULL); | |
| 26 selection_on_ = false; | |
| 27 model_.reset(new TextfieldModel(text)); | |
| 28 set_background(Background::CreateSolidBackground(255, 255, 255, 255)); | |
| 29 SetHorizontalAlignment(ALIGN_LEFT); | |
| 30 this->SetFocusable(true); | |
| 31 RequestFocus(); | |
| 32 onscreen_cursor_pos_ = font().GetStringWidth(text); | |
| 33 } | |
| 34 | |
| 35 void TextfieldView::SetController(TextfieldController* controller) { | |
| 36 controller_.reset(controller); | |
| 37 } | |
| 38 | |
| 39 //////////////////////////////////////////////////////////////////////////////// | |
| 40 // View overrides: | |
| 41 | |
| 42 bool TextfieldView::SkipDefaultKeyEventProcessing(const views::KeyEvent& e) { | |
| 43 return true; | |
| 44 } | |
| 45 | |
| 46 // TODO(varunjain): implement mouse and focus events related methods. | |
| 47 bool TextfieldView::OnMousePressed(const views::MouseEvent& e) { | |
| 48 return true; | |
| 49 } | |
| 50 | |
| 51 bool TextfieldView::OnMouseDragged(const views::MouseEvent& e) { | |
| 52 return true; | |
| 53 } | |
| 54 | |
| 55 void TextfieldView::OnMouseReleased(const views::MouseEvent& e, bool canceled) { | |
| 56 RequestFocus(); | |
| 57 } | |
| 58 | |
| 59 bool TextfieldView::OnKeyPressed(const views::KeyEvent& e) { | |
| 60 bool handled = HandleKeyEvent(e); | |
| 61 this->SetText(model_->text()); | |
| 62 SchedulePaint(); | |
| 63 return handled; | |
| 64 } | |
| 65 | |
| 66 bool TextfieldView::OnKeyReleased(const views::KeyEvent& e) { | |
| 67 return true; | |
| 68 } | |
| 69 | |
| 70 void TextfieldView::WillGainFocus() { | |
| 71 } | |
| 72 | |
| 73 void TextfieldView::DidGainFocus() { | |
| 74 } | |
| 75 | |
| 76 void TextfieldView::WillLoseFocus() { | |
| 77 } | |
| 78 | |
| 79 std::wstring TextfieldView::GetText() { | |
| 80 return model_->text(); | |
| 81 } | |
| 82 | |
| 83 gfx::Rect TextfieldView::GetTextBounds() { | |
| 84 gfx::Insets insets = View::GetInsets(); | |
| 85 int text_height = font().GetHeight(); | |
| 86 int text_y = std::max(0, (bounds().height() - text_height)) / 2; | |
| 87 gfx::Rect text_bounds(insets.left(), text_y, | |
| 88 bounds().width() - insets.width(), text_height); | |
| 89 return text_bounds; | |
| 90 } | |
| 91 | |
| 92 void TextfieldView::PaintTextAndCursor(gfx::Canvas* canvas) { | |
| 93 // TODO(varunjain): re-implement this so that only dirty text is painted. | |
| 94 std::wstring str = model_->text(); | |
| 95 gfx::Rect text_bounds = GetTextBounds(); | |
| 96 int onscreen_cursor_pos = onscreen_cursor_pos_ + View::GetInsets().left(); | |
| 97 | |
| 98 // Get string to the left of cursor. | |
| 99 std::wstring left_string = GetStringToLeftOfCursor(str, | |
| 100 model_->GetCurrentCursorPos(), onscreen_cursor_pos, text_bounds); | |
| 101 int left_str_width = font().GetStringWidth(left_string); | |
| 102 | |
| 103 // Get string to the right of cursor. | |
| 104 std::wstring right_string = GetStringToRightOfCursor(str, | |
| 105 model_->GetCurrentCursorPos(), onscreen_cursor_pos, text_bounds); | |
| 106 | |
| 107 // Paint the complete string. | |
| 108 visible_text_ = left_string + right_string; | |
| 109 canvas->DrawStringInt(visible_text_, font(), GetColor(), text_bounds.x(), | |
| 110 text_bounds.y(), text_bounds.width(), text_bounds.height(), 0); | |
| 111 | |
| 112 // Paint Cursor. | |
| 113 if (HasFocus()) { | |
| 114 canvas->DrawLineInt(SK_ColorBLUE, text_bounds.x() + left_str_width, 0, | |
| 115 text_bounds.x() + left_str_width, bounds().height()); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 void TextfieldView::Paint(gfx::Canvas* canvas) { | |
| 120 set_border(HasFocus() ? Border::CreateSolidBorder(2, SK_ColorGRAY) | |
| 121 : Border::CreateSolidBorder(1, SK_ColorBLACK)); | |
| 122 PaintBackground(canvas); | |
| 123 PaintTextAndCursor(canvas); | |
| 124 PaintBorder(canvas); | |
| 125 } | |
| 126 | |
| 127 std::wstring TextfieldView::GetStringToLeftOfCursor(std::wstring text, | |
| 128 int cursor_pos, int onscreen_cursor_pos, gfx::Rect text_bounds) { | |
| 129 std::wstring str = L""; | |
|
tfarina
2010/11/18 11:55:31
I think when you do: std::wstring str; it is alrea
| |
| 130 int available_size = onscreen_cursor_pos - text_bounds.x(); | |
| 131 for (int pos = cursor_pos; pos > 0; pos--) { | |
|
tfarina
2010/11/18 11:55:31
--pos
| |
| 132 if (font().GetStringWidth(str + text[pos - 1]) <= available_size) { | |
| 133 str += text[pos - 1]; | |
| 134 } else { | |
| 135 break; | |
| 136 } | |
| 137 } | |
| 138 for (unsigned i = 0; i < str.length() / 2.0; i++) { | |
|
tfarina
2010/11/18 11:55:31
++i
| |
| 139 wchar_t temp = str[i]; | |
| 140 str[i] = str[str.length() - i - 1]; | |
| 141 str[str.length() - i - 1] = temp; | |
| 142 } | |
| 143 return str; | |
| 144 } | |
| 145 | |
| 146 std::wstring TextfieldView::GetStringToRightOfCursor(std::wstring text, | |
| 147 int cursor_pos, int onscreen_cursor_pos, gfx::Rect text_bounds) { | |
| 148 std::wstring str; | |
| 149 int available_size = text_bounds.x() + text_bounds.width() | |
| 150 - onscreen_cursor_pos; | |
| 151 for (unsigned pos = cursor_pos; pos < text.length(); pos++) { | |
|
tfarina
2010/11/18 11:55:31
++pos
| |
| 152 if (font().GetStringWidth(str + text[pos]) <= available_size) { | |
| 153 str += text[pos]; | |
| 154 } else { | |
| 155 break; | |
| 156 } | |
| 157 } | |
| 158 return str; | |
| 159 } | |
| 160 | |
| 161 bool TextfieldView::HandleKeyEvent(const KeyEvent& key_event) { | |
| 162 int current_cursor_pos = model_->GetCurrentCursorPos(); | |
| 163 std::wstring removed_str = L""; | |
| 164 bool need_cursor_repositioning = true; | |
| 165 bool text_changed = false; | |
| 166 if (key_event.GetType() == views::Event::ET_KEY_PRESSED) { | |
| 167 app::KeyboardCode key_code = key_event.GetKeyCode(); | |
| 168 if (key_code == app::VKEY_TAB) { | |
| 169 return false; | |
| 170 } | |
| 171 switch (key_code) { | |
| 172 case app::VKEY_RIGHT: | |
| 173 key_event.IsControlDown() ? model_->MoveCursorToNextWord() | |
| 174 : model_->MoveCursorRight(); | |
| 175 break; | |
| 176 case app::VKEY_LEFT: | |
| 177 key_event.IsControlDown() ? model_->MoveCursorToPreviousWord() | |
| 178 : model_->MoveCursorLeft(); | |
| 179 break; | |
| 180 case app::VKEY_END: | |
| 181 model_->MoveCursorToEnd(); | |
| 182 break; | |
| 183 case app::VKEY_HOME: | |
| 184 model_->MoveCursorToStart(); | |
| 185 break; | |
| 186 case app::VKEY_BACK: | |
| 187 removed_str += model_->InsertBackspace(); | |
| 188 if (font().GetStringWidth(model_->text()) | |
| 189 <= bounds().width() - View::GetInsets().width()) { | |
| 190 SafeDecCursorPos(font().GetStringWidth(removed_str)); | |
| 191 } | |
| 192 need_cursor_repositioning = false; | |
| 193 break; | |
| 194 case app::VKEY_DELETE: | |
| 195 removed_str += model_->InsertDelete(); | |
| 196 default: | |
| 197 break; | |
| 198 } | |
| 199 if (IsWritable(key_code)) { | |
| 200 model_->Insert(GetWritableChar(key_event)); | |
| 201 text_changed = true; | |
| 202 } | |
| 203 if (key_event.IsShiftDown() && !selection_on_ && | |
| 204 IsDirectionalKey(key_code)) { | |
| 205 selection_on_ = true; | |
| 206 selection_start_ = model_->GetCurrentCursorPos(); | |
| 207 } | |
| 208 if (removed_str.length() > 0) { | |
| 209 text_changed = true; | |
| 210 } | |
| 211 | |
| 212 // Move the onscreen cursor to new position if required. | |
| 213 if (need_cursor_repositioning) { | |
| 214 std::wstring text = model_->text(); | |
| 215 int new_cursor_pos = model_->GetCurrentCursorPos(); | |
| 216 std::wstring str_diff; | |
| 217 if (new_cursor_pos > current_cursor_pos) { | |
| 218 for (int i = current_cursor_pos; i < new_cursor_pos; i++) { | |
| 219 str_diff += text[i]; | |
| 220 } | |
| 221 SafeIncCursorPos(font().GetStringWidth(str_diff)); | |
| 222 } else if (new_cursor_pos < current_cursor_pos) { | |
| 223 for (int i = new_cursor_pos; i < current_cursor_pos; i++) { | |
| 224 str_diff += text[i]; | |
| 225 } | |
| 226 SafeDecCursorPos(font().GetStringWidth(str_diff)); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 // Send notification to the controller if text has changed. | |
| 231 if (controller_.get() && text_changed) { | |
| 232 controller_->TextChanged(model_->text()); | |
| 233 } | |
| 234 } | |
| 235 return true; | |
| 236 } | |
| 237 | |
| 238 void TextfieldView::SafeIncCursorPos(int inc) { | |
| 239 if (onscreen_cursor_pos_ + inc | |
| 240 < bounds().width() - View::GetInsets().width()) { | |
|
tfarina
2010/11/18 11:55:31
put this < in the line above?
| |
| 241 onscreen_cursor_pos_ += inc; | |
| 242 } else { | |
| 243 onscreen_cursor_pos_ = bounds().width() - View::GetInsets().width(); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 void TextfieldView::SafeDecCursorPos(int dec) { | |
| 248 if (onscreen_cursor_pos_ - dec > 0) { | |
| 249 onscreen_cursor_pos_ -= dec; | |
| 250 } else { | |
| 251 onscreen_cursor_pos_ = 0; | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 bool TextfieldView::IsDirectionalKey(app::KeyboardCode key_code) { | |
| 256 switch (key_code) { | |
| 257 case app::VKEY_RIGHT: | |
| 258 case app::VKEY_LEFT: | |
| 259 case app::VKEY_END: | |
| 260 case app::VKEY_HOME: | |
| 261 return true; | |
| 262 break; | |
|
tfarina
2010/11/18 11:55:31
I think the break is not necessary here, it won't
| |
| 263 default: | |
| 264 return false; | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 bool TextfieldView::IsWritable(app::KeyboardCode key_code) { | |
| 269 return (key_code >= app::VKEY_0 && key_code <= app::VKEY_Z) | |
| 270 || (key_code >= app::VKEY_NUMPAD0 && key_code <= app::VKEY_DIVIDE) | |
|
tfarina
2010/11/18 11:55:31
Please, fix all the occurrences in this file. This
| |
| 271 || (key_code >= app::VKEY_OEM_1 && key_code <= app::VKEY_OEM_8) | |
| 272 || key_code == app::VKEY_SPACE; | |
| 273 } | |
| 274 | |
| 275 wchar_t TextfieldView::GetWritableChar(const KeyEvent& key_event) { | |
| 276 app::KeyboardCode key_code = key_event.GetKeyCode(); | |
| 277 bool shift = false; | |
| 278 if ((key_code >= app::VKEY_0 && key_code <= app::VKEY_9) | |
| 279 || (key_code >= app::VKEY_OEM_1 && key_code <= app::VKEY_OEM_8)) { | |
| 280 shift = key_event.IsShiftDown(); | |
| 281 } else if (key_code >= app::VKEY_A && key_code <= app::VKEY_Z) { | |
| 282 shift = (key_event.IsLockDown() && !key_event.IsShiftDown()) | |
| 283 || (!key_event.IsLockDown() && key_event.IsShiftDown()); | |
| 284 } | |
| 285 return GetWritableChar(key_code, shift); | |
| 286 } | |
| 287 | |
| 288 wchar_t TextfieldView::GetWritableChar(app::KeyboardCode key_code, | |
| 289 bool shift) { | |
|
tfarina
2010/11/18 11:55:31
You can align shift with app::KeyboardCode. Also p
| |
| 290 switch (key_code) { | |
| 291 case app::VKEY_NUMPAD0: | |
| 292 return '0'; | |
| 293 case app::VKEY_NUMPAD1: | |
| 294 return '1'; | |
| 295 case app::VKEY_NUMPAD2: | |
| 296 return '2'; | |
| 297 case app::VKEY_NUMPAD3: | |
| 298 return '3'; | |
| 299 case app::VKEY_NUMPAD4: | |
| 300 return '4'; | |
| 301 case app::VKEY_NUMPAD5: | |
| 302 return '5'; | |
| 303 case app::VKEY_NUMPAD6: | |
| 304 return '6'; | |
| 305 case app::VKEY_NUMPAD7: | |
| 306 return '7'; | |
| 307 case app::VKEY_NUMPAD8: | |
| 308 return '8'; | |
| 309 case app::VKEY_NUMPAD9: | |
| 310 return '9'; | |
| 311 case app::VKEY_MULTIPLY: | |
| 312 return '*'; | |
| 313 case app::VKEY_ADD: | |
| 314 return '+'; | |
| 315 case app::VKEY_SUBTRACT: | |
| 316 return '-'; | |
| 317 case app::VKEY_DECIMAL: | |
| 318 return '.'; | |
| 319 case app::VKEY_DIVIDE: | |
| 320 return '/'; | |
| 321 | |
| 322 // case app::VKEY_BACK: | |
|
tfarina
2010/11/18 11:55:31
Why this big block of comment? If it isn't used. P
| |
| 323 // return GDK_BackSpace; | |
| 324 // case app::VKEY_TAB: | |
| 325 // return shift ? GDK_ISO_Left_Tab : GDK_Tab; | |
| 326 // case app::VKEY_CLEAR: | |
| 327 // return GDK_Clear; | |
| 328 // case app::VKEY_RETURN: | |
| 329 // return GDK_Return; | |
| 330 // case app::VKEY_SHIFT: | |
| 331 // return GDK_Shift_L; | |
| 332 // case app::VKEY_CONTROL: | |
| 333 // return GDK_Control_L; | |
| 334 // case app::VKEY_MENU: | |
| 335 // return GDK_Alt_L; | |
| 336 // case app::VKEY_APPS: | |
| 337 // return GDK_Menu; | |
| 338 // | |
| 339 // case app::VKEY_PAUSE: | |
| 340 // return GDK_Pause; | |
| 341 // case app::VKEY_CAPITAL: | |
| 342 // return GDK_Caps_Lock; | |
| 343 // case app::VKEY_KANA: | |
| 344 // return GDK_Kana_Lock; | |
| 345 // case app::VKEY_HANJA: | |
| 346 // return GDK_Hangul_Hanja; | |
| 347 // case app::VKEY_ESCAPE: | |
| 348 // return GDK_Escape; | |
| 349 case app::VKEY_SPACE: | |
| 350 return ' '; | |
| 351 // case app::VKEY_PRIOR: | |
| 352 // return GDK_Page_Up; | |
| 353 // case app::VKEY_NEXT: | |
| 354 // return GDK_Page_Down; | |
| 355 // case app::VKEY_END: | |
| 356 // return GDK_End; | |
| 357 // case app::VKEY_HOME: | |
| 358 // return GDK_Home; | |
| 359 // case app::VKEY_LEFT: | |
| 360 // return GDK_Left; | |
| 361 // case app::VKEY_UP: | |
| 362 // return GDK_Up; | |
| 363 // case app::VKEY_RIGHT: | |
| 364 // return GDK_Right; | |
| 365 // case app::VKEY_DOWN: | |
| 366 // return GDK_Down; | |
| 367 // case app::VKEY_SELECT: | |
| 368 // return GDK_Select; | |
| 369 // case app::VKEY_PRINT: | |
| 370 // return GDK_Print; | |
| 371 // case app::VKEY_EXECUTE: | |
| 372 // return GDK_Execute; | |
| 373 // case app::VKEY_INSERT: | |
| 374 // return GDK_Insert; | |
| 375 // case app::VKEY_DELETE: | |
| 376 // return GDK_Delete; | |
| 377 // case app::VKEY_HELP: | |
| 378 // return GDK_Help; | |
| 379 case app::VKEY_0: | |
| 380 return shift ? ')' : '0'; | |
| 381 case app::VKEY_1: | |
| 382 return shift ? '!' : '1'; | |
| 383 case app::VKEY_2: | |
| 384 return shift ? '@' : '2'; | |
| 385 case app::VKEY_3: | |
| 386 return shift ? '#' : '3'; | |
| 387 case app::VKEY_4: | |
| 388 return shift ? '$' : '4'; | |
| 389 case app::VKEY_5: | |
| 390 return shift ? '%' : '5'; | |
| 391 case app::VKEY_6: | |
| 392 return shift ? '^' : '6'; | |
| 393 case app::VKEY_7: | |
| 394 return shift ? '&' : '7'; | |
| 395 case app::VKEY_8: | |
| 396 return shift ? '*' : '8'; | |
| 397 case app::VKEY_9: | |
| 398 return shift ? '(' : '9'; | |
| 399 | |
| 400 case app::VKEY_A: | |
| 401 case app::VKEY_B: | |
| 402 case app::VKEY_C: | |
| 403 case app::VKEY_D: | |
| 404 case app::VKEY_E: | |
| 405 case app::VKEY_F: | |
| 406 case app::VKEY_G: | |
| 407 case app::VKEY_H: | |
| 408 case app::VKEY_I: | |
| 409 case app::VKEY_J: | |
| 410 case app::VKEY_K: | |
| 411 case app::VKEY_L: | |
| 412 case app::VKEY_M: | |
| 413 case app::VKEY_N: | |
| 414 case app::VKEY_O: | |
| 415 case app::VKEY_P: | |
| 416 case app::VKEY_Q: | |
| 417 case app::VKEY_R: | |
| 418 case app::VKEY_S: | |
| 419 case app::VKEY_T: | |
| 420 case app::VKEY_U: | |
| 421 case app::VKEY_V: | |
| 422 case app::VKEY_W: | |
| 423 case app::VKEY_X: | |
| 424 case app::VKEY_Y: | |
| 425 case app::VKEY_Z: | |
| 426 return (shift ? 'A' : 'a') + (key_code - app::VKEY_A); | |
| 427 | |
| 428 // case app::VKEY_LWIN: | |
| 429 // return GDK_Meta_L; | |
| 430 // case app::VKEY_RWIN: | |
| 431 // return GDK_Meta_R; | |
| 432 // | |
| 433 // case app::VKEY_NUMLOCK: | |
| 434 // return GDK_Num_Lock; | |
| 435 // | |
| 436 // case app::VKEY_SCROLL: | |
| 437 // return GDK_Scroll_Lock; | |
| 438 // | |
| 439 case app::VKEY_OEM_1: | |
| 440 return shift ? ':' : ';'; | |
| 441 case app::VKEY_OEM_PLUS: | |
| 442 return shift ? '+' : '='; | |
| 443 case app::VKEY_OEM_COMMA: | |
| 444 return shift ? '<' : ','; | |
| 445 case app::VKEY_OEM_MINUS: | |
| 446 return shift ? '_' : '-'; | |
| 447 case app::VKEY_OEM_PERIOD: | |
| 448 return shift ? '>' : '.'; | |
| 449 case app::VKEY_OEM_2: | |
| 450 return shift ? '?' : '/'; | |
| 451 case app::VKEY_OEM_3: | |
| 452 return shift ? '~' : '`'; | |
| 453 case app::VKEY_OEM_4: | |
| 454 return shift ? '}' : ']'; | |
| 455 case app::VKEY_OEM_5: | |
| 456 return shift ? '|' : '\\'; | |
| 457 case app::VKEY_OEM_6: | |
| 458 return shift ? '{' : '['; | |
| 459 case app::VKEY_OEM_7: | |
| 460 return shift ? '"' : '\''; | |
| 461 // | |
| 462 // case app::VKEY_F1: | |
| 463 // case app::VKEY_F2: | |
| 464 // case app::VKEY_F3: | |
| 465 // case app::VKEY_F4: | |
| 466 // case app::VKEY_F5: | |
| 467 // case app::VKEY_F6: | |
| 468 // case app::VKEY_F7: | |
| 469 // case app::VKEY_F8: | |
| 470 // case app::VKEY_F9: | |
| 471 // case app::VKEY_F10: | |
| 472 // case app::VKEY_F11: | |
| 473 // case app::VKEY_F12: | |
| 474 // case app::VKEY_F13: | |
| 475 // case app::VKEY_F14: | |
| 476 // case app::VKEY_F15: | |
| 477 // case app::VKEY_F16: | |
| 478 // case app::VKEY_F17: | |
| 479 // case app::VKEY_F18: | |
| 480 // case app::VKEY_F19: | |
| 481 // case app::VKEY_F20: | |
| 482 // case app::VKEY_F21: | |
| 483 // case app::VKEY_F22: | |
| 484 // case app::VKEY_F23: | |
| 485 // case app::VKEY_F24: | |
| 486 // return GDK_F1 + (keycode - app::VKEY_F1); | |
| 487 | |
| 488 default: | |
| 489 return 0; | |
| 490 } | |
| 491 } | |
| 492 } | |
| OLD | NEW |