summaryrefslogtreecommitdiffstats
path: root/views/controls/textfield/textfield.cc
diff options
context:
space:
mode:
Diffstat (limited to 'views/controls/textfield/textfield.cc')
-rw-r--r--views/controls/textfield/textfield.cc1241
1 files changed, 142 insertions, 1099 deletions
diff --git a/views/controls/textfield/textfield.cc b/views/controls/textfield/textfield.cc
index 6675ce9..68afc91 100644
--- a/views/controls/textfield/textfield.cc
+++ b/views/controls/textfield/textfield.cc
@@ -4,1054 +4,139 @@
#include "views/controls/textfield/textfield.h"
-#include <atlbase.h>
-#include <atlapp.h>
-#include <atlcrack.h>
-#include <atlctrls.h>
-#include <atlmisc.h>
-#include <tom.h> // For ITextDocument, a COM interface to CRichEditCtrl
-#include <vsstyle.h>
-
#include "app/gfx/insets.h"
-#include "app/l10n_util.h"
-#include "app/l10n_util_win.h"
+#if defined(OS_WIN)
#include "app/win_util.h"
-#include "base/clipboard.h"
-#include "base/gfx/native_theme.h"
-#include "base/scoped_clipboard_writer.h"
+#endif
#include "base/string_util.h"
-#include "base/win_util.h"
-#include "grit/app_strings.h"
-#include "skia/ext/skia_utils_win.h"
-#include "views/controls/hwnd_view.h"
-#include "views/controls/menu/menu_win.h"
-#include "views/focus/focus_util_win.h"
-#include "views/views_delegate.h"
+#include "views/controls/textfield/native_textfield_wrapper.h"
#include "views/widget/widget.h"
-using gfx::NativeTheme;
-
namespace views {
-static const int kDefaultEditStyle = WS_CHILD | WS_VISIBLE;
-
-class Textfield::Edit
- : public CWindowImpl<Textfield::Edit, CRichEditCtrl,
- CWinTraits<kDefaultEditStyle> >,
- public CRichEditCommands<Textfield::Edit>,
- public Menu::Delegate {
- public:
- DECLARE_WND_CLASS(L"ChromeViewsTextfieldEdit");
-
- Edit(Textfield* parent, bool draw_border);
- ~Edit();
-
- std::wstring GetText() const;
- void SetText(const std::wstring& text);
- void AppendText(const std::wstring& text);
-
- std::wstring GetSelectedText() const;
-
- // Selects all the text in the edit. Use this in place of SetSelAll() to
- // avoid selecting the "phantom newline" at the end of the edit.
- void SelectAll();
-
- // Clears the selection within the edit field and sets the caret to the end.
- void ClearSelection();
-
- // Removes the border.
- void RemoveBorder();
-
- void SetEnabled(bool enabled);
-
- void SetBackgroundColor(COLORREF bg_color);
-
- // CWindowImpl
- BEGIN_MSG_MAP(Edit)
- MSG_WM_CHAR(OnChar)
- MSG_WM_CONTEXTMENU(OnContextMenu)
- MSG_WM_COPY(OnCopy)
- MSG_WM_CUT(OnCut)
- MESSAGE_HANDLER_EX(WM_IME_CHAR, OnImeChar)
- MESSAGE_HANDLER_EX(WM_IME_STARTCOMPOSITION, OnImeStartComposition)
- MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeComposition)
- MESSAGE_HANDLER_EX(WM_IME_ENDCOMPOSITION, OnImeEndComposition)
- MSG_WM_KEYDOWN(OnKeyDown)
- MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
- MSG_WM_LBUTTONDOWN(OnLButtonDown)
- MSG_WM_LBUTTONUP(OnLButtonUp)
- MSG_WM_MBUTTONDOWN(OnNonLButtonDown)
- MSG_WM_MOUSEMOVE(OnMouseMove)
- MSG_WM_MOUSELEAVE(OnMouseLeave)
- MESSAGE_HANDLER_EX(WM_MOUSEWHEEL, OnMouseWheel)
- MSG_WM_NCCALCSIZE(OnNCCalcSize)
- MSG_WM_NCPAINT(OnNCPaint)
- MSG_WM_RBUTTONDOWN(OnNonLButtonDown)
- MSG_WM_PASTE(OnPaste)
- MSG_WM_SYSCHAR(OnSysChar) // WM_SYSxxx == WM_xxx with ALT down
- MSG_WM_SYSKEYDOWN(OnKeyDown)
- END_MSG_MAP()
-
- // Menu::Delegate
- virtual bool IsCommandEnabled(int id) const;
- virtual void ExecuteCommand(int id);
-
- private:
- // This object freezes repainting of the edit until the object is destroyed.
- // Some methods of the CRichEditCtrl draw synchronously to the screen. If we
- // don't freeze, the user will see a rapid series of calls to these as
- // flickers.
- //
- // Freezing the control while it is already frozen is permitted; the control
- // will unfreeze once both freezes are released (the freezes stack).
- class ScopedFreeze {
- public:
- ScopedFreeze(Edit* edit, ITextDocument* text_object_model);
- ~ScopedFreeze();
-
- private:
- Edit* const edit_;
- ITextDocument* const text_object_model_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFreeze);
- };
-
- // message handlers
- void OnChar(TCHAR key, UINT repeat_count, UINT flags);
- void OnContextMenu(HWND window, const CPoint& point);
- void OnCopy();
- void OnCut();
- LRESULT OnImeChar(UINT message, WPARAM wparam, LPARAM lparam);
- LRESULT OnImeStartComposition(UINT message, WPARAM wparam, LPARAM lparam);
- LRESULT OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam);
- LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam);
- void OnKeyDown(TCHAR key, UINT repeat_count, UINT flags);
- void OnLButtonDblClk(UINT keys, const CPoint& point);
- void OnLButtonDown(UINT keys, const CPoint& point);
- void OnLButtonUp(UINT keys, const CPoint& point);
- void OnMouseLeave();
- LRESULT OnMouseWheel(UINT message, WPARAM w_param, LPARAM l_param);
- void OnMouseMove(UINT keys, const CPoint& point);
- int OnNCCalcSize(BOOL w_param, LPARAM l_param);
- void OnNCPaint(HRGN region);
- void OnNonLButtonDown(UINT keys, const CPoint& point);
- void OnPaste();
- void OnSysChar(TCHAR ch, UINT repeat_count, UINT flags);
-
- // Helper function for OnChar() and OnKeyDown() that handles keystrokes that
- // could change the text in the edit.
- void HandleKeystroke(UINT message, TCHAR key, UINT repeat_count, UINT flags);
-
- // Every piece of code that can change the edit should call these functions
- // before and after the change. These functions determine if anything
- // meaningful changed, and do any necessary updating and notification.
- void OnBeforePossibleChange();
- void OnAfterPossibleChange();
-
- // Given an X coordinate in client coordinates, returns that coordinate
- // clipped to be within the horizontal bounds of the visible text.
- //
- // This is used in our mouse handlers to work around quirky behaviors of the
- // underlying CRichEditCtrl like not supporting triple-click when the user
- // doesn't click on the text itself.
- //
- // |is_triple_click| should be true iff this is the third click of a triple
- // click. Sadly, we need to clip slightly differently in this case.
- LONG ClipXCoordToVisibleText(LONG x, bool is_triple_click) const;
-
- // Sets whether the mouse is in the edit. As necessary this redraws the
- // edit.
- void SetContainsMouse(bool contains_mouse);
-
- // Getter for the text_object_model_, used by the ScopedFreeze class. Note
- // that the pointer returned here is only valid as long as the Edit is still
- // alive.
- ITextDocument* GetTextObjectModel() const;
-
- // We need to know if the user triple-clicks, so track double click points
- // and times so we can see if subsequent clicks are actually triple clicks.
- bool tracking_double_click_;
- CPoint double_click_point_;
- DWORD double_click_time_;
-
- // Used to discard unnecessary WM_MOUSEMOVE events after the first such
- // unnecessary event. See detailed comments in OnMouseMove().
- bool can_discard_mousemove_;
-
- // The text of this control before a possible change.
- std::wstring text_before_change_;
-
- // If true, the mouse is over the edit.
- bool contains_mouse_;
-
- static bool did_load_library_;
-
- Textfield* parent_;
-
- // The context menu for the edit.
- scoped_ptr<Menu> context_menu_;
-
- // Border insets.
- gfx::Insets content_insets_;
-
- // Whether the border is drawn.
- bool draw_border_;
-
- // This interface is useful for accessing the CRichEditCtrl at a low level.
- mutable CComQIPtr<ITextDocument> text_object_model_;
-
- // The position and the length of the ongoing composition string.
- // These values are used for removing a composition string from a search
- // text to emulate Firefox.
- bool ime_discard_composition_;
- int ime_composition_start_;
- int ime_composition_length_;
-
- COLORREF bg_color_;
-
- DISALLOW_COPY_AND_ASSIGN(Edit);
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Helper classes
-
-Textfield::Edit::ScopedFreeze::ScopedFreeze(Textfield::Edit* edit,
- ITextDocument* text_object_model)
- : edit_(edit),
- text_object_model_(text_object_model) {
- // Freeze the screen.
- if (text_object_model_) {
- long count;
- text_object_model_->Freeze(&count);
- }
-}
-
-Textfield::Edit::ScopedFreeze::~ScopedFreeze() {
- // Unfreeze the screen.
- if (text_object_model_) {
- long count;
- text_object_model_->Unfreeze(&count);
- if (count == 0) {
- // We need to UpdateWindow() here instead of InvalidateRect() because, as
- // far as I can tell, the edit likes to synchronously erase its background
- // when unfreezing, thus requiring us to synchronously redraw if we don't
- // want flicker.
- edit_->UpdateWindow();
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Textfield::Edit
-
-bool Textfield::Edit::did_load_library_ = false;
-
-Textfield::Edit::Edit(Textfield* parent, bool draw_border)
- : parent_(parent),
- tracking_double_click_(false),
- double_click_time_(0),
- can_discard_mousemove_(false),
- contains_mouse_(false),
- draw_border_(draw_border),
- ime_discard_composition_(false),
- ime_composition_start_(0),
- ime_composition_length_(0),
- bg_color_(0) {
- if (!did_load_library_)
- did_load_library_ = !!LoadLibrary(L"riched20.dll");
-
- DWORD style = kDefaultEditStyle;
- if (parent->GetStyle() & Textfield::STYLE_PASSWORD)
- style |= ES_PASSWORD;
-
- if (parent->read_only_)
- style |= ES_READONLY;
-
- if (parent->GetStyle() & Textfield::STYLE_MULTILINE)
- style |= ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL;
- else
- style |= ES_AUTOHSCROLL;
- // Make sure we apply RTL related extended window styles if necessary.
- DWORD ex_style = l10n_util::GetExtendedStyles();
-
- RECT r = {0, 0, parent_->width(), parent_->height()};
- Create(parent_->GetWidget()->GetNativeView(), r, NULL, style, ex_style);
-
- if (parent->GetStyle() & Textfield::STYLE_LOWERCASE) {
- DCHECK((parent->GetStyle() & Textfield::STYLE_PASSWORD) == 0);
- SetEditStyle(SES_LOWERCASE, SES_LOWERCASE);
- }
-
- // Set up the text_object_model_.
- CComPtr<IRichEditOle> ole_interface;
- ole_interface.Attach(GetOleInterface());
- text_object_model_ = ole_interface;
-
- context_menu_.reset(new MenuWin(this, Menu::TOPLEFT, m_hWnd));
- context_menu_->AppendMenuItemWithLabel(IDS_APP_UNDO,
- l10n_util::GetString(IDS_APP_UNDO));
- context_menu_->AppendSeparator();
- context_menu_->AppendMenuItemWithLabel(IDS_APP_CUT,
- l10n_util::GetString(IDS_APP_CUT));
- context_menu_->AppendMenuItemWithLabel(IDS_APP_COPY,
- l10n_util::GetString(IDS_APP_COPY));
- context_menu_->AppendMenuItemWithLabel(IDS_APP_PASTE,
- l10n_util::GetString(IDS_APP_PASTE));
- context_menu_->AppendSeparator();
- context_menu_->AppendMenuItemWithLabel(IDS_APP_SELECT_ALL,
- l10n_util::GetString(IDS_APP_SELECT_ALL));
-}
-
-Textfield::Edit::~Edit() {
-}
-
-std::wstring Textfield::Edit::GetText() const {
- int len = GetTextLength() + 1;
- std::wstring str;
- GetWindowText(WriteInto(&str, len), len);
- return str;
-}
-
-void Textfield::Edit::SetText(const std::wstring& text) {
- // Adjusting the string direction before setting the text in order to make
- // sure both RTL and LTR strings are displayed properly.
- std::wstring text_to_set;
- if (!l10n_util::AdjustStringForLocaleDirection(text, &text_to_set))
- text_to_set = text;
- if (parent_->GetStyle() & STYLE_LOWERCASE)
- text_to_set = l10n_util::ToLower(text_to_set);
- SetWindowText(text_to_set.c_str());
-}
-
-void Textfield::Edit::AppendText(const std::wstring& text) {
- int text_length = GetWindowTextLength();
- ::SendMessage(m_hWnd, TBM_SETSEL, true, MAKELPARAM(text_length, text_length));
- ::SendMessage(m_hWnd, EM_REPLACESEL, false,
- reinterpret_cast<LPARAM>(text.c_str()));
-}
-
-std::wstring Textfield::Edit::GetSelectedText() const {
- // Figure out the length of the selection.
- long start;
- long end;
- GetSel(start, end);
-
- // Grab the selected text.
- std::wstring str;
- GetSelText(WriteInto(&str, end - start + 1));
-
- return str;
-}
-
-void Textfield::Edit::SelectAll() {
- // Select from the end to the front so that the first part of the text is
- // always visible.
- SetSel(GetTextLength(), 0);
-}
-
-void Textfield::Edit::ClearSelection() {
- SetSel(GetTextLength(), GetTextLength());
-}
-
-void Textfield::Edit::RemoveBorder() {
- if (!draw_border_)
- return;
-
- draw_border_ = false;
- SetWindowPos(NULL, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOACTIVATE |
- SWP_NOOWNERZORDER | SWP_NOSIZE);
-}
-
-void Textfield::Edit::SetEnabled(bool enabled) {
- SendMessage(parent_->GetNativeComponent(), WM_ENABLE,
- static_cast<WPARAM>(enabled), 0);
-}
-
-// static
-bool Textfield::IsKeystrokeEnter(const Keystroke& key) {
- return key.key == VK_RETURN;
-}
-
// static
-bool Textfield::IsKeystrokeEscape(const Keystroke& key) {
- return key.key == VK_ESCAPE;
-}
-
-void Textfield::Edit::SetBackgroundColor(COLORREF bg_color) {
- CRichEditCtrl::SetBackgroundColor(bg_color);
- bg_color_ = bg_color;
-}
-
-bool Textfield::Edit::IsCommandEnabled(int id) const {
- switch (id) {
- case IDS_APP_UNDO: return !parent_->IsReadOnly() && !!CanUndo();
- case IDS_APP_CUT: return !parent_->IsReadOnly() &&
- !parent_->IsPassword() && !!CanCut();
- case IDS_APP_COPY: return !!CanCopy() && !parent_->IsPassword();
- case IDS_APP_PASTE: return !parent_->IsReadOnly() && !!CanPaste();
- case IDS_APP_SELECT_ALL: return !!CanSelectAll();
- default: NOTREACHED();
- return false;
- }
-}
-
-void Textfield::Edit::ExecuteCommand(int id) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- switch (id) {
- case IDS_APP_UNDO: Undo(); break;
- case IDS_APP_CUT: Cut(); break;
- case IDS_APP_COPY: Copy(); break;
- case IDS_APP_PASTE: Paste(); break;
- case IDS_APP_SELECT_ALL: SelectAll(); break;
- default: NOTREACHED(); break;
- }
- OnAfterPossibleChange();
-}
-
-void Textfield::Edit::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
- HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags);
-}
-
-void Textfield::Edit::OnContextMenu(HWND window, const CPoint& point) {
- CPoint p(point);
- if (point.x == -1 || point.y == -1) {
- GetCaretPos(&p);
- MapWindowPoints(HWND_DESKTOP, &p, 1);
- }
- context_menu_->RunMenuAt(p.x, p.y);
-}
+const char Textfield::kViewClassName[] = "views/Textfield";
-void Textfield::Edit::OnCopy() {
- if (parent_->IsPassword())
- return;
-
- const std::wstring text(GetSelectedText());
-
- if (!text.empty() && ViewsDelegate::views_delegate) {
- ScopedClipboardWriter scw(ViewsDelegate::views_delegate->GetClipboard());
- scw.WriteText(text);
- }
-}
-
-void Textfield::Edit::OnCut() {
- if (parent_->IsReadOnly() || parent_->IsPassword())
- return;
-
- OnCopy();
-
- // This replace selection will have no effect (even on the undo stack) if the
- // current selection is empty.
- ReplaceSel(L"", true);
-}
+/////////////////////////////////////////////////////////////////////////////
+// Textfield
-LRESULT Textfield::Edit::OnImeChar(UINT message, WPARAM wparam, LPARAM lparam) {
- // http://crbug.com/7707: a rich-edit control may crash when it receives a
- // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message.
- // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR
- // messages from being dispatched to view controls via the CallWindowProc()
- // call.
- return 0;
+Textfield::Textfield()
+ : native_wrapper_(NULL),
+ controller_(NULL),
+ style_(STYLE_DEFAULT),
+ read_only_(false),
+ default_width_in_chars_(0),
+ draw_border_(true),
+ background_color_(SK_ColorWHITE),
+ use_default_background_color_(true),
+ num_lines_(1),
+ initialized_(false) {
+ SetFocusable(true);
+}
+
+Textfield::Textfield(StyleFlags style)
+ : native_wrapper_(NULL),
+ controller_(NULL),
+ style_(style),
+ read_only_(false),
+ default_width_in_chars_(0),
+ draw_border_(true),
+ background_color_(SK_ColorWHITE),
+ use_default_background_color_(true),
+ num_lines_(1),
+ initialized_(false) {
+ SetFocusable(true);
}
-LRESULT Textfield::Edit::OnImeStartComposition(UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- // Users may press alt+shift or control+shift keys to change their keyboard
- // layouts. So, we retrieve the input locale identifier everytime we start
- // an IME composition.
- int language_id = PRIMARYLANGID(GetKeyboardLayout(0));
- ime_discard_composition_ =
- language_id == LANG_JAPANESE || language_id == LANG_CHINESE;
- ime_composition_start_ = 0;
- ime_composition_length_ = 0;
-
- return DefWindowProc(message, wparam, lparam);
+Textfield::~Textfield() {
+ if (native_wrapper_)
+ delete native_wrapper_;
}
-LRESULT Textfield::Edit::OnImeComposition(UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- text_before_change_.clear();
- LRESULT result = DefWindowProc(message, wparam, lparam);
-
- ime_composition_start_ = 0;
- ime_composition_length_ = 0;
- if (ime_discard_composition_) {
- // Call IMM32 functions to retrieve the position and the length of the
- // ongoing composition string and notify the OnAfterPossibleChange()
- // function that it should discard the composition string from a search
- // string. We should not call IMM32 functions in the function because it
- // is called when an IME is not composing a string.
- HIMC imm_context = ImmGetContext(m_hWnd);
- if (imm_context) {
- CHARRANGE selection;
- GetSel(selection);
- const int cursor_position =
- ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0);
- if (cursor_position >= 0)
- ime_composition_start_ = selection.cpMin - cursor_position;
-
- const int composition_size =
- ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0);
- if (composition_size >= 0)
- ime_composition_length_ = composition_size / sizeof(wchar_t);
-
- ImmReleaseContext(m_hWnd, imm_context);
- }
- }
-
- OnAfterPossibleChange();
- return result;
+void Textfield::SetController(Controller* controller) {
+ controller_ = controller;
}
-LRESULT Textfield::Edit::OnImeEndComposition(UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without
- // sending any WM_IME_COMPOSITION messages when a user deletes all
- // composition characters, i.e. a composition string becomes empty. To handle
- // this case, we need to update the find results when a composition is
- // finished or canceled.
- parent_->SyncText();
- if (parent_->GetController())
- parent_->GetController()->ContentsChanged(parent_, GetText());
- return DefWindowProc(message, wparam, lparam);
+Textfield::Controller* Textfield::GetController() const {
+ return controller_;
}
-void Textfield::Edit::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) {
- // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than
- // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places
- // in this function even with a WM_SYSKEYDOWN handler.
-
- switch (key) {
- case VK_RETURN:
- // If we are multi-line, we want to let returns through so they start a
- // new line.
- if (parent_->IsMultiLine())
- break;
- else
- return;
- // Hijacking Editing Commands
- //
- // We hijack the keyboard short-cuts for Cut, Copy, and Paste here so that
- // they go through our clipboard routines. This allows us to be smarter
- // about how we interact with the clipboard and avoid bugs in the
- // CRichEditCtrl. If we didn't hijack here, the edit control would handle
- // these internally with sending the WM_CUT, WM_COPY, or WM_PASTE messages.
- //
- // Cut: Shift-Delete and Ctrl-x are treated as cut. Ctrl-Shift-Delete and
- // Ctrl-Shift-x are not treated as cut even though the underlying
- // CRichTextEdit would treat them as such.
- // Copy: Ctrl-c is treated as copy. Shift-Ctrl-c is not.
- // Paste: Shift-Insert and Ctrl-v are tread as paste. Ctrl-Shift-Insert and
- // Ctrl-Shift-v are not.
- //
- // This behavior matches most, but not all Windows programs, and largely
- // conforms to what users expect.
-
- case VK_DELETE:
- case 'X':
- if ((flags & KF_ALTDOWN) ||
- (GetKeyState((key == 'X') ? VK_CONTROL : VK_SHIFT) >= 0))
- break;
- if (GetKeyState((key == 'X') ? VK_SHIFT : VK_CONTROL) >= 0) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- Cut();
- OnAfterPossibleChange();
- }
- return;
-
- case 'C':
- if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
- break;
- if (GetKeyState(VK_SHIFT) >= 0)
- Copy();
- return;
-
- case VK_INSERT:
- case 'V':
- if ((flags & KF_ALTDOWN) ||
- (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
- break;
- if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- Paste();
- OnAfterPossibleChange();
- }
- return;
-
- case 0xbb: // Ctrl-'='. Triggers subscripting, even in plain text mode.
- return;
-
- case VK_PROCESSKEY:
- // This key event is consumed by an IME.
- // We ignore this event because an IME sends WM_IME_COMPOSITION messages
- // when it updates the CRichEditCtrl text.
- return;
+void Textfield::SetReadOnly(bool read_only) {
+ read_only_ = read_only;
+ if (native_wrapper_) {
+ native_wrapper_->UpdateReadOnly();
+ native_wrapper_->UpdateBackgroundColor();
}
-
- // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many
- // different keys (backspace, ctrl-v, ...), so we call this in both cases.
- HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags);
-}
-
-void Textfield::Edit::OnLButtonDblClk(UINT keys, const CPoint& point) {
- // Save the double click info for later triple-click detection.
- tracking_double_click_ = true;
- double_click_point_ = point;
- double_click_time_ = GetCurrentMessage()->time;
-
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- DefWindowProc(WM_LBUTTONDBLCLK, keys,
- MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
- OnAfterPossibleChange();
-}
-
-void Textfield::Edit::OnLButtonDown(UINT keys, const CPoint& point) {
- // Check for triple click, then reset tracker. Should be safe to subtract
- // double_click_time_ from the current message's time even if the timer has
- // wrapped in between.
- const bool is_triple_click = tracking_double_click_ &&
- win_util::IsDoubleClick(double_click_point_, point,
- GetCurrentMessage()->time - double_click_time_);
- tracking_double_click_ = false;
-
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- DefWindowProc(WM_LBUTTONDOWN, keys,
- MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
- point.y));
- OnAfterPossibleChange();
-}
-
-void Textfield::Edit::OnLButtonUp(UINT keys, const CPoint& point) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- DefWindowProc(WM_LBUTTONUP, keys,
- MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
- OnAfterPossibleChange();
}
-void Textfield::Edit::OnMouseLeave() {
- SetContainsMouse(false);
-}
-
-LRESULT Textfield::Edit::OnMouseWheel(UINT message,
- WPARAM w_param, LPARAM l_param) {
- // Reroute the mouse-wheel to the window under the mouse pointer if
- // applicable.
- if (views::RerouteMouseWheel(m_hWnd, w_param, l_param))
- return 0;
- return DefWindowProc(message, w_param, l_param);;
+bool Textfield::IsPassword() const {
+ return style_ & STYLE_PASSWORD;
}
-void Textfield::Edit::OnMouseMove(UINT keys, const CPoint& point) {
- SetContainsMouse(true);
- // Clamp the selection to the visible text so the user can't drag to select
- // the "phantom newline". In theory we could achieve this by clipping the X
- // coordinate, but in practice the edit seems to behave nondeterministically
- // with similar sequences of clipped input coordinates fed to it. Maybe it's
- // reading the mouse cursor position directly?
- //
- // This solution has a minor visual flaw, however: if there's a visible
- // cursor at the edge of the text (only true when there's no selection),
- // dragging the mouse around outside that edge repaints the cursor on every
- // WM_MOUSEMOVE instead of allowing it to blink normally. To fix this, we
- // special-case this exact case and discard the WM_MOUSEMOVE messages instead
- // of passing them along.
- //
- // But even this solution has a flaw! (Argh.) In the case where the user
- // has a selection that starts at the edge of the edit, and proceeds to the
- // middle of the edit, and the user is dragging back past the start edge to
- // remove the selection, there's a redraw problem where the change between
- // having the last few bits of text still selected and having nothing
- // selected can be slow to repaint (which feels noticeably strange). This
- // occurs if you only let the edit receive a single WM_MOUSEMOVE past the
- // edge of the text. I think on each WM_MOUSEMOVE the edit is repainting its
- // previous state, then updating its internal variables to the new state but
- // not repainting. To fix this, we allow one more WM_MOUSEMOVE through after
- // the selection has supposedly been shrunk to nothing; this makes the edit
- // redraw the selection quickly so it feels smooth.
- CHARRANGE selection;
- GetSel(selection);
- const bool possibly_can_discard_mousemove =
- (selection.cpMin == selection.cpMax) &&
- (((selection.cpMin == 0) &&
- (ClipXCoordToVisibleText(point.x, false) > point.x)) ||
- ((selection.cpMin == GetTextLength()) &&
- (ClipXCoordToVisibleText(point.x, false) < point.x)));
- if (!can_discard_mousemove_ || !possibly_can_discard_mousemove) {
- can_discard_mousemove_ = possibly_can_discard_mousemove;
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- // Force the Y coordinate to the center of the clip rect. The edit
- // behaves strangely when the cursor is dragged vertically: if the cursor
- // is in the middle of the text, drags inside the clip rect do nothing,
- // and drags outside the clip rect act as if the cursor jumped to the
- // left edge of the text. When the cursor is at the right edge, drags of
- // just a few pixels vertically end up selecting the "phantom newline"...
- // sometimes.
- RECT r;
- GetRect(&r);
- DefWindowProc(WM_MOUSEMOVE, keys,
- MAKELPARAM(point.x, (r.bottom - r.top) / 2));
- OnAfterPossibleChange();
- }
+bool Textfield::IsMultiLine() const {
+ return !!(style_ & STYLE_MULTILINE);
}
-int Textfield::Edit::OnNCCalcSize(BOOL w_param, LPARAM l_param) {
- content_insets_.Set(0, 0, 0, 0);
- parent_->CalculateInsets(&content_insets_);
- if (w_param) {
- NCCALCSIZE_PARAMS* nc_params =
- reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param);
- nc_params->rgrc[0].left += content_insets_.left();
- nc_params->rgrc[0].right -= content_insets_.right();
- nc_params->rgrc[0].top += content_insets_.top();
- nc_params->rgrc[0].bottom -= content_insets_.bottom();
- } else {
- RECT* rect = reinterpret_cast<RECT*>(l_param);
- rect->left += content_insets_.left();
- rect->right -= content_insets_.right();
- rect->top += content_insets_.top();
- rect->bottom -= content_insets_.bottom();
- }
- return 0;
+void Textfield::SetText(const std::wstring& text) {
+ text_ = text;
+ if (native_wrapper_)
+ native_wrapper_->UpdateText();
}
-void Textfield::Edit::OnNCPaint(HRGN region) {
- if (!draw_border_)
- return;
-
- HDC hdc = GetWindowDC();
-
- CRect window_rect;
- GetWindowRect(&window_rect);
- // Convert to be relative to 0x0.
- window_rect.MoveToXY(0, 0);
-
- ExcludeClipRect(hdc,
- window_rect.left + content_insets_.left(),
- window_rect.top + content_insets_.top(),
- window_rect.right - content_insets_.right(),
- window_rect.bottom - content_insets_.bottom());
-
- HBRUSH brush = CreateSolidBrush(bg_color_);
- FillRect(hdc, &window_rect, brush);
- DeleteObject(brush);
-
- int part;
- int state;
-
- if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) {
- part = EP_EDITTEXT;
-
- if (!parent_->IsEnabled())
- state = ETS_DISABLED;
- else if (parent_->IsReadOnly())
- state = ETS_READONLY;
- else if (!contains_mouse_)
- state = ETS_NORMAL;
- else
- state = ETS_HOT;
- } else {
- part = EP_EDITBORDER_HVSCROLL;
-
- if (!parent_->IsEnabled())
- state = EPSHV_DISABLED;
- else if (GetFocus() == m_hWnd)
- state = EPSHV_FOCUSED;
- else if (contains_mouse_)
- state = EPSHV_HOT;
- else
- state = EPSHV_NORMAL;
- // Vista doesn't appear to have a unique state for readonly.
- }
-
- int classic_state =
- (!parent_->IsEnabled() || parent_->IsReadOnly()) ? DFCS_INACTIVE : 0;
-
- NativeTheme::instance()->PaintTextField(hdc, part, state, classic_state,
- &window_rect, bg_color_, false,
- true);
-
- // NOTE: I tried checking the transparent property of the theme and invoking
- // drawParentBackground, but it didn't seem to make a difference.
-
- ReleaseDC(hdc);
+void Textfield::AppendText(const std::wstring& text) {
+ text_ += text;
+ if (native_wrapper_)
+ native_wrapper_->AppendText(text);
}
-void Textfield::Edit::OnNonLButtonDown(UINT keys, const CPoint& point) {
- // Interestingly, the edit doesn't seem to cancel triple clicking when the
- // x-buttons (which usually means "thumb buttons") are pressed, so we only
- // call this for M and R down.
- tracking_double_click_ = false;
- SetMsgHandled(false);
+void Textfield::SelectAll() {
+ if (native_wrapper_)
+ native_wrapper_->SelectAll();
}
-void Textfield::Edit::OnPaste() {
- if (parent_->IsReadOnly() || !ViewsDelegate::views_delegate)
- return;
-
- Clipboard* clipboard = ViewsDelegate::views_delegate->GetClipboard();
-
- if (!clipboard->IsFormatAvailable(Clipboard::GetPlainTextWFormatType()))
- return;
-
- std::wstring clipboard_str;
- clipboard->ReadText(&clipboard_str);
- if (!clipboard_str.empty()) {
- std::wstring collapsed(CollapseWhitespace(clipboard_str, false));
- if (parent_->GetStyle() & STYLE_LOWERCASE)
- collapsed = l10n_util::ToLower(collapsed);
- // Force a Paste operation to trigger OnContentsChanged, even if identical
- // contents are pasted into the text box.
- text_before_change_.clear();
- ReplaceSel(collapsed.c_str(), true);
- }
+void Textfield::ClearSelection() const {
+ if (native_wrapper_)
+ native_wrapper_->ClearSelection();
}
-void Textfield::Edit::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) {
- // Nearly all alt-<xxx> combos result in beeping rather than doing something
- // useful, so we discard most. Exceptions:
- // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead
- // of WM_SYSCHAR, so it doesn't need to be handled here.
- // * alt-space gets translated by the default WM_SYSCHAR handler to a
- // WM_SYSCOMMAND to open the application context menu, so we need to allow
- // it through.
- if (ch == VK_SPACE)
- SetMsgHandled(false);
+void Textfield::SetBackgroundColor(SkColor color) {
+ background_color_ = color;
+ use_default_background_color_ = false;
+ if (native_wrapper_)
+ native_wrapper_->UpdateBackgroundColor();
}
-void Textfield::Edit::HandleKeystroke(UINT message,
- TCHAR key,
- UINT repeat_count,
- UINT flags) {
- ScopedFreeze freeze(this, GetTextObjectModel());
-
- Textfield::Controller* controller = parent_->GetController();
- bool handled = false;
- if (controller) {
- handled = controller->HandleKeystroke(parent_,
- Textfield::Keystroke(message, key, repeat_count, flags));
- }
-
- if (!handled) {
- OnBeforePossibleChange();
- DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
- OnAfterPossibleChange();
- }
+void Textfield::UseDefaultBackgroundColor() {
+ use_default_background_color_ = true;
+ if (native_wrapper_)
+ native_wrapper_->UpdateBackgroundColor();
}
-void Textfield::Edit::OnBeforePossibleChange() {
- // Record our state.
- text_before_change_ = GetText();
+void Textfield::SetFont(const gfx::Font& font) {
+ font_ = font;
+ if (native_wrapper_)
+ native_wrapper_->UpdateFont();
}
-void Textfield::Edit::OnAfterPossibleChange() {
- // Prevent the user from selecting the "phantom newline" at the end of the
- // edit. If they try, we just silently move the end of the selection back to
- // the end of the real text.
- CHARRANGE new_sel;
- GetSel(new_sel);
- const int length = GetTextLength();
- if (new_sel.cpMax > length) {
- new_sel.cpMax = length;
- if (new_sel.cpMin > length)
- new_sel.cpMin = length;
- SetSel(new_sel);
- }
-
- std::wstring new_text(GetText());
- if (new_text != text_before_change_) {
- if (ime_discard_composition_ && ime_composition_start_ >= 0 &&
- ime_composition_length_ > 0) {
- // A string retrieved with a GetText() call contains a string being
- // composed by an IME. We remove the composition string from this search
- // string.
- new_text.erase(ime_composition_start_, ime_composition_length_);
- ime_composition_start_ = 0;
- ime_composition_length_ = 0;
- if (new_text.empty())
- return;
- }
- parent_->SyncText();
- if (parent_->GetController())
- parent_->GetController()->ContentsChanged(parent_, new_text);
- }
+void Textfield::SetHorizontalMargins(int left, int right) {
+ if (native_wrapper_)
+ native_wrapper_->SetHorizontalMargins(left, right);
}
-LONG Textfield::Edit::ClipXCoordToVisibleText(LONG x,
- bool is_triple_click) const {
- // Clip the X coordinate to the left edge of the text. Careful:
- // PosFromChar(0) may return a negative X coordinate if the beginning of the
- // text has scrolled off the edit, so don't go past the clip rect's edge.
- PARAFORMAT2 pf2;
- GetParaFormat(pf2);
- // Calculation of the clipped coordinate is more complicated if the paragraph
- // layout is RTL layout, or if there is RTL characters inside the LTR layout
- // paragraph.
- bool ltr_text_in_ltr_layout = true;
- if ((pf2.wEffects & PFE_RTLPARA) ||
- l10n_util::StringContainsStrongRTLChars(GetText())) {
- ltr_text_in_ltr_layout = false;
- }
- const int length = GetTextLength();
- RECT r;
- GetRect(&r);
- // The values returned by PosFromChar() seem to refer always
- // to the left edge of the character's bounding box.
- const LONG first_position_x = PosFromChar(0).x;
- LONG min_x = first_position_x;
- if (!ltr_text_in_ltr_layout) {
- for (int i = 1; i < length; ++i)
- min_x = std::min(min_x, PosFromChar(i).x);
- }
- const LONG left_bound = std::max(r.left, min_x);
-
- // PosFromChar(length) is a phantom character past the end of the text. It is
- // not necessarily a right bound; in RTL controls it may be a left bound. So
- // treat it as a right bound only if it is to the right of the first
- // character.
- LONG right_bound = r.right;
- LONG end_position_x = PosFromChar(length).x;
- if (end_position_x >= first_position_x) {
- right_bound = std::min(right_bound, end_position_x); // LTR case.
- }
- // For trailing characters that are 2 pixels wide of less (like "l" in some
- // fonts), we have a problem:
- // * Clicks on any pixel within the character will place the cursor before
- // the character.
- // * Clicks on the pixel just after the character will not allow triple-
- // click to work properly (true for any last character width).
- // So, we move to the last pixel of the character when this is a
- // triple-click, and moving to one past the last pixel in all other
- // scenarios. This way, all clicks that can move the cursor will place it at
- // the end of the text, but triple-click will still work.
- if (x < left_bound) {
- return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 :
- left_bound;
- }
- if ((length == 0) || (x < right_bound))
- return x;
- return is_triple_click ? (right_bound - 1) : right_bound;
+void Textfield::SetHeightInLines(int num_lines) {
+ DCHECK(IsMultiLine());
+ num_lines_ = num_lines;
}
-void Textfield::Edit::SetContainsMouse(bool contains_mouse) {
- if (contains_mouse == contains_mouse_)
- return;
-
- contains_mouse_ = contains_mouse;
-
+void Textfield::RemoveBorder() {
if (!draw_border_)
return;
- if (contains_mouse_) {
- // Register for notification when the mouse leaves. Need to do this so
- // that we can reset contains mouse properly.
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(tme);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = m_hWnd;
- tme.dwHoverTime = 0;
- TrackMouseEvent(&tme);
- }
- RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME);
-}
-
-ITextDocument* Textfield::Edit::GetTextObjectModel() const {
- if (!text_object_model_) {
- CComPtr<IRichEditOle> ole_interface;
- ole_interface.Attach(GetOleInterface());
- text_object_model_ = ole_interface;
- }
- return text_object_model_;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// Textfield
-
-Textfield::~Textfield() {
- if (edit_) {
- // If the edit hwnd still exists, we need to destroy it explicitly.
- if (*edit_)
- edit_->DestroyWindow();
- delete edit_;
- }
-}
-
-void Textfield::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
- Widget* widget;
-
- if (is_add && (widget = GetWidget())) {
- // This notification is called from the AddChildView call below. Ignore it.
- if (native_view_ && !edit_)
- return;
-
- if (!native_view_) {
- native_view_ = new HWNDView(); // Deleted from our superclass destructor
- AddChildView(native_view_);
-
- // Maps the focus of the native control to the focus of this view.
- native_view_->SetAssociatedFocusView(this);
- }
-
- // If edit_ is invalid from a previous use. Reset it.
- if (edit_ && !IsWindow(edit_->m_hWnd)) {
- native_view_->Detach();
- delete edit_;
- edit_ = NULL;
- }
-
- if (!edit_) {
- edit_ = new Edit(this, draw_border_);
- edit_->SetFont(font_.hfont());
- native_view_->Attach(*edit_);
- if (!text_.empty())
- edit_->SetText(text_);
- UpdateEditBackgroundColor();
- Layout();
- }
- } else if (!is_add && edit_ && IsWindow(edit_->m_hWnd)) {
- edit_->SetParent(NULL);
- }
-}
-
-void Textfield::Layout() {
- if (native_view_) {
- native_view_->SetBounds(GetLocalBounds(true));
- native_view_->Layout();
- }
-}
-
-gfx::Size Textfield::GetPreferredSize() {
- gfx::Insets insets;
- CalculateInsets(&insets);
- return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) +
- insets.width(),
- num_lines_ * font_.height() + insets.height());
-}
-
-std::wstring Textfield::GetText() const {
- return text_;
-}
-
-void Textfield::SetText(const std::wstring& text) {
- text_ = text;
- if (edit_)
- edit_->SetText(text);
+ draw_border_ = false;
+ if (native_wrapper_)
+ native_wrapper_->UpdateBorder();
}
-void Textfield::AppendText(const std::wstring& text) {
- text_ += text;
- if (edit_)
- edit_->AppendText(text);
-}
void Textfield::CalculateInsets(gfx::Insets* insets) {
DCHECK(insets);
@@ -1068,108 +153,40 @@ void Textfield::CalculateInsets(gfx::Insets* insets) {
}
void Textfield::SyncText() {
- if (edit_)
- text_ = edit_->GetText();
-}
-
-void Textfield::SetController(Controller* controller) {
- controller_ = controller;
-}
-
-Textfield::Controller* Textfield::GetController() const {
- return controller_;
+ if (native_wrapper_)
+ text_ = native_wrapper_->GetText();
}
-bool Textfield::IsReadOnly() const {
- return edit_ ? ((edit_->GetStyle() & ES_READONLY) != 0) : read_only_;
+// static
+bool Textfield::IsKeystrokeEnter(const Keystroke& key) {
+ return key.key == VK_RETURN;
}
-bool Textfield::IsPassword() const {
- return GetStyle() & Textfield::STYLE_PASSWORD;
+// static
+bool Textfield::IsKeystrokeEscape(const Keystroke& key) {
+ return key.key == VK_ESCAPE;
}
-bool Textfield::IsMultiLine() const {
- return (style_ & STYLE_MULTILINE) != 0;
-}
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, View overrides:
-void Textfield::SetReadOnly(bool read_only) {
- read_only_ = read_only;
- if (edit_) {
- edit_->SetReadOnly(read_only);
- UpdateEditBackgroundColor();
+void Textfield::Layout() {
+ if (native_wrapper_) {
+ native_wrapper_->GetView()->SetBounds(GetLocalBounds(true));
+ native_wrapper_->GetView()->Layout();
}
}
-void Textfield::Focus() {
- ::SetFocus(native_view_->GetHWND());
-}
-
-void Textfield::SelectAll() {
- if (edit_)
- edit_->SelectAll();
-}
-
-void Textfield::ClearSelection() const {
- if (edit_)
- edit_->ClearSelection();
-}
-
-HWND Textfield::GetNativeComponent() {
- return native_view_->GetHWND();
-}
-
-void Textfield::SetBackgroundColor(SkColor color) {
- background_color_ = color;
- use_default_background_color_ = false;
- UpdateEditBackgroundColor();
-}
-
-void Textfield::SetDefaultBackgroundColor() {
- use_default_background_color_ = true;
- UpdateEditBackgroundColor();
-}
-
-void Textfield::SetFont(const gfx::Font& font) {
- font_ = font;
- if (edit_)
- edit_->SetFont(font.hfont());
-}
-
-gfx::Font Textfield::GetFont() const {
- return font_;
-}
-
-bool Textfield::SetHorizontalMargins(int left, int right) {
- // SendMessage expects the two values to be packed into one using MAKELONG
- // so we truncate to 16 bits if necessary.
- return ERROR_SUCCESS == SendMessage(GetNativeComponent(),
- (UINT) EM_SETMARGINS,
- (WPARAM) EC_LEFTMARGIN | EC_RIGHTMARGIN,
- (LPARAM) MAKELONG(left & 0xFFFF,
- right & 0xFFFF));
-}
-
-void Textfield::SetHeightInLines(int num_lines) {
- DCHECK(IsMultiLine());
- num_lines_ = num_lines;
-}
-
-void Textfield::RemoveBorder() {
- if (!draw_border_)
- return;
-
- draw_border_ = false;
- if (edit_)
- edit_->RemoveBorder();
-}
-
-void Textfield::SetEnabled(bool enabled) {
- View::SetEnabled(enabled);
- edit_->SetEnabled(enabled);
+gfx::Size Textfield::GetPreferredSize() {
+ gfx::Insets insets;
+ CalculateInsets(&insets);
+ return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) +
+ insets.width(),
+ num_lines_ * font_.height() + insets.height());
}
bool Textfield::IsFocusable() const {
- return IsEnabled() && !IsReadOnly();
+ return IsEnabled() && !read_only_;
}
void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
@@ -1177,6 +194,7 @@ void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
}
bool Textfield::SkipDefaultKeyEventProcessing(const KeyEvent& e) {
+#if defined(OS_WIN)
// TODO(hamaji): Figure out which keyboard combinations we need to add here,
// similar to LocationBarView::SkipDefaultKeyEventProcessing.
if (e.GetCharacter() == VK_BACK)
@@ -1187,20 +205,45 @@ bool Textfield::SkipDefaultKeyEventProcessing(const KeyEvent& e) {
if (e.IsAltDown() &&
win_util::IsNumPadDigit(e.GetCharacter(), e.IsExtendedKey()))
return true;
-
+#endif
return false;
}
-void Textfield::UpdateEditBackgroundColor() {
- if (!edit_)
- return;
+void Textfield::SetEnabled(bool enabled) {
+ View::SetEnabled(enabled);
+ if (native_wrapper_)
+ native_wrapper_->UpdateEnabled();
+}
+
+void Textfield::Focus() {
+ if (native_wrapper_) {
+ // Forward the focus to the wrapper if it exists.
+ native_wrapper_->SetFocus();
+ } else {
+ // If there is no wrapper, cause the RootView to be focused so that we still
+ // get keyboard messages.
+ View::Focus();
+ }
+}
- COLORREF bg_color;
- if (!use_default_background_color_)
- bg_color = skia::SkColorToCOLORREF(background_color_);
- else
- bg_color = GetSysColor(read_only_ ? COLOR_3DFACE : COLOR_WINDOW);
- edit_->SetBackgroundColor(bg_color);
+void Textfield::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+ if (is_add && !native_wrapper_ && GetWidget() && !initialized_) {
+ initialized_ = true;
+ native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this);
+ //AddChildView(native_wrapper_->GetView());
+ // TODO(beng): Move this initialization to NativeTextfieldWin once it
+ // subclasses NativeControlWin.
+ native_wrapper_->UpdateText();
+ native_wrapper_->UpdateBackgroundColor();
+ native_wrapper_->UpdateReadOnly();
+ native_wrapper_->UpdateFont();
+ native_wrapper_->UpdateEnabled();
+ native_wrapper_->UpdateBorder();
+ }
+}
+
+std::string Textfield::GetClassName() const {
+ return kViewClassName;
}
} // namespace views