Chromium Code Reviews| Index: views/controls/textfield/textfield_view.cc |
| diff --git a/views/controls/textfield/textfield_view.cc b/views/controls/textfield/textfield_view.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7c578f6d532516d85710a55b08db2c4626b95739 |
| --- /dev/null |
| +++ b/views/controls/textfield/textfield_view.cc |
| @@ -0,0 +1,492 @@ |
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "views/controls/textfield/textfield_view.h" |
| + |
| +namespace views { |
| + |
| +const char TextfieldView::kViewClassName[] = "views/TextfieldView"; |
| + |
| +TextfieldView::TextfieldView() { |
| + Init(L""); |
| +} |
| + |
| +TextfieldView::TextfieldView(const std::wstring& text) { |
| + Init(text); |
| +} |
| + |
| +TextfieldView::TextfieldView(TextfieldModel* model) { |
| + Init(L""); |
| + model_.reset(model); |
| +} |
| + |
| +void TextfieldView::Init(const std::wstring& text) { |
| + controller_.reset(NULL); |
| + selection_on_ = false; |
| + model_.reset(new TextfieldModel(text)); |
| + set_background(Background::CreateSolidBackground(255, 255, 255, 255)); |
| + SetHorizontalAlignment(ALIGN_LEFT); |
| + this->SetFocusable(true); |
| + RequestFocus(); |
| + onscreen_cursor_pos_ = font().GetStringWidth(text); |
| +} |
| + |
| +void TextfieldView::SetController(TextfieldController* controller) { |
| + controller_.reset(controller); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// View overrides: |
| + |
| +bool TextfieldView::SkipDefaultKeyEventProcessing(const views::KeyEvent& e) { |
| + return true; |
| +} |
| + |
| +// TODO(varunjain): implement mouse and focus events related methods. |
| +bool TextfieldView::OnMousePressed(const views::MouseEvent& e) { |
| + return true; |
| +} |
| + |
| +bool TextfieldView::OnMouseDragged(const views::MouseEvent& e) { |
| + return true; |
| +} |
| + |
| +void TextfieldView::OnMouseReleased(const views::MouseEvent& e, bool canceled) { |
| + RequestFocus(); |
| +} |
| + |
| +bool TextfieldView::OnKeyPressed(const views::KeyEvent& e) { |
| + bool handled = HandleKeyEvent(e); |
| + this->SetText(model_->text()); |
| + SchedulePaint(); |
| + return handled; |
| +} |
| + |
| +bool TextfieldView::OnKeyReleased(const views::KeyEvent& e) { |
| + return true; |
| +} |
| + |
| +void TextfieldView::WillGainFocus() { |
| +} |
| + |
| +void TextfieldView::DidGainFocus() { |
| +} |
| + |
| +void TextfieldView::WillLoseFocus() { |
| +} |
| + |
| +std::wstring TextfieldView::GetText() { |
| + return model_->text(); |
| +} |
| + |
| +gfx::Rect TextfieldView::GetTextBounds() { |
| + gfx::Insets insets = View::GetInsets(); |
| + int text_height = font().GetHeight(); |
| + int text_y = std::max(0, (bounds().height() - text_height)) / 2; |
| + gfx::Rect text_bounds(insets.left(), text_y, |
| + bounds().width() - insets.width(), text_height); |
| + return text_bounds; |
| +} |
| + |
| +void TextfieldView::PaintTextAndCursor(gfx::Canvas* canvas) { |
| + // TODO(varunjain): re-implement this so that only dirty text is painted. |
| + std::wstring str = model_->text(); |
| + gfx::Rect text_bounds = GetTextBounds(); |
| + int onscreen_cursor_pos = onscreen_cursor_pos_ + View::GetInsets().left(); |
| + |
| + // Get string to the left of cursor. |
| + std::wstring left_string = GetStringToLeftOfCursor(str, |
| + model_->GetCurrentCursorPos(), onscreen_cursor_pos, text_bounds); |
| + int left_str_width = font().GetStringWidth(left_string); |
| + |
| + // Get string to the right of cursor. |
| + std::wstring right_string = GetStringToRightOfCursor(str, |
| + model_->GetCurrentCursorPos(), onscreen_cursor_pos, text_bounds); |
| + |
| + // Paint the complete string. |
| + visible_text_ = left_string + right_string; |
| + canvas->DrawStringInt(visible_text_, font(), GetColor(), text_bounds.x(), |
| + text_bounds.y(), text_bounds.width(), text_bounds.height(), 0); |
| + |
| + // Paint Cursor. |
| + if (HasFocus()) { |
| + canvas->DrawLineInt(SK_ColorBLUE, text_bounds.x() + left_str_width, 0, |
| + text_bounds.x() + left_str_width, bounds().height()); |
| + } |
| +} |
| + |
| +void TextfieldView::Paint(gfx::Canvas* canvas) { |
| + set_border(HasFocus() ? Border::CreateSolidBorder(2, SK_ColorGRAY) |
| + : Border::CreateSolidBorder(1, SK_ColorBLACK)); |
| + PaintBackground(canvas); |
| + PaintTextAndCursor(canvas); |
| + PaintBorder(canvas); |
| +} |
| + |
| +std::wstring TextfieldView::GetStringToLeftOfCursor(std::wstring text, |
| + int cursor_pos, int onscreen_cursor_pos, gfx::Rect text_bounds) { |
| + std::wstring str = L""; |
|
tfarina
2010/11/18 11:55:31
I think when you do: std::wstring str; it is alrea
|
| + int available_size = onscreen_cursor_pos - text_bounds.x(); |
| + for (int pos = cursor_pos; pos > 0; pos--) { |
|
tfarina
2010/11/18 11:55:31
--pos
|
| + if (font().GetStringWidth(str + text[pos - 1]) <= available_size) { |
| + str += text[pos - 1]; |
| + } else { |
| + break; |
| + } |
| + } |
| + for (unsigned i = 0; i < str.length() / 2.0; i++) { |
|
tfarina
2010/11/18 11:55:31
++i
|
| + wchar_t temp = str[i]; |
| + str[i] = str[str.length() - i - 1]; |
| + str[str.length() - i - 1] = temp; |
| + } |
| + return str; |
| +} |
| + |
| +std::wstring TextfieldView::GetStringToRightOfCursor(std::wstring text, |
| + int cursor_pos, int onscreen_cursor_pos, gfx::Rect text_bounds) { |
| + std::wstring str; |
| + int available_size = text_bounds.x() + text_bounds.width() |
| + - onscreen_cursor_pos; |
| + for (unsigned pos = cursor_pos; pos < text.length(); pos++) { |
|
tfarina
2010/11/18 11:55:31
++pos
|
| + if (font().GetStringWidth(str + text[pos]) <= available_size) { |
| + str += text[pos]; |
| + } else { |
| + break; |
| + } |
| + } |
| + return str; |
| +} |
| + |
| +bool TextfieldView::HandleKeyEvent(const KeyEvent& key_event) { |
| + int current_cursor_pos = model_->GetCurrentCursorPos(); |
| + std::wstring removed_str = L""; |
| + bool need_cursor_repositioning = true; |
| + bool text_changed = false; |
| + if (key_event.GetType() == views::Event::ET_KEY_PRESSED) { |
| + app::KeyboardCode key_code = key_event.GetKeyCode(); |
| + if (key_code == app::VKEY_TAB) { |
| + return false; |
| + } |
| + switch (key_code) { |
| + case app::VKEY_RIGHT: |
| + key_event.IsControlDown() ? model_->MoveCursorToNextWord() |
| + : model_->MoveCursorRight(); |
| + break; |
| + case app::VKEY_LEFT: |
| + key_event.IsControlDown() ? model_->MoveCursorToPreviousWord() |
| + : model_->MoveCursorLeft(); |
| + break; |
| + case app::VKEY_END: |
| + model_->MoveCursorToEnd(); |
| + break; |
| + case app::VKEY_HOME: |
| + model_->MoveCursorToStart(); |
| + break; |
| + case app::VKEY_BACK: |
| + removed_str += model_->InsertBackspace(); |
| + if (font().GetStringWidth(model_->text()) |
| + <= bounds().width() - View::GetInsets().width()) { |
| + SafeDecCursorPos(font().GetStringWidth(removed_str)); |
| + } |
| + need_cursor_repositioning = false; |
| + break; |
| + case app::VKEY_DELETE: |
| + removed_str += model_->InsertDelete(); |
| + default: |
| + break; |
| + } |
| + if (IsWritable(key_code)) { |
| + model_->Insert(GetWritableChar(key_event)); |
| + text_changed = true; |
| + } |
| + if (key_event.IsShiftDown() && !selection_on_ && |
| + IsDirectionalKey(key_code)) { |
| + selection_on_ = true; |
| + selection_start_ = model_->GetCurrentCursorPos(); |
| + } |
| + if (removed_str.length() > 0) { |
| + text_changed = true; |
| + } |
| + |
| + // Move the onscreen cursor to new position if required. |
| + if (need_cursor_repositioning) { |
| + std::wstring text = model_->text(); |
| + int new_cursor_pos = model_->GetCurrentCursorPos(); |
| + std::wstring str_diff; |
| + if (new_cursor_pos > current_cursor_pos) { |
| + for (int i = current_cursor_pos; i < new_cursor_pos; i++) { |
| + str_diff += text[i]; |
| + } |
| + SafeIncCursorPos(font().GetStringWidth(str_diff)); |
| + } else if (new_cursor_pos < current_cursor_pos) { |
| + for (int i = new_cursor_pos; i < current_cursor_pos; i++) { |
| + str_diff += text[i]; |
| + } |
| + SafeDecCursorPos(font().GetStringWidth(str_diff)); |
| + } |
| + } |
| + |
| + // Send notification to the controller if text has changed. |
| + if (controller_.get() && text_changed) { |
| + controller_->TextChanged(model_->text()); |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +void TextfieldView::SafeIncCursorPos(int inc) { |
| + if (onscreen_cursor_pos_ + inc |
| + < bounds().width() - View::GetInsets().width()) { |
|
tfarina
2010/11/18 11:55:31
put this < in the line above?
|
| + onscreen_cursor_pos_ += inc; |
| + } else { |
| + onscreen_cursor_pos_ = bounds().width() - View::GetInsets().width(); |
| + } |
| +} |
| + |
| +void TextfieldView::SafeDecCursorPos(int dec) { |
| + if (onscreen_cursor_pos_ - dec > 0) { |
| + onscreen_cursor_pos_ -= dec; |
| + } else { |
| + onscreen_cursor_pos_ = 0; |
| + } |
| +} |
| + |
| +bool TextfieldView::IsDirectionalKey(app::KeyboardCode key_code) { |
| + switch (key_code) { |
| + case app::VKEY_RIGHT: |
| + case app::VKEY_LEFT: |
| + case app::VKEY_END: |
| + case app::VKEY_HOME: |
| + return true; |
| + break; |
|
tfarina
2010/11/18 11:55:31
I think the break is not necessary here, it won't
|
| + default: |
| + return false; |
| + } |
| +} |
| + |
| +bool TextfieldView::IsWritable(app::KeyboardCode key_code) { |
| + return (key_code >= app::VKEY_0 && key_code <= app::VKEY_Z) |
| + || (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
|
| + || (key_code >= app::VKEY_OEM_1 && key_code <= app::VKEY_OEM_8) |
| + || key_code == app::VKEY_SPACE; |
| +} |
| + |
| +wchar_t TextfieldView::GetWritableChar(const KeyEvent& key_event) { |
| + app::KeyboardCode key_code = key_event.GetKeyCode(); |
| + bool shift = false; |
| + if ((key_code >= app::VKEY_0 && key_code <= app::VKEY_9) |
| + || (key_code >= app::VKEY_OEM_1 && key_code <= app::VKEY_OEM_8)) { |
| + shift = key_event.IsShiftDown(); |
| + } else if (key_code >= app::VKEY_A && key_code <= app::VKEY_Z) { |
| + shift = (key_event.IsLockDown() && !key_event.IsShiftDown()) |
| + || (!key_event.IsLockDown() && key_event.IsShiftDown()); |
| + } |
| + return GetWritableChar(key_code, shift); |
| +} |
| + |
| +wchar_t TextfieldView::GetWritableChar(app::KeyboardCode key_code, |
| + bool shift) { |
|
tfarina
2010/11/18 11:55:31
You can align shift with app::KeyboardCode. Also p
|
| + switch (key_code) { |
| + case app::VKEY_NUMPAD0: |
| + return '0'; |
| + case app::VKEY_NUMPAD1: |
| + return '1'; |
| + case app::VKEY_NUMPAD2: |
| + return '2'; |
| + case app::VKEY_NUMPAD3: |
| + return '3'; |
| + case app::VKEY_NUMPAD4: |
| + return '4'; |
| + case app::VKEY_NUMPAD5: |
| + return '5'; |
| + case app::VKEY_NUMPAD6: |
| + return '6'; |
| + case app::VKEY_NUMPAD7: |
| + return '7'; |
| + case app::VKEY_NUMPAD8: |
| + return '8'; |
| + case app::VKEY_NUMPAD9: |
| + return '9'; |
| + case app::VKEY_MULTIPLY: |
| + return '*'; |
| + case app::VKEY_ADD: |
| + return '+'; |
| + case app::VKEY_SUBTRACT: |
| + return '-'; |
| + case app::VKEY_DECIMAL: |
| + return '.'; |
| + case app::VKEY_DIVIDE: |
| + return '/'; |
| + |
| +// case app::VKEY_BACK: |
|
tfarina
2010/11/18 11:55:31
Why this big block of comment? If it isn't used. P
|
| +// return GDK_BackSpace; |
| +// case app::VKEY_TAB: |
| +// return shift ? GDK_ISO_Left_Tab : GDK_Tab; |
| +// case app::VKEY_CLEAR: |
| +// return GDK_Clear; |
| +// case app::VKEY_RETURN: |
| +// return GDK_Return; |
| +// case app::VKEY_SHIFT: |
| +// return GDK_Shift_L; |
| +// case app::VKEY_CONTROL: |
| +// return GDK_Control_L; |
| +// case app::VKEY_MENU: |
| +// return GDK_Alt_L; |
| +// case app::VKEY_APPS: |
| +// return GDK_Menu; |
| +// |
| +// case app::VKEY_PAUSE: |
| +// return GDK_Pause; |
| +// case app::VKEY_CAPITAL: |
| +// return GDK_Caps_Lock; |
| +// case app::VKEY_KANA: |
| +// return GDK_Kana_Lock; |
| +// case app::VKEY_HANJA: |
| +// return GDK_Hangul_Hanja; |
| +// case app::VKEY_ESCAPE: |
| +// return GDK_Escape; |
| + case app::VKEY_SPACE: |
| + return ' '; |
| +// case app::VKEY_PRIOR: |
| +// return GDK_Page_Up; |
| +// case app::VKEY_NEXT: |
| +// return GDK_Page_Down; |
| +// case app::VKEY_END: |
| +// return GDK_End; |
| +// case app::VKEY_HOME: |
| +// return GDK_Home; |
| +// case app::VKEY_LEFT: |
| +// return GDK_Left; |
| +// case app::VKEY_UP: |
| +// return GDK_Up; |
| +// case app::VKEY_RIGHT: |
| +// return GDK_Right; |
| +// case app::VKEY_DOWN: |
| +// return GDK_Down; |
| +// case app::VKEY_SELECT: |
| +// return GDK_Select; |
| +// case app::VKEY_PRINT: |
| +// return GDK_Print; |
| +// case app::VKEY_EXECUTE: |
| +// return GDK_Execute; |
| +// case app::VKEY_INSERT: |
| +// return GDK_Insert; |
| +// case app::VKEY_DELETE: |
| +// return GDK_Delete; |
| +// case app::VKEY_HELP: |
| +// return GDK_Help; |
| + case app::VKEY_0: |
| + return shift ? ')' : '0'; |
| + case app::VKEY_1: |
| + return shift ? '!' : '1'; |
| + case app::VKEY_2: |
| + return shift ? '@' : '2'; |
| + case app::VKEY_3: |
| + return shift ? '#' : '3'; |
| + case app::VKEY_4: |
| + return shift ? '$' : '4'; |
| + case app::VKEY_5: |
| + return shift ? '%' : '5'; |
| + case app::VKEY_6: |
| + return shift ? '^' : '6'; |
| + case app::VKEY_7: |
| + return shift ? '&' : '7'; |
| + case app::VKEY_8: |
| + return shift ? '*' : '8'; |
| + case app::VKEY_9: |
| + return shift ? '(' : '9'; |
| + |
| + case app::VKEY_A: |
| + case app::VKEY_B: |
| + case app::VKEY_C: |
| + case app::VKEY_D: |
| + case app::VKEY_E: |
| + case app::VKEY_F: |
| + case app::VKEY_G: |
| + case app::VKEY_H: |
| + case app::VKEY_I: |
| + case app::VKEY_J: |
| + case app::VKEY_K: |
| + case app::VKEY_L: |
| + case app::VKEY_M: |
| + case app::VKEY_N: |
| + case app::VKEY_O: |
| + case app::VKEY_P: |
| + case app::VKEY_Q: |
| + case app::VKEY_R: |
| + case app::VKEY_S: |
| + case app::VKEY_T: |
| + case app::VKEY_U: |
| + case app::VKEY_V: |
| + case app::VKEY_W: |
| + case app::VKEY_X: |
| + case app::VKEY_Y: |
| + case app::VKEY_Z: |
| + return (shift ? 'A' : 'a') + (key_code - app::VKEY_A); |
| + |
| +// case app::VKEY_LWIN: |
| +// return GDK_Meta_L; |
| +// case app::VKEY_RWIN: |
| +// return GDK_Meta_R; |
| +// |
| +// case app::VKEY_NUMLOCK: |
| +// return GDK_Num_Lock; |
| +// |
| +// case app::VKEY_SCROLL: |
| +// return GDK_Scroll_Lock; |
| +// |
| + case app::VKEY_OEM_1: |
| + return shift ? ':' : ';'; |
| + case app::VKEY_OEM_PLUS: |
| + return shift ? '+' : '='; |
| + case app::VKEY_OEM_COMMA: |
| + return shift ? '<' : ','; |
| + case app::VKEY_OEM_MINUS: |
| + return shift ? '_' : '-'; |
| + case app::VKEY_OEM_PERIOD: |
| + return shift ? '>' : '.'; |
| + case app::VKEY_OEM_2: |
| + return shift ? '?' : '/'; |
| + case app::VKEY_OEM_3: |
| + return shift ? '~' : '`'; |
| + case app::VKEY_OEM_4: |
| + return shift ? '}' : ']'; |
| + case app::VKEY_OEM_5: |
| + return shift ? '|' : '\\'; |
| + case app::VKEY_OEM_6: |
| + return shift ? '{' : '['; |
| + case app::VKEY_OEM_7: |
| + return shift ? '"' : '\''; |
| +// |
| +// case app::VKEY_F1: |
| +// case app::VKEY_F2: |
| +// case app::VKEY_F3: |
| +// case app::VKEY_F4: |
| +// case app::VKEY_F5: |
| +// case app::VKEY_F6: |
| +// case app::VKEY_F7: |
| +// case app::VKEY_F8: |
| +// case app::VKEY_F9: |
| +// case app::VKEY_F10: |
| +// case app::VKEY_F11: |
| +// case app::VKEY_F12: |
| +// case app::VKEY_F13: |
| +// case app::VKEY_F14: |
| +// case app::VKEY_F15: |
| +// case app::VKEY_F16: |
| +// case app::VKEY_F17: |
| +// case app::VKEY_F18: |
| +// case app::VKEY_F19: |
| +// case app::VKEY_F20: |
| +// case app::VKEY_F21: |
| +// case app::VKEY_F22: |
| +// case app::VKEY_F23: |
| +// case app::VKEY_F24: |
| +// return GDK_F1 + (keycode - app::VKEY_F1); |
| + |
| + default: |
| + return 0; |
| + } |
| + } |
| +} |