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""; |
| 130 int available_size = onscreen_cursor_pos - text_bounds.x(); |
| 131 for (int pos = cursor_pos; pos > 0; 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++) { |
| 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++) { |
| 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 base::KeyboardCode key_code = key_event.GetKeyCode(); |
| 168 if (key_code == base::VKEY_TAB) { |
| 169 return false; |
| 170 } |
| 171 switch (key_code) { |
| 172 case base::VKEY_RIGHT: |
| 173 key_event.IsControlDown() ? model_->MoveCursorToNextWord() |
| 174 : model_->MoveCursorRight(); |
| 175 break; |
| 176 case base::VKEY_LEFT: |
| 177 key_event.IsControlDown() ? model_->MoveCursorToPreviousWord() |
| 178 : model_->MoveCursorLeft(); |
| 179 break; |
| 180 case base::VKEY_END: |
| 181 model_->MoveCursorToEnd(); |
| 182 break; |
| 183 case base::VKEY_HOME: |
| 184 model_->MoveCursorToStart(); |
| 185 break; |
| 186 case base::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 base::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()) { |
| 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(base::KeyboardCode key_code) { |
| 256 switch (key_code) { |
| 257 case base::VKEY_RIGHT: |
| 258 case base::VKEY_LEFT: |
| 259 case base::VKEY_END: |
| 260 case base::VKEY_HOME: |
| 261 return true; |
| 262 break; |
| 263 default: |
| 264 return false; |
| 265 } |
| 266 } |
| 267 |
| 268 bool TextfieldView::IsWritable(base::KeyboardCode key_code) { |
| 269 return (key_code >= base::VKEY_0 && key_code <= base::VKEY_Z) |
| 270 || (key_code >= base::VKEY_NUMPAD0 && key_code <= base::VKEY_DIVIDE) |
| 271 || (key_code >= base::VKEY_OEM_1 && key_code <= base::VKEY_OEM_8) |
| 272 || key_code == base::VKEY_SPACE; |
| 273 } |
| 274 |
| 275 wchar_t TextfieldView::GetWritableChar(const KeyEvent& key_event) { |
| 276 base::KeyboardCode key_code = key_event.GetKeyCode(); |
| 277 bool shift = false; |
| 278 if ((key_code >= base::VKEY_0 && key_code <= base::VKEY_9) |
| 279 || (key_code >= base::VKEY_OEM_1 && key_code <= base::VKEY_OEM_8)) { |
| 280 shift = key_event.IsShiftDown(); |
| 281 } else if (key_code >= base::VKEY_A && key_code <= base::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(base::KeyboardCode key_code, |
| 289 bool shift) { |
| 290 switch (key_code) { |
| 291 case base::VKEY_NUMPAD0: |
| 292 return '0'; |
| 293 case base::VKEY_NUMPAD1: |
| 294 return '1'; |
| 295 case base::VKEY_NUMPAD2: |
| 296 return '2'; |
| 297 case base::VKEY_NUMPAD3: |
| 298 return '3'; |
| 299 case base::VKEY_NUMPAD4: |
| 300 return '4'; |
| 301 case base::VKEY_NUMPAD5: |
| 302 return '5'; |
| 303 case base::VKEY_NUMPAD6: |
| 304 return '6'; |
| 305 case base::VKEY_NUMPAD7: |
| 306 return '7'; |
| 307 case base::VKEY_NUMPAD8: |
| 308 return '8'; |
| 309 case base::VKEY_NUMPAD9: |
| 310 return '9'; |
| 311 case base::VKEY_MULTIPLY: |
| 312 return '*'; |
| 313 case base::VKEY_ADD: |
| 314 return '+'; |
| 315 case base::VKEY_SUBTRACT: |
| 316 return '-'; |
| 317 case base::VKEY_DECIMAL: |
| 318 return '.'; |
| 319 case base::VKEY_DIVIDE: |
| 320 return '/'; |
| 321 |
| 322 // case base::VKEY_BACK: |
| 323 // return GDK_BackSpace; |
| 324 // case base::VKEY_TAB: |
| 325 // return shift ? GDK_ISO_Left_Tab : GDK_Tab; |
| 326 // case base::VKEY_CLEAR: |
| 327 // return GDK_Clear; |
| 328 // case base::VKEY_RETURN: |
| 329 // return GDK_Return; |
| 330 // case base::VKEY_SHIFT: |
| 331 // return GDK_Shift_L; |
| 332 // case base::VKEY_CONTROL: |
| 333 // return GDK_Control_L; |
| 334 // case base::VKEY_MENU: |
| 335 // return GDK_Alt_L; |
| 336 // case base::VKEY_APPS: |
| 337 // return GDK_Menu; |
| 338 // |
| 339 // case base::VKEY_PAUSE: |
| 340 // return GDK_Pause; |
| 341 // case base::VKEY_CAPITAL: |
| 342 // return GDK_Caps_Lock; |
| 343 // case base::VKEY_KANA: |
| 344 // return GDK_Kana_Lock; |
| 345 // case base::VKEY_HANJA: |
| 346 // return GDK_Hangul_Hanja; |
| 347 // case base::VKEY_ESCAPE: |
| 348 // return GDK_Escape; |
| 349 case base::VKEY_SPACE: |
| 350 return ' '; |
| 351 // case base::VKEY_PRIOR: |
| 352 // return GDK_Page_Up; |
| 353 // case base::VKEY_NEXT: |
| 354 // return GDK_Page_Down; |
| 355 // case base::VKEY_END: |
| 356 // return GDK_End; |
| 357 // case base::VKEY_HOME: |
| 358 // return GDK_Home; |
| 359 // case base::VKEY_LEFT: |
| 360 // return GDK_Left; |
| 361 // case base::VKEY_UP: |
| 362 // return GDK_Up; |
| 363 // case base::VKEY_RIGHT: |
| 364 // return GDK_Right; |
| 365 // case base::VKEY_DOWN: |
| 366 // return GDK_Down; |
| 367 // case base::VKEY_SELECT: |
| 368 // return GDK_Select; |
| 369 // case base::VKEY_PRINT: |
| 370 // return GDK_Print; |
| 371 // case base::VKEY_EXECUTE: |
| 372 // return GDK_Execute; |
| 373 // case base::VKEY_INSERT: |
| 374 // return GDK_Insert; |
| 375 // case base::VKEY_DELETE: |
| 376 // return GDK_Delete; |
| 377 // case base::VKEY_HELP: |
| 378 // return GDK_Help; |
| 379 case base::VKEY_0: |
| 380 return shift ? ')' : '0'; |
| 381 case base::VKEY_1: |
| 382 return shift ? '!' : '1'; |
| 383 case base::VKEY_2: |
| 384 return shift ? '@' : '2'; |
| 385 case base::VKEY_3: |
| 386 return shift ? '#' : '3'; |
| 387 case base::VKEY_4: |
| 388 return shift ? '$' : '4'; |
| 389 case base::VKEY_5: |
| 390 return shift ? '%' : '5'; |
| 391 case base::VKEY_6: |
| 392 return shift ? '^' : '6'; |
| 393 case base::VKEY_7: |
| 394 return shift ? '&' : '7'; |
| 395 case base::VKEY_8: |
| 396 return shift ? '*' : '8'; |
| 397 case base::VKEY_9: |
| 398 return shift ? '(' : '9'; |
| 399 |
| 400 case base::VKEY_A: |
| 401 case base::VKEY_B: |
| 402 case base::VKEY_C: |
| 403 case base::VKEY_D: |
| 404 case base::VKEY_E: |
| 405 case base::VKEY_F: |
| 406 case base::VKEY_G: |
| 407 case base::VKEY_H: |
| 408 case base::VKEY_I: |
| 409 case base::VKEY_J: |
| 410 case base::VKEY_K: |
| 411 case base::VKEY_L: |
| 412 case base::VKEY_M: |
| 413 case base::VKEY_N: |
| 414 case base::VKEY_O: |
| 415 case base::VKEY_P: |
| 416 case base::VKEY_Q: |
| 417 case base::VKEY_R: |
| 418 case base::VKEY_S: |
| 419 case base::VKEY_T: |
| 420 case base::VKEY_U: |
| 421 case base::VKEY_V: |
| 422 case base::VKEY_W: |
| 423 case base::VKEY_X: |
| 424 case base::VKEY_Y: |
| 425 case base::VKEY_Z: |
| 426 return (shift ? 'A' : 'a') + (key_code - base::VKEY_A); |
| 427 |
| 428 // case base::VKEY_LWIN: |
| 429 // return GDK_Meta_L; |
| 430 // case base::VKEY_RWIN: |
| 431 // return GDK_Meta_R; |
| 432 // |
| 433 // case base::VKEY_NUMLOCK: |
| 434 // return GDK_Num_Lock; |
| 435 // |
| 436 // case base::VKEY_SCROLL: |
| 437 // return GDK_Scroll_Lock; |
| 438 // |
| 439 case base::VKEY_OEM_1: |
| 440 return shift ? ':' : ';'; |
| 441 case base::VKEY_OEM_PLUS: |
| 442 return shift ? '+' : '='; |
| 443 case base::VKEY_OEM_COMMA: |
| 444 return shift ? '<' : ','; |
| 445 case base::VKEY_OEM_MINUS: |
| 446 return shift ? '_' : '-'; |
| 447 case base::VKEY_OEM_PERIOD: |
| 448 return shift ? '>' : '.'; |
| 449 case base::VKEY_OEM_2: |
| 450 return shift ? '?' : '/'; |
| 451 case base::VKEY_OEM_3: |
| 452 return shift ? '~' : '`'; |
| 453 case base::VKEY_OEM_4: |
| 454 return shift ? '}' : ']'; |
| 455 case base::VKEY_OEM_5: |
| 456 return shift ? '|' : '\\'; |
| 457 case base::VKEY_OEM_6: |
| 458 return shift ? '{' : '['; |
| 459 case base::VKEY_OEM_7: |
| 460 return shift ? '"' : '\''; |
| 461 // |
| 462 // case base::VKEY_F1: |
| 463 // case base::VKEY_F2: |
| 464 // case base::VKEY_F3: |
| 465 // case base::VKEY_F4: |
| 466 // case base::VKEY_F5: |
| 467 // case base::VKEY_F6: |
| 468 // case base::VKEY_F7: |
| 469 // case base::VKEY_F8: |
| 470 // case base::VKEY_F9: |
| 471 // case base::VKEY_F10: |
| 472 // case base::VKEY_F11: |
| 473 // case base::VKEY_F12: |
| 474 // case base::VKEY_F13: |
| 475 // case base::VKEY_F14: |
| 476 // case base::VKEY_F15: |
| 477 // case base::VKEY_F16: |
| 478 // case base::VKEY_F17: |
| 479 // case base::VKEY_F18: |
| 480 // case base::VKEY_F19: |
| 481 // case base::VKEY_F20: |
| 482 // case base::VKEY_F21: |
| 483 // case base::VKEY_F22: |
| 484 // case base::VKEY_F23: |
| 485 // case base::VKEY_F24: |
| 486 // return GDK_F1 + (keycode - base::VKEY_F1); |
| 487 |
| 488 default: |
| 489 return 0; |
| 490 } |
| 491 } |
| 492 } |
OLD | NEW |