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 |