Chromium Code Reviews

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

Powered by Google App Engine