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; |
+ } |
+ } |
+} |