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

Side by Side Diff: views/controls/textfield/native_textfield_views.cc

Issue 5857002: no native implementation of Textfield. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: remove selection_end and add tests 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "views/controls/textfield/native_textfield_views.h"
6
7 #include <algorithm>
8
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/utf_string_conversions.h"
13 #include "gfx/canvas.h"
14 #include "gfx/canvas_skia.h"
15 #include "gfx/insets.h"
16 #include "views/background.h"
17 #include "views/border.h"
18 #include "views/controls/textfield/native_textfield_gtk.h"
19 #include "views/controls/textfield/textfield.h"
20 #include "views/controls/textfield/textfield_views_model.h"
21 #include "views/event.h"
22
23 namespace {
24
25 // A global flag to switch the Textfield wrapper to TextfieldViews.
26 bool textfield_view_enabled = false;
27
28 // Color setttings for text, border, backgrounds and cursor.
29 // These are tentative, and should be derived from theme, system
30 // settings and current settings.
31 const SkColor kSelectedTextColor = SK_ColorWHITE;
32 const SkColor kReadonlyTextColor = SK_ColorDKGRAY;
33 const SkColor kFocusedSelectionColor = SK_ColorBLUE;
34 const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
35 const SkColor kFocusedBorderColor = SK_ColorCYAN;
36 const SkColor kDefaultBorderColor = SK_ColorGRAY;
37 const SkColor kCursorColor = SK_ColorBLACK;
38
39 // Parameters to control cursor blinking.
40 const int kCursorVisibleTimeMs = 800;
41 const int kCursorInvisibleTimeMs = 500;
42
43 // A switch to enable NativeTextfieldViews;
44 const char kEnableViewsBasedTextfieldSwitch[] = "enable-textfield-view";
45 } // namespace
46
47 namespace views {
48
49 const char NativeTextfieldViews::kViewClassName[] = "views/NativeTextfieldViews" ;
sky 2010/12/16 22:43:04 nit: > 80
oshima 2010/12/16 23:50:02 Done.
50
51 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent)
52 : textfield_(parent),
53 model_(new TextfieldViewsModel()),
54 text_border_(new TextfieldBorder()),
55 text_offset_(0),
56 insert_(true),
57 is_cursor_visible_(false),
58 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)) {
59 SetFocusable(true);
60 set_border(text_border_);
61
62 // Multiline is not supported.
63 DCHECK_NE(parent->style(), Textfield::STYLE_MULTILINE);
64 // Lowercase is not supported.
65 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE);
66 }
67
68 NativeTextfieldViews::~NativeTextfieldViews() {
69 }
70
71 ////////////////////////////////////////////////////////////////////////////////
72 // NativeTextfieldViews, View overrides:
73
74 bool NativeTextfieldViews::OnMousePressed(const views::MouseEvent& e) {
75 RequestFocus();
76 size_t pos = FindCursorPosition(e.location());
77 if (model_->MoveCursorTo(pos, false)) {
78 UpdateCursorBoundsAndTextOffset();
79 SchedulePaint();
80 }
81 return true;
82 }
83
84 bool NativeTextfieldViews::OnMouseDragged(const views::MouseEvent& e) {
85 size_t pos = FindCursorPosition(e.location());
86 if (model_->MoveCursorTo(pos, true)) {
87 UpdateCursorBoundsAndTextOffset();
88 SchedulePaint();
89 }
90 return true;
91 }
92
93 void NativeTextfieldViews::OnMouseReleased(const views::MouseEvent& e,
94 bool canceled) {
95 }
96
97 bool NativeTextfieldViews::OnKeyPressed(const views::KeyEvent& e) {
98 Textfield::Controller* controller = textfield_->GetController();
99 bool handled = false;
100 if (controller) {
101 Textfield::Keystroke ks(&e);
102 handled = controller->HandleKeystroke(textfield_, ks);
103 }
104 handled = handled || HandleKeyEvent(e);
sky 2010/12/16 22:43:04 No point in assigning and just returning. Make 104
oshima 2010/12/16 23:50:02 Done.
105 return handled;
106 }
107
108 bool NativeTextfieldViews::OnKeyReleased(const views::KeyEvent& e) {
109 return true;
110 }
111
112 void NativeTextfieldViews::Paint(gfx::Canvas* canvas) {
113 text_border_->set_has_focus(HasFocus());
114 PaintBackground(canvas);
115 PaintTextAndCursor(canvas);
116 if (textfield_->draw_border())
117 PaintBorder(canvas);
118 }
119
120 void NativeTextfieldViews::WillGainFocus() {
121 }
122
123 void NativeTextfieldViews::DidGainFocus() {
124 is_cursor_visible_ = true;
125 SchedulePaint();
126 // Start blinking cursor.
127 MessageLoop::current()->PostDelayedTask(
sky 2010/12/16 22:43:04 Any reason you're not using a RepeatingTimer?
oshima 2010/12/16 23:50:02 because its delay is alternativing 800 and 500 ms
128 FROM_HERE,
129 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor),
130 800);
sky 2010/12/16 22:43:04 make this is a constant.
oshima 2010/12/16 23:50:02 Overlooked. Done.
131 }
132
133 void NativeTextfieldViews::WillLoseFocus() {
134 // Stop blinking cursor.
135 cursor_timer_.RevokeAll();
136 if (is_cursor_visible_) {
137 is_cursor_visible_ = false;
138 RepaintCursor();
139 }
140 }
141
142 void NativeTextfieldViews::DidChangeBounds(const gfx::Rect& previous,
143 const gfx::Rect& current) {
144 UpdateCursorBoundsAndTextOffset();
145 }
146
147 /////////////////////////////////////////////////////////////////
148 // NativeTextfieldViews, NativeTextifieldWrapper overrides:
149
150 string16 NativeTextfieldViews::GetText() const {
151 return model_->text();
152 }
153
154 void NativeTextfieldViews::UpdateText() {
155 bool changed = model_->SetText(textfield_->text());
156 UpdateCursorBoundsAndTextOffset();
157 SchedulePaint();
158 if (changed) {
159 Textfield::Controller* controller = textfield_->GetController();
sky 2010/12/16 22:43:04 Are you sure we want to notify the controller here
oshima 2010/12/16 23:50:02 gtk is using "changed" signal, which is called whe
160 if (controller)
161 controller->ContentsChanged(textfield_, GetText());
162 }
163 }
164
165 void NativeTextfieldViews::AppendText(const string16& text) {
166 if (text.empty())
167 return;
168 model_->Append(text);
169 UpdateCursorBoundsAndTextOffset();
170 SchedulePaint();
171
172 Textfield::Controller* controller = textfield_->GetController();
173 if (controller)
174 controller->ContentsChanged(textfield_, GetText());
175 }
176
177 string16 NativeTextfieldViews::GetSelectedText() const {
178 return model_->GetSelectedText();
179 }
180
181 void NativeTextfieldViews::SelectAll() {
182 model_->SelectAll();
183 SchedulePaint();
184 }
185
186 void NativeTextfieldViews::ClearSelection() {
187 model_->ClearSelection();
188 SchedulePaint();
189 }
190
191 void NativeTextfieldViews::UpdateBorder() {
192 if (textfield_->draw_border()) {
193 gfx::Insets insets = GetInsets();
194 textfield_->SetHorizontalMargins(insets.left(), insets.right());
195 textfield_->SetVerticalMargins(insets.top(), insets.bottom());
196 } else {
197 textfield_->SetHorizontalMargins(0, 0);
198 textfield_->SetVerticalMargins(0, 0);
199 }
200 }
201
202 void NativeTextfieldViews::UpdateTextColor() {
203 SchedulePaint();
204 }
205
206 void NativeTextfieldViews::UpdateBackgroundColor() {
207 // TODO(oshima): Background has to match the border's shape.
208 set_background(
209 Background::CreateSolidBackground(textfield_->background_color()));
210 SchedulePaint();
211 }
212
213 void NativeTextfieldViews::UpdateReadOnly() {
214 SchedulePaint();
215 }
216
217 void NativeTextfieldViews::UpdateFont() {
218 UpdateCursorBoundsAndTextOffset();
219 }
220
221 void NativeTextfieldViews::UpdateIsPassword() {
222 model_->set_is_password(textfield_->IsPassword());
223 UpdateCursorBoundsAndTextOffset();
224 SchedulePaint();
225 }
226
227 void NativeTextfieldViews::UpdateEnabled() {
228 SchedulePaint();
229 }
230
231 bool NativeTextfieldViews::IsPassword() {
232 // looks unnecessary. should we remove?
233 NOTREACHED();
234 return false;
235 }
236
237 gfx::Insets NativeTextfieldViews::CalculateInsets() {
238 return GetInsets();
239 }
240
241 void NativeTextfieldViews::UpdateHorizontalMargins() {
242 int left, right;
243 if (!textfield_->GetHorizontalMargins(&left, &right))
244 return;
245 gfx::Insets inset = GetInsets();
246
247 text_border_->SetInsets(inset.top(), left, inset.bottom(), right);
248 UpdateCursorBoundsAndTextOffset();
249 }
250
251 void NativeTextfieldViews::UpdateVerticalMargins() {
252 int top, bottom;
253 if (!textfield_->GetVerticalMargins(&top, &bottom))
254 return;
255 gfx::Insets inset = GetInsets();
256
257 text_border_->SetInsets(top, inset.left(), bottom, inset.right());
258 UpdateCursorBoundsAndTextOffset();
259 }
260
261 void NativeTextfieldViews::SetFocus() {
262 RequestFocus();
263 }
264
265 View* NativeTextfieldViews::GetView() {
266 return this;
267 }
268
269 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const {
270 NOTREACHED();
271 return NULL;
272 }
273
274 bool NativeTextfieldViews::IsIMEComposing() const {
275 return false;
276 }
277
278 // static
279 bool NativeTextfieldViews::IsTextfieldViewsEnabled() {
280 #if defined(TOUCH_UI)
281 return true;
282 #else
283 return textfield_view_enabled ||
284 CommandLine::ForCurrentProcess()->HasSwitch(
285 kEnableViewsBasedTextfieldSwitch);
286 #endif
287 }
288
289 // static
290 void NativeTextfieldViews::SetEnableTextfieldViews(bool enabled) {
291 textfield_view_enabled = enabled;
292 }
293
294
295 ///////////////////////////////////////////////////////////////////////////////
296 // NativeTextfieldViews private:
297
298 const gfx::Font& NativeTextfieldViews::GetFont() const {
299 return textfield_->font();
300 }
301
302 SkColor NativeTextfieldViews::GetTextColor() const {
303 return textfield_->text_color();
304 }
305
306 void NativeTextfieldViews::UpdateCursor() {
307 is_cursor_visible_ = !is_cursor_visible_;
308 RepaintCursor();
309 MessageLoop::current()->PostDelayedTask(
310 FROM_HERE,
311 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor),
312 is_cursor_visible_ ? kCursorVisibleTimeMs : kCursorInvisibleTimeMs);
313 }
314
315 void NativeTextfieldViews::RepaintCursor() {
316 gfx::Rect r = cursor_bounds_;
317 r.Inset(-1, -1, -1, -1);
318 SchedulePaint(r, false);
319 }
320
321 void NativeTextfieldViews::UpdateCursorBoundsAndTextOffset() {
322 if (bounds().IsEmpty())
323 return;
324
325 gfx::Insets insets = GetInsets();
326
327 int width = bounds().width() - insets.width();
328
329 // TODO(oshima): bidi
330 const gfx::Font& font = GetFont();
331 int full_width = font.GetStringWidth(UTF16ToWide(model_->GetVisibleText()));
332 cursor_bounds_ = model_->GetCursorBounds(font);
333 cursor_bounds_.set_y(cursor_bounds_.y() + insets.top());
334
335 int x_right = text_offset_ + cursor_bounds_.right();
336 int x_left = text_offset_ + cursor_bounds_.x();
337
338 if (full_width < width) {
339 // Show all text whenever the text fits to the size.
340 text_offset_ = 0;
341 } else if (x_right > width) {
342 // when the cursor overflows to the right
343 text_offset_ = width - cursor_bounds_.right();
344 } else if (x_left < 0) {
345 // when the cursor overflows to the left
346 text_offset_ = -cursor_bounds_.x();
347 } else if(full_width > width && text_offset_ + full_width < width) {
348 // when the cursor moves within the textfield with the text
349 // longer than the field.
350 text_offset_ = width - full_width;
351 } else {
352 // move cursor freely.
353 }
354 // shift cursor bounds to fit insets.
355 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left());
356 }
357
358 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) {
359 gfx::Insets insets = GetInsets();
360
361 canvas->Save();
362 canvas->ClipRectInt(insets.left(), insets.top(),
363 width() - insets.width(), height() - insets.height());
364
365 // TODO(oshima): bidi support
366 // TODO(varunjain): re-implement this so only that dirty text is painted.
367 TextfieldViewsModel::TextFragments fragments;
368 model_->GetFragments(&fragments);
369 int x_offset = text_offset_ + insets.left();
370 int y = insets.top();
371 int text_height = height() - insets.height();
372 SkColor selection_color =
373 HasFocus() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
374 SkColor text_color =
375 textfield_->read_only() ? kReadonlyTextColor : GetTextColor();
376
377 for (TextfieldViewsModel::TextFragments::const_iterator iter =
378 fragments.begin();
379 iter != fragments.end();
380 iter++) {
381 string16 text = model_->GetVisibleText((*iter).begin, (*iter).end);
382 // TODO(oshima): This does not give the accurate position due to
383 // kerning. Figure out how webkit does this with skia.
384 int width = GetFont().GetStringWidth(UTF16ToWide(text));
385
386 if ((*iter).selected) {
387 canvas->FillRectInt(selection_color, x_offset, y, width, text_height);
388 canvas->DrawStringInt(
389 UTF16ToWide(text), GetFont(), kSelectedTextColor,
390 x_offset, y, width, text_height);
391 } else {
392 canvas->DrawStringInt(
393 UTF16ToWide(text), GetFont(), text_color,
394 x_offset, y, width, text_height);
395 }
396 x_offset += width;
397 }
398 canvas->Restore();
399
400 if (textfield_->IsEnabled() && is_cursor_visible_ &&
401 !model_->HasSelection()) {
402 // Paint Cursor. Replace cursor is drawn as rectangle for now.
403 canvas->DrawRectInt(kCursorColor,
404 cursor_bounds_.x(),
405 cursor_bounds_.y(),
406 insert_ ? 0 : cursor_bounds_.width(),
407 cursor_bounds_.height());
408 }
409 }
410
411 bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
412 // TODO(oshima): handle IME.
413 if (key_event.GetType() == views::Event::ET_KEY_PRESSED) {
414 app::KeyboardCode key_code = key_event.GetKeyCode();
415 // TODO(oshima): shift-tab does not work. Figure out why and fix.
416 if (key_code == app::VKEY_TAB)
417 return false;
418 bool selection = key_event.IsShiftDown();
419 bool control = key_event.IsControlDown();
420 bool text_changed = false;
421 bool cursor_changed = false;
422 switch (key_code) {
423 case app::VKEY_A:
424 if (control) {
425 model_->SelectAll();
426 cursor_changed = true;
427 }
428 break;
429 case app::VKEY_RIGHT:
430 control ? model_->MoveCursorToNextWord(selection)
431 : model_->MoveCursorRight(selection);
432 cursor_changed = true;
433 break;
434 case app::VKEY_LEFT:
435 control ? model_->MoveCursorToPreviousWord(selection)
436 : model_->MoveCursorLeft(selection);
437 cursor_changed = true;
438 break;
439 case app::VKEY_END:
440 model_->MoveCursorToEnd(selection);
441 cursor_changed = true;
442 break;
443 case app::VKEY_HOME:
444 model_->MoveCursorToStart(selection);
445 cursor_changed = true;
446 break;
447 case app::VKEY_BACK:
448 text_changed = model_->Backspace();
449 cursor_changed = true;
450 break;
451 case app::VKEY_DELETE:
452 text_changed = model_->Delete();
453 break;
454 case app::VKEY_INSERT:
455 insert_ = !insert_;
456 cursor_changed = true;
457 break;
458 default:
459 break;
460 }
461 char16 print_char = GetPrintableChar(key_event);
462 if (!control && print_char) {
463 if (insert_)
464 model_->Insert(print_char);
465 else
466 model_->Replace(print_char);
467 text_changed = true;
468 }
469 if (text_changed) {
470 textfield_->SyncText();
471 Textfield::Controller* controller = textfield_->GetController();
472 if (controller)
473 controller->ContentsChanged(textfield_, GetText());
474 }
475 if (text_changed || cursor_changed) {
476 UpdateCursorBoundsAndTextOffset();
477 SchedulePaint();
478 }
479 }
480 return false;
481 }
482
483 char16 NativeTextfieldViews::GetPrintableChar(const KeyEvent& key_event) {
484 // TODO(oshima): IME, i18n support.
485 // This only works for UCS-2 characters.
486 app::KeyboardCode key_code = key_event.GetKeyCode();
487 bool shift = key_event.IsShiftDown() ^ key_event.IsCapsLockDown();
488 // TODO(oshima): We should have a utility function
489 // under app to convert a KeyboardCode to a printable character,
490 // probably in keyboard_code_conversion{.h, _x
491 switch (key_code) {
492 case app::VKEY_NUMPAD0:
493 return '0';
494 case app::VKEY_NUMPAD1:
495 return '1';
496 case app::VKEY_NUMPAD2:
497 return '2';
498 case app::VKEY_NUMPAD3:
499 return '3';
500 case app::VKEY_NUMPAD4:
501 return '4';
502 case app::VKEY_NUMPAD5:
503 return '5';
504 case app::VKEY_NUMPAD6:
505 return '6';
506 case app::VKEY_NUMPAD7:
507 return '7';
508 case app::VKEY_NUMPAD8:
509 return '8';
510 case app::VKEY_NUMPAD9:
511 return '9';
512 case app::VKEY_MULTIPLY:
513 return '*';
514 case app::VKEY_ADD:
515 return '+';
516 case app::VKEY_SUBTRACT:
517 return '-';
518 case app::VKEY_DECIMAL:
519 return '.';
520 case app::VKEY_DIVIDE:
521 return '/';
522 case app::VKEY_SPACE:
523 return ' ';
524 case app::VKEY_0:
525 return shift ? ')' : '0';
526 case app::VKEY_1:
527 return shift ? '!' : '1';
528 case app::VKEY_2:
529 return shift ? '@' : '2';
530 case app::VKEY_3:
531 return shift ? '#' : '3';
532 case app::VKEY_4:
533 return shift ? '$' : '4';
534 case app::VKEY_5:
535 return shift ? '%' : '5';
536 case app::VKEY_6:
537 return shift ? '^' : '6';
538 case app::VKEY_7:
539 return shift ? '&' : '7';
540 case app::VKEY_8:
541 return shift ? '*' : '8';
542 case app::VKEY_9:
543 return shift ? '(' : '9';
544
545 case app::VKEY_A:
546 case app::VKEY_B:
547 case app::VKEY_C:
548 case app::VKEY_D:
549 case app::VKEY_E:
550 case app::VKEY_F:
551 case app::VKEY_G:
552 case app::VKEY_H:
553 case app::VKEY_I:
554 case app::VKEY_J:
555 case app::VKEY_K:
556 case app::VKEY_L:
557 case app::VKEY_M:
558 case app::VKEY_N:
559 case app::VKEY_O:
560 case app::VKEY_P:
561 case app::VKEY_Q:
562 case app::VKEY_R:
563 case app::VKEY_S:
564 case app::VKEY_T:
565 case app::VKEY_U:
566 case app::VKEY_V:
567 case app::VKEY_W:
568 case app::VKEY_X:
569 case app::VKEY_Y:
570 case app::VKEY_Z:
571 return (shift ? 'A' : 'a') + (key_code - app::VKEY_A);
572 case app::VKEY_OEM_1:
573 return shift ? ':' : ';';
574 case app::VKEY_OEM_PLUS:
575 return shift ? '+' : '=';
576 case app::VKEY_OEM_COMMA:
577 return shift ? '<' : ',';
578 case app::VKEY_OEM_MINUS:
579 return shift ? '_' : '-';
580 case app::VKEY_OEM_PERIOD:
581 return shift ? '>' : '.';
582 case app::VKEY_OEM_2:
583 return shift ? '?' : '/';
584 case app::VKEY_OEM_3:
585 return shift ? '~' : '`';
586 case app::VKEY_OEM_4:
587 return shift ? '}' : ']';
588 case app::VKEY_OEM_5:
589 return shift ? '|' : '\\';
590 case app::VKEY_OEM_6:
591 return shift ? '{' : '[';
592 case app::VKEY_OEM_7:
593 return shift ? '"' : '\'';
594 default:
595 return 0;
596 }
597 }
598
599 size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const {
600 // TODO(oshima): BIDI/i18n support.
601 gfx::Font font = GetFont();
sky 2010/12/16 22:43:04 Doesn't this need to take into consideration text_
oshima 2010/12/16 23:50:02 good catch. Thanks. done.
602 gfx::Insets insets = GetInsets();
603 std::wstring text = UTF16ToWide(model_->GetVisibleText());
604 int left = 0;
605 int left_pos = 0;
606 int right = font.GetStringWidth(text);
607 int right_pos = text.length();
608
609 int x = point.x() - insets.left();
610 if (x <= left) return left_pos;
611 if (x >= right) return right_pos;
612 // binary searching the cursor position.
613 // TODO(oshima): use the center of character instead of edge.
614 // Binary search may not work for language like arabic.
615 while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) {
616 int pivot_pos = left_pos + (right_pos - left_pos) / 2;
617 int pivot = font.GetStringWidth(text.substr(0, pivot_pos));
618 if (pivot < x) {
619 left = pivot;
620 left_pos = pivot_pos;
621 } if (pivot == x) {
622 return pivot_pos;
623 } else {
624 right = pivot;
625 right_pos = pivot_pos;
626 }
627 }
628 return left_pos;
629 }
630
631 ///////////////////////////////////////////////////////////////////////////////
632 // NativeTextfieldWrapper:
633
634 // static
635 NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper(
636 Textfield* field) {
637 if (NativeTextfieldViews::IsTextfieldViewsEnabled()) {
638 return new NativeTextfieldViews(field);
639 } else {
640 return new NativeTextfieldGtk(field);
641 }
642 }
643
644 ///////////////////////////////////////////////////////////////////////////////
645 //
646 // TextifieldBorder
647 //
648 ///////////////////////////////////////////////////////////////////////////////
649
650 NativeTextfieldViews::TextfieldBorder::TextfieldBorder()
651 : insets_(4, 4, 4, 4) {
sky 2010/12/16 22:43:04 Initialize has_focus_ to false
oshima 2010/12/16 23:50:02 Done.
652 }
653
654 void NativeTextfieldViews::TextfieldBorder::Paint(
655 const View& view, gfx::Canvas* canvas) const {
656 SkRect rect;
657 rect.set(SkIntToScalar(0), SkIntToScalar(0),
658 SkIntToScalar(view.width()), SkIntToScalar(view.height()));
659 SkScalar corners[8] = {
660 // top-left
661 insets_.left(),
662 insets_.top(),
663 // top-right
664 insets_.right(),
665 insets_.top(),
666 // bottom-right
667 insets_.right(),
668 insets_.bottom(),
669 // bottom-left
670 insets_.left(),
671 insets_.bottom(),
672 };
673 SkPath path;
674 path.addRoundRect(rect, corners);
675 SkPaint paint;
676 paint.setStyle(SkPaint::kStroke_Style);
677 paint.setFlags(SkPaint::kAntiAlias_Flag);
678 // TODO(oshima): Copy what WebKit does for focused border.
679 paint.setColor(has_focus_ ? kFocusedBorderColor : kDefaultBorderColor);
680 paint.setStrokeWidth(has_focus_ ? 2 : 1);
681
682 canvas->AsCanvasSkia()->drawPath(path, paint);
683 }
684
685 void NativeTextfieldViews::TextfieldBorder::GetInsets(gfx::Insets* insets) const
686 {
687 *insets = insets_;
688 }
689
690 void NativeTextfieldViews::TextfieldBorder::SetInsets(int top,
691 int left,
692 int bottom,
693 int right) {
694 insets_.Set(top, left, bottom, right);
695 }
696
697 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698