Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Unified Diff: views/controls/textfield/native_textfield_view.cc

Issue 5857002: no native implementation of Textfield. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: " Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: views/controls/textfield/native_textfield_view.cc
diff --git a/views/controls/textfield/native_textfield_view.cc b/views/controls/textfield/native_textfield_view.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5296191cbb3cdc33419d73ad5f8497d4f4d715c3
--- /dev/null
+++ b/views/controls/textfield/native_textfield_view.cc
@@ -0,0 +1,702 @@
+// 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/native_textfield_view.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/canvas.h"
+#include "gfx/canvas_skia.h"
+#include "gfx/insets.h"
+#include "views/background.h"
+#include "views/border.h"
+#include "views/controls/textfield/native_textfield_gtk.h"
+#include "views/controls/textfield/textfield.h"
+#include "views/controls/textfield/textfield_view_model.h"
+#include "views/event.h"
+
+namespace {
+
+// A global flag to switch the Textfield wrapper to TextfieldView.
+bool textfield_view_enabled = false;
+
+// Color setttings for text, border, backgrounds and cursor.
+// These are tentative, and should be derived from theme, system
+// settings and current settings.
+const SkColor kSelectedTextColor = SK_ColorWHITE;
+const SkColor kReadonlyTextColor = SK_ColorDKGRAY;
+const SkColor kFocusedSelectionColor = SK_ColorBLUE;
+const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
+const SkColor kFocusedBorderColor = SK_ColorCYAN;
+const SkColor kDefaultBorderColor = SK_ColorGRAY;
+const SkColor kCursorColor = SK_ColorBLACK;
+
+} // namespace
+
+namespace views {
+
+const char NativeTextfieldView::kViewClassName[] = "views/NativeTextfieldView";
+
+NativeTextfieldView::NativeTextfieldView(Textfield* parent)
+ : textfield_(parent),
+ model_(new TextfieldViewModel()),
+ text_border_(new TextfieldBorder()),
+ text_offset_(0),
+ insert_(true),
+ cursor_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)) {
+ SetFocusable(true);
+ set_border(text_border_);
+
+ // Multiline is not supported.
+ DCHECK_NE(parent->style(), Textfield::STYLE_MULTILINE);
+ // Lowercase is not supported.
+ DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE);
+}
+
+NativeTextfieldView::~NativeTextfieldView() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeTextfieldView, View overrides:
+
+bool NativeTextfieldView::SkipDefaultKeyEventProcessing(
+ const views::KeyEvent& e) {
+ return false;
sky 2010/12/15 20:31:27 This is the default return value. Did you mean to
oshima 2010/12/16 01:15:19 No, I was going to remove this, but forgot to do s
+}
+
+bool NativeTextfieldView::OnMousePressed(const views::MouseEvent& e) {
+ RequestFocus();
+ size_t pos = FindCursorPosition(e.location());
+ if (model_->MoveCursorTo(pos, false)) {
sky 2010/12/15 20:31:27 What about a double click?
oshima 2010/12/16 01:15:19 Not yet supported. I'll implement it in follow up
+ UpdateCursorBoundsAndTextOffset();
+ SchedulePaint();
+ }
+ return true;
+}
+
+bool NativeTextfieldView::OnMouseDragged(const views::MouseEvent& e) {
+ size_t pos = FindCursorPosition(e.location());
+ if (model_->MoveCursorTo(pos, true)) {
+ UpdateCursorBoundsAndTextOffset();
+ SchedulePaint();
+ }
+ return true;
+}
+
+void NativeTextfieldView::OnMouseReleased(const views::MouseEvent& e,
+ bool canceled) {
+}
+
+bool NativeTextfieldView::OnKeyPressed(const views::KeyEvent& e) {
+ Textfield::Controller* controller = textfield_->GetController();
+ bool handled = false;
+ if (controller) {
+ Textfield::Keystroke ks(&e);
+ handled = controller->HandleKeystroke(textfield_, ks);
+ }
+ handled = handled || HandleKeyEvent(e);
+ return handled;
+}
+
+bool NativeTextfieldView::OnKeyReleased(const views::KeyEvent& e) {
+ return true;
+}
+
+void NativeTextfieldView::Paint(gfx::Canvas* canvas) {
+ text_border_->set_has_focus(HasFocus());
+ PaintBackground(canvas);
+ PaintTextAndCursor(canvas);
+ if (textfield_->draw_border())
+ PaintBorder(canvas);
+}
+
+void NativeTextfieldView::WillGainFocus() {
+}
+
+void NativeTextfieldView::DidGainFocus() {
+ cursor_ = true;
+ SchedulePaint();
+ // Start blinking cursor.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ cursor_timer_.NewRunnableMethod(&NativeTextfieldView::UpdateCursor),
+ 800);
+}
+
+void NativeTextfieldView::WillLoseFocus() {
+ // Stop blinking cursor.
+ cursor_timer_.RevokeAll();
+ if (cursor_) {
+ cursor_ = false;
+ RepaintCursor();
+ }
+}
+
+void NativeTextfieldView::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+ UpdateCursorBoundsAndTextOffset();
+}
+
+
+/////////////////////////////////////////////////////////////////
+// NativeTextfieldView, NativeTextifieldWrapper overrides:
+
+string16 NativeTextfieldView::GetText() const {
+ return model_->text();
+}
+
+void NativeTextfieldView::UpdateText() {
+ bool changed = model_->SetText(textfield_->text());
+ UpdateCursorBoundsAndTextOffset();
+ SchedulePaint();
+ if (changed) {
+ Textfield::Controller* controller = textfield_->GetController();
+ if (controller)
+ controller->ContentsChanged(textfield_, GetText());
+ }
+}
+
+void NativeTextfieldView::AppendText(const string16& text) {
+ if (text.empty())
+ return;
+ model_->Append(text);
+ UpdateCursorBoundsAndTextOffset();
+ SchedulePaint();
+
+ Textfield::Controller* controller = textfield_->GetController();
+ if (controller)
+ controller->ContentsChanged(textfield_, GetText());
+}
+
+string16 NativeTextfieldView::GetSelectedText() const {
+ return model_->GetSelectedText();
+}
+
+void NativeTextfieldView::SelectAll() {
+ model_->SelectAll();
+ SchedulePaint();
+}
+
+void NativeTextfieldView::ClearSelection() {
+ model_->ClearSelection();
+ SchedulePaint();
+}
+
+void NativeTextfieldView::UpdateBorder() {
+ if (textfield_->draw_border()) {
+ gfx::Insets insets = GetInsets();
+ textfield_->SetHorizontalMargins(insets.left(), insets.right());
+ textfield_->SetVerticalMargins(insets.top(), insets.bottom());
+ } else {
+ textfield_->SetHorizontalMargins(0, 0);
+ textfield_->SetVerticalMargins(0, 0);
+ }
+}
+
+void NativeTextfieldView::UpdateTextColor() {
+ SchedulePaint();
+}
+
+void NativeTextfieldView::UpdateBackgroundColor() {
+ // TODO(oshima): Background has to match the border's shape.
+ set_background(
+ Background::CreateSolidBackground(textfield_->background_color()));
+ SchedulePaint();
+}
+
+void NativeTextfieldView::UpdateReadOnly() {
+ SchedulePaint();
+}
+
+void NativeTextfieldView::UpdateFont() {
+ UpdateCursorBoundsAndTextOffset();
+}
+
+void NativeTextfieldView::UpdateIsPassword() {
+ model_->set_is_password(textfield_->IsPassword());
+ UpdateCursorBoundsAndTextOffset();
+ SchedulePaint();
+}
+
+void NativeTextfieldView::UpdateEnabled() {
+ SchedulePaint();
+}
+
+bool NativeTextfieldView::IsPassword() {
+ // looks unnecessary. should we remove?
+ NOTREACHED();
+ return false;
+}
+
+gfx::Insets NativeTextfieldView::CalculateInsets() {
+ return GetInsets();
+}
+
+void NativeTextfieldView::UpdateHorizontalMargins() {
+ int left, right;
+ if (!textfield_->GetHorizontalMargins(&left, &right))
+ return;
+ gfx::Insets inset = GetInsets();
+
+ text_border_->SetInsets(inset.top(), left, inset.bottom(), right);
+ UpdateCursorBoundsAndTextOffset();
+}
+
+void NativeTextfieldView::UpdateVerticalMargins() {
+ int top, bottom;
+ if (!textfield_->GetVerticalMargins(&top, &bottom))
+ return;
+ gfx::Insets inset = GetInsets();
+
+ text_border_->SetInsets(top, inset.left(), bottom, inset.right());
+ UpdateCursorBoundsAndTextOffset();
+}
+
+void NativeTextfieldView::SetFocus() {
+ RequestFocus();
+}
+
+View* NativeTextfieldView::GetView() {
+ return this;
+}
+
+gfx::NativeView NativeTextfieldView::GetTestingHandle() const {
+ NOTREACHED();
+ return NULL;
+}
+
+bool NativeTextfieldView::IsIMEComposing() const {
+ return false;
+}
+
+// static
+bool NativeTextfieldView::IsTextfieldViewEnabled() {
+ return textfield_view_enabled;
+}
+
+// static
+void NativeTextfieldView::SetEnableTextfieldView(bool enabled) {
+ textfield_view_enabled = enabled;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// NativeTextfieldView private:
+
+gfx::Font NativeTextfieldView::GetFont() const {
+ return textfield_->font();
+}
+
+SkColor NativeTextfieldView::GetTextColor() const {
+ return textfield_->text_color();
+}
+
+
+void NativeTextfieldView::UpdateCursor() {
+ cursor_ = !cursor_;
+ RepaintCursor();
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ cursor_timer_.NewRunnableMethod(&NativeTextfieldView::UpdateCursor),
+ cursor_ ? 800 : 500);
+}
+
+void NativeTextfieldView::RepaintCursor() {
+ gfx::Rect r = cursor_bounds_;
+ r.Inset(-1, -1, -1, -1);
+ SchedulePaint(r, false);
+}
+
+void NativeTextfieldView::UpdateCursorBoundsAndTextOffset() {
+ if (bounds().IsEmpty()) return;
sky 2010/12/15 20:31:27 nit: don't do single ifs like this.
oshima 2010/12/16 01:15:19 Done.
+
+ gfx::Insets insets = GetInsets();
+
+ int width = bounds().width() - insets.width();
+
+ // TODO(oshima): bidi
+ gfx::Font font = GetFont();
sky 2010/12/15 20:31:27 const gfx::Font&
oshima 2010/12/16 01:15:19 Done.
+ int full_width = font.GetStringWidth(UTF16ToWide(model_->GetVisibleText()));
+ cursor_bounds_ = model_->GetCursorBounds(font);
+ cursor_bounds_.set_y(cursor_bounds_.y() + insets.top());
+
+ int x_right = text_offset_ + cursor_bounds_.right();
+ int x_left = text_offset_ + cursor_bounds_.x();
+
+ if (full_width < width) {
+ // Show all text whenever the text fits to the size.
+ text_offset_ = 0;
+ } else if (x_right > width) {
+ // when the cursor overflows to the right
+ text_offset_ = width - cursor_bounds_.right();
sky 2010/12/15 20:31:27 Wouldn't this trigger scrolling on every cursor ch
oshima 2010/12/16 01:15:19 This is executed only if the cursor moves out of t
+ } else if (x_left < 0) {
+ // when the cursor overflows to the left
+ text_offset_ = -cursor_bounds_.x();
+ } else if(full_width > width && text_offset_ + full_width < width) {
+ // when the cursor moves within the textfield with the text
+ // longer than the field.
+ text_offset_ = width - full_width;
+ } else {
+ // move cursor freely.
+ }
+ // shift cursor bounds to fit insets.
+ cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left());
+}
+
+void NativeTextfieldView::PaintTextAndCursor(gfx::Canvas* canvas) {
+ gfx::Insets insets = GetInsets();
+
+ canvas->Save();
+ canvas->ClipRectInt(insets.left(), insets.top(),
+ width() - insets.width(), height() - insets.height());
+
+ // TODO(oshima): bidi support
+ // TODO(varunjain): re-implement this so only that dirty text is painted.
+ TextfieldViewModel::TextFragments fragments;
+ model_->PopulateFragments(&fragments);
+ int x_offset = text_offset_ + insets.left();
+ int y = insets.top();
+ int text_height = height() - insets.height();
+ SkColor selection_color =
+ HasFocus() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
+ SkColor text_color =
+ textfield_->read_only() ? kReadonlyTextColor : GetTextColor();
+
+ for (TextfieldViewModel::TextFragments::const_iterator iter =
+ fragments.begin();
+ iter != fragments.end();
+ iter++) {
+ string16 text = model_->GetVisibleText((*iter).begin, (*iter).end);
sky 2010/12/15 20:31:27 iter->begin, iter->end
oshima 2010/12/16 01:15:19 It does not work. * is an operator that returns Te
+ // TODO(oshima): This does not give the accurate position due to
+ // kerning. Figure out how webkit does this with skia.
+ int width = GetFont().GetStringWidth(UTF16ToWide(text));
+
+ if ((*iter).selected) {
sky 2010/12/15 20:31:27 iter->selected
oshima 2010/12/16 01:15:19 same here.
+ canvas->FillRectInt(selection_color, x_offset, y, width, text_height);
+ canvas->DrawStringInt(
+ UTF16ToWide(text), GetFont(), kSelectedTextColor,
+ x_offset, y, width, text_height);
+ } else {
+ canvas->DrawStringInt(
+ UTF16ToWide(text), GetFont(), text_color,
+ x_offset, y, width, text_height);
+ }
+ x_offset += width;
+ }
+ canvas->Restore();
+
+ if (textfield_->IsEnabled() && cursor_) {
+ // Paint Cursor. Replace cursor is drawn as rectangle for now.
+ canvas->DrawRectInt(kCursorColor,
+ cursor_bounds_.x(),
+ cursor_bounds_.y(),
+ insert_ ? 0 : cursor_bounds_.width(),
+ cursor_bounds_.height());
+ }
+}
+
+bool NativeTextfieldView::HandleKeyEvent(const KeyEvent& key_event) {
+ // TODO(oshima): handle IME.
+ if (key_event.GetType() == views::Event::ET_KEY_PRESSED) {
+ app::KeyboardCode key_code = key_event.GetKeyCode();
+ // TODO(oshima): shift-tab does not work. Figure out why and fix.
+ if (key_code == app::VKEY_TAB)
+ return false;
+ bool selection = key_event.IsShiftDown();
+ bool control = key_event.IsControlDown();
+ bool text_changed = false;
+ bool cursor_changed = false;
+ switch (key_code) {
+ case app::VKEY_A:
+ if (control) {
+ model_->SelectAll();
+ cursor_changed = true;
+ }
+ break;
+ case app::VKEY_RIGHT:
+ control ? model_->MoveCursorToNextWord(selection)
+ : model_->MoveCursorRight(selection);
+ cursor_changed = true;
+ break;
+ case app::VKEY_LEFT:
+ control ? model_->MoveCursorToPreviousWord(selection)
+ : model_->MoveCursorLeft(selection);
+ cursor_changed = true;
+ break;
+ case app::VKEY_END:
+ model_->MoveCursorToEnd(selection);
+ cursor_changed = true;
+ break;
+ case app::VKEY_HOME:
+ model_->MoveCursorToStart(selection);
+ cursor_changed = true;
+ break;
+ case app::VKEY_BACK:
+ text_changed = model_->Backspace();
+ cursor_changed = true;
+ break;
+ case app::VKEY_DELETE:
+ text_changed = model_->Delete();
+ break;
+ case app::VKEY_INSERT:
+ insert_ = !insert_;
+ cursor_changed = true;
+ break;
+ default:
+ break;
+ }
+ char16 print_char = GetPrintableChar(key_event);
+ if (!control && print_char) {
+ if (insert_)
+ model_->Insert(print_char);
+ else
+ model_->Replace(print_char);
+ text_changed = true;
+ }
+ if (text_changed) {
+ textfield_->SyncText();
+ Textfield::Controller* controller = textfield_->GetController();
+ if (controller)
+ controller->ContentsChanged(textfield_, GetText());
+ }
+ if (text_changed || cursor_changed) {
+ UpdateCursorBoundsAndTextOffset();
+ SchedulePaint();
+ }
+ }
+ return false;
+}
+
+char16 NativeTextfieldView::GetPrintableChar(const KeyEvent& key_event) {
+ // TODO(oshima): IME, i18n support.
+ 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) {
+#if defined(TOUCH_UI)
+ shift = (key_event.IsLockDown() && !key_event.IsShiftDown()) ||
+ (!key_event.IsLockDown() && key_event.IsShiftDown());
+#else
+ shift = key_event.IsShiftDown();
+#endif
+ }
+ if (key_event.IsCapsLockDown())
+ shift = !shift;
+ return GetPrintableChar(key_code, shift);
+}
+
+char16 NativeTextfieldView::GetPrintableChar(app::KeyboardCode key_code,
rjkroege 2010/12/15 21:23:31 Could this merge with keyboard_code_conversion_x.c
oshima 2010/12/16 01:15:19 That's xevent -> keyboard code right? what we need
+ bool shift) {
+ // TODO(oshima): We should have a utility function
+ // under app to convert a KeyboardCode to a printable character.
+ 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_SPACE:
+ return ' ';
+ 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_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 ? '"' : '\'';
+ default:
+ return 0;
+ }
+}
+
+size_t NativeTextfieldView::FindCursorPosition(const gfx::Point& point) const {
+ // TODO(oshima): BIDI
+ gfx::Font font = GetFont();
+ gfx::Insets insets = GetInsets();
+ std::wstring text = UTF16ToWide(model_->GetVisibleText());
+ int left = 0;
+ int left_pos = 0;
+ int right = font.GetStringWidth(text);
+ int right_pos = text.length();
+
+ int x = point.x() - insets.left();
+ if (x <= left) return left_pos;
+ if (x >= right) return right_pos;
+ // binary searching the cursor position.
+ // TODO(oshima): use the center of character instead of edge.
+ while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) {
rjkroege 2010/12/15 21:23:31 AFAIK the binary search approach won't necessarily
oshima 2010/12/16 01:15:19 Thank you for the info. Updated the comment above.
+ int pivot_pos = left_pos + (right_pos - left_pos) / 2;
+ int pivot = font.GetStringWidth(text.substr(0, pivot_pos));
+ if (pivot < x) {
+ left = pivot;
+ left_pos = pivot_pos;
+ } else {
sky 2010/12/15 20:31:27 What about == ?
oshima 2010/12/16 01:15:19 Added shortcut for == case.
+ right = pivot;
+ right_pos = pivot_pos;
+ }
+ }
+ return left_pos;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// NativeTextfieldWrapper:
+
+// static
+NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper(
+ Textfield* field) {
+ if (NativeTextfieldView::IsTextfieldViewEnabled()) {
+ return new NativeTextfieldView(field);
+ } else {
+ return new NativeTextfieldGtk(field);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// TextifieldBorder
+//
+///////////////////////////////////////////////////////////////////////////////
+
+NativeTextfieldView::TextfieldBorder::TextfieldBorder()
+ : insets_(4, 4, 4, 4) {
+}
+
+void NativeTextfieldView::TextfieldBorder::Paint(
+ const View& view, gfx::Canvas* canvas) const {
+ SkRect rect;
+ rect.set(SkIntToScalar(0), SkIntToScalar(0),
+ SkIntToScalar(view.width()), SkIntToScalar(view.height()));
+ SkScalar corners[8] = {
+ // top-left
+ insets_.left(),
+ insets_.top(),
+ // top-right
+ insets_.right(),
+ insets_.top(),
+ // bottom-right
+ insets_.right(),
+ insets_.bottom(),
+ // bottom-left
+ insets_.left(),
+ insets_.bottom(),
+ };
+ SkPath path;
+ path.addRoundRect(rect, corners);
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+ // TODO(oshima): Copy what WebKit does for focused border.
+ paint.setColor(has_focus_ ? kFocusedBorderColor : kDefaultBorderColor);
+ paint.setStrokeWidth(has_focus_ ? 3 : 1);
+
+ canvas->AsCanvasSkia()->drawPath(path, paint);
+}
+
+void NativeTextfieldView::TextfieldBorder::GetInsets(gfx::Insets* insets) const
+{
+ *insets = insets_;
+}
+
+void NativeTextfieldView::TextfieldBorder::SetInsets(int top,
+ int left,
+ int bottom,
+ int right) {
+ insets_.Set(top, left, bottom, right);
+}
+
+} // namespace views

Powered by Google App Engine
This is Rietveld 408576698