Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "views/controls/textfield/native_textfield_views.h" | 5 #include "views/controls/textfield/native_textfield_views.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 const char NativeTextfieldViews::kViewClassName[] = | 60 const char NativeTextfieldViews::kViewClassName[] = |
| 61 "views/NativeTextfieldViews"; | 61 "views/NativeTextfieldViews"; |
| 62 | 62 |
| 63 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) | 63 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) |
| 64 : textfield_(parent), | 64 : textfield_(parent), |
| 65 ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), | 65 ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), |
| 66 text_border_(new FocusableBorder()), | 66 text_border_(new FocusableBorder()), |
| 67 text_offset_(0), | 67 text_offset_(0), |
| 68 insert_(true), | 68 insert_(true), |
| 69 is_cursor_visible_(false), | 69 is_cursor_visible_(false), |
| 70 is_drop_cursor_visible_(false), | |
| 70 skip_input_method_cancel_composition_(false), | 71 skip_input_method_cancel_composition_(false), |
| 71 initiating_drag_(false), | 72 initiating_drag_(false), |
| 72 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), | 73 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), |
| 73 aggregated_clicks_(0), | 74 aggregated_clicks_(0), |
| 74 last_click_time_(base::Time::FromInternalValue(0)), | 75 last_click_time_(base::Time::FromInternalValue(0)), |
| 75 last_click_location_(0, 0) { | 76 last_click_location_(0, 0) { |
| 76 set_border(text_border_); | 77 set_border(text_border_); |
| 77 | 78 |
| 78 // Lowercase is not supported. | 79 // Lowercase is not supported. |
| 79 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); | 80 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 *formats = ui::OSExchangeData::STRING; | 171 *formats = ui::OSExchangeData::STRING; |
| 171 return true; | 172 return true; |
| 172 } | 173 } |
| 173 | 174 |
| 174 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { | 175 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { |
| 175 return textfield_->IsEnabled() && !textfield_->read_only() && | 176 return textfield_->IsEnabled() && !textfield_->read_only() && |
| 176 data.HasString(); | 177 data.HasString(); |
| 177 } | 178 } |
| 178 | 179 |
| 179 int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { | 180 int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { |
| 180 // TODO(msw): retain unfocused selection, render secondary cursor... | |
| 181 DCHECK(CanDrop(event.data())); | 181 DCHECK(CanDrop(event.data())); |
| 182 bool is_point_in_selection = IsPointInSelection(event.location()); | |
| 183 is_drop_cursor_visible_ = !is_point_in_selection; | |
| 184 drop_cursor_bounds_ = GetCursorBounds(FindCursorPosition(event.location())); | |
| 185 SchedulePaint(); | |
| 186 | |
| 182 if (initiating_drag_) { | 187 if (initiating_drag_) { |
| 183 if (IsPointInSelection(event.location())) | 188 if (is_point_in_selection) |
| 184 return ui::DragDropTypes::DRAG_NONE; | 189 return ui::DragDropTypes::DRAG_NONE; |
| 185 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : | 190 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : |
| 186 ui::DragDropTypes::DRAG_MOVE; | 191 ui::DragDropTypes::DRAG_MOVE; |
| 187 } | 192 } |
| 188 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; | 193 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; |
| 189 } | 194 } |
| 190 | 195 |
| 191 int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { | 196 int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { |
| 192 DCHECK(CanDrop(event.data())); | 197 DCHECK(CanDrop(event.data())); |
| 193 DCHECK(!initiating_drag_ || !IsPointInSelection(event.location())); | 198 DCHECK(!initiating_drag_ || !IsPointInSelection(event.location())); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 216 model_->InsertText(text); | 221 model_->InsertText(text); |
| 217 } | 222 } |
| 218 skip_input_method_cancel_composition_ = false; | 223 skip_input_method_cancel_composition_ = false; |
| 219 UpdateAfterChange(true, true); | 224 UpdateAfterChange(true, true); |
| 220 OnAfterUserAction(); | 225 OnAfterUserAction(); |
| 221 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; | 226 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; |
| 222 } | 227 } |
| 223 | 228 |
| 224 void NativeTextfieldViews::OnDragDone() { | 229 void NativeTextfieldViews::OnDragDone() { |
| 225 initiating_drag_ = false; | 230 initiating_drag_ = false; |
| 231 is_drop_cursor_visible_ = false; | |
| 226 } | 232 } |
| 227 | 233 |
| 228 void NativeTextfieldViews::OnPaint(gfx::Canvas* canvas) { | 234 void NativeTextfieldViews::OnPaint(gfx::Canvas* canvas) { |
| 229 text_border_->set_has_focus(textfield_->HasFocus()); | 235 text_border_->set_has_focus(textfield_->HasFocus()); |
| 230 OnPaintBackground(canvas); | 236 OnPaintBackground(canvas); |
| 231 PaintTextAndCursor(canvas); | 237 PaintTextAndCursor(canvas); |
| 232 if (textfield_->draw_border()) | 238 if (textfield_->draw_border()) |
| 233 OnPaintBorder(canvas); | 239 OnPaintBorder(canvas); |
| 234 } | 240 } |
| 235 | 241 |
| (...skipping 512 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 748 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), | 754 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), |
| 749 is_cursor_visible_ ? kCursorVisibleTimeMs : kCursorInvisibleTimeMs); | 755 is_cursor_visible_ ? kCursorVisibleTimeMs : kCursorInvisibleTimeMs); |
| 750 } | 756 } |
| 751 | 757 |
| 752 void NativeTextfieldViews::RepaintCursor() { | 758 void NativeTextfieldViews::RepaintCursor() { |
| 753 gfx::Rect r = cursor_bounds_; | 759 gfx::Rect r = cursor_bounds_; |
| 754 r.Inset(-1, -1, -1, -1); | 760 r.Inset(-1, -1, -1, -1); |
| 755 SchedulePaintInRect(r); | 761 SchedulePaintInRect(r); |
| 756 } | 762 } |
| 757 | 763 |
| 764 gfx::Rect NativeTextfieldViews::GetCursorBounds(size_t cursor_pos) const { | |
| 765 string16 text = model_->GetVisibleText(); | |
| 766 const gfx::Font& font = GetFont(); | |
| 767 int x = font.GetStringWidth(text.substr(0U, cursor_pos)); | |
| 768 DCHECK(x >= 0); | |
|
oshima
2011/06/07 17:32:52
can you change to DCHECK_GE?
msw
2011/06/08 23:50:29
Done.
| |
| 769 int h = std::min(height() - GetInsets().height(), font.GetHeight()); | |
| 770 gfx::Rect bounds(x, (height() - h) / 2, 0, h); | |
| 771 if (text.length() != cursor_pos) | |
| 772 bounds.set_width(font.GetStringWidth(text.substr(0, cursor_pos + 1)) - x); | |
| 773 return bounds; | |
| 774 } | |
| 775 | |
| 776 | |
| 758 void NativeTextfieldViews::UpdateCursorBoundsAndTextOffset() { | 777 void NativeTextfieldViews::UpdateCursorBoundsAndTextOffset() { |
| 759 if (bounds().IsEmpty()) | 778 if (bounds().IsEmpty()) |
| 760 return; | 779 return; |
| 761 | 780 |
| 762 gfx::Insets insets = GetInsets(); | |
| 763 | |
| 764 int width = bounds().width() - insets.width(); | |
| 765 | |
| 766 // TODO(oshima): bidi | 781 // TODO(oshima): bidi |
| 767 const gfx::Font& font = GetFont(); | 782 int width = bounds().width() - GetInsets().width(); |
| 768 int full_width = font.GetStringWidth(model_->GetVisibleText()); | 783 int full_width = GetFont().GetStringWidth(model_->GetVisibleText()); |
| 769 int cursor_height = std::min(height() - insets.height(), font.GetHeight()); | 784 cursor_bounds_ = GetCursorBounds(model_->cursor_pos()); |
| 770 | |
| 771 cursor_bounds_ = model_->GetCursorBounds(font); | |
| 772 cursor_bounds_.set_y((height() - cursor_height) / 2); | |
| 773 cursor_bounds_.set_height(cursor_height); | |
| 774 | |
| 775 int x_right = text_offset_ + cursor_bounds_.right(); | |
| 776 int x_left = text_offset_ + cursor_bounds_.x(); | |
| 777 | 785 |
| 778 if (full_width < width) { | 786 if (full_width < width) { |
| 779 // Show all text whenever the text fits to the size. | 787 // Show all text whenever the text fits to the size. |
| 780 text_offset_ = 0; | 788 text_offset_ = 0; |
| 781 } else if (x_right > width) { | 789 } else if ((text_offset_ + cursor_bounds_.right()) > width) { |
| 782 // when the cursor overflows to the right | 790 // when the cursor overflows to the right |
| 783 text_offset_ = width - cursor_bounds_.right(); | 791 text_offset_ = width - cursor_bounds_.right(); |
| 784 } else if (x_left < 0) { | 792 } else if ((text_offset_ + cursor_bounds_.x()) < 0) { |
| 785 // when the cursor overflows to the left | 793 // when the cursor overflows to the left |
| 786 text_offset_ = -cursor_bounds_.x(); | 794 text_offset_ = -cursor_bounds_.x(); |
| 787 } else if (full_width > width && text_offset_ + full_width < width) { | 795 } else if (full_width > width && text_offset_ + full_width < width) { |
| 788 // when the cursor moves within the textfield with the text | 796 // when the cursor moves within the textfield with the text |
| 789 // longer than the field. | 797 // longer than the field. |
| 790 text_offset_ = width - full_width; | 798 text_offset_ = width - full_width; |
| 791 } else { | 799 } else { |
| 792 // move cursor freely. | 800 // move cursor freely. |
| 793 } | 801 } |
| 794 // shift cursor bounds to fit insets. | 802 // shift cursor bounds to fit insets. |
| 795 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left()); | 803 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + GetInsets().left()); |
| 796 | 804 |
| 797 OnCaretBoundsChanged(); | 805 OnCaretBoundsChanged(); |
| 798 } | 806 } |
| 799 | 807 |
| 800 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { | 808 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
| 801 gfx::Insets insets = GetInsets(); | 809 gfx::Insets insets = GetInsets(); |
| 802 | 810 |
| 803 canvas->Save(); | 811 canvas->Save(); |
| 804 canvas->ClipRectInt(insets.left(), insets.top(), | 812 canvas->ClipRectInt(insets.left(), insets.top(), |
| 805 width() - insets.width(), height() - insets.height()); | 813 width() - insets.width(), height() - insets.height()); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 832 iter->range.end()); | 840 iter->range.end()); |
| 833 // TODO(oshima): This does not give the accurate position due to | 841 // TODO(oshima): This does not give the accurate position due to |
| 834 // kerning. Figure out how to do. | 842 // kerning. Figure out how to do. |
| 835 int width = font.GetStringWidth(text); | 843 int width = font.GetStringWidth(text); |
| 836 iter->style->DrawString(canvas, text, font, textfield_->read_only(), | 844 iter->style->DrawString(canvas, text, font, textfield_->read_only(), |
| 837 x_offset, y, width, text_height); | 845 x_offset, y, width, text_height); |
| 838 x_offset += width; | 846 x_offset += width; |
| 839 } | 847 } |
| 840 canvas->Restore(); | 848 canvas->Restore(); |
| 841 | 849 |
| 842 if (textfield_->IsEnabled() && is_cursor_visible_ && | 850 if (textfield_->IsEnabled()) { |
| 843 !model_->HasSelection()) { | 851 // Paint cursor. Replace cursor is drawn as rectangle for now. |
| 844 // Paint Cursor. Replace cursor is drawn as rectangle for now. | 852 if (is_cursor_visible_ && !model_->HasSelection()) |
| 845 canvas->DrawRectInt(kCursorColor, | 853 canvas->DrawRectInt(kCursorColor, |
| 846 cursor_bounds_.x(), | 854 cursor_bounds_.x(), |
| 847 cursor_bounds_.y(), | 855 cursor_bounds_.y(), |
| 848 insert_ ? 0 : cursor_bounds_.width(), | 856 insert_ ? 0 : cursor_bounds_.width(), |
| 849 cursor_bounds_.height()); | 857 cursor_bounds_.height()); |
| 858 // Paint drop cursor. | |
| 859 if (is_drop_cursor_visible_) | |
| 860 canvas->DrawRectInt(kCursorColor, | |
| 861 drop_cursor_bounds_.x(), | |
| 862 drop_cursor_bounds_.y(), | |
| 863 0, | |
| 864 drop_cursor_bounds_.height()); | |
|
oshima
2011/06/07 17:32:52
basic question. Can both be visible at the same ti
msw
2011/06/08 23:50:29
Done.
| |
| 850 } | 865 } |
| 851 } | 866 } |
| 852 | 867 |
| 853 bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { | 868 bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
| 854 // TODO(oshima): Refactor and consolidate with ExecuteCommand. | 869 // TODO(oshima): Refactor and consolidate with ExecuteCommand. |
| 855 if (key_event.type() == ui::ET_KEY_PRESSED) { | 870 if (key_event.type() == ui::ET_KEY_PRESSED) { |
| 856 ui::KeyboardCode key_code = key_event.key_code(); | 871 ui::KeyboardCode key_code = key_event.key_code(); |
| 857 // TODO(oshima): shift-tab does not work. Figure out why and fix. | 872 // TODO(oshima): shift-tab does not work. Figure out why and fix. |
| 858 if (key_code == ui::VKEY_TAB) | 873 if (key_code == ui::VKEY_TAB) |
| 859 return false; | 874 return false; |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1088 // Filter out all control characters, including tab and new line characters, | 1103 // Filter out all control characters, including tab and new line characters, |
| 1089 // and all characters with Alt modifier. But we need to allow characters with | 1104 // and all characters with Alt modifier. But we need to allow characters with |
| 1090 // AltGr modifier. | 1105 // AltGr modifier. |
| 1091 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different | 1106 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different |
| 1092 // flag that we don't care about. | 1107 // flag that we don't care about. |
| 1093 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && | 1108 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && |
| 1094 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; | 1109 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; |
| 1095 } | 1110 } |
| 1096 | 1111 |
| 1097 } // namespace views | 1112 } // namespace views |
| OLD | NEW |