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

Unified Diff: ui/views/controls/textfield/textfield_views_model.cc

Issue 9467017: Password support for NativeTextfieldViews, in the model (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 10 months 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
« no previous file with comments | « ui/views/controls/textfield/textfield_views_model.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/views/controls/textfield/textfield_views_model.cc
diff --git a/ui/views/controls/textfield/textfield_views_model.cc b/ui/views/controls/textfield/textfield_views_model.cc
index d0fc8e01aec4c841e6429b8b3b7241661718158a..1eda7aa9dcf10d232d685e559853e7481ded8055 100644
--- a/ui/views/controls/textfield/textfield_views_model.cc
+++ b/ui/views/controls/textfield/textfield_views_model.cc
@@ -9,6 +9,7 @@
#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "base/stl_util.h"
+#include "base/utf_offset_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
@@ -23,6 +24,11 @@ namespace views {
namespace internal {
+// All chars are replaced by this char when the password style is set.
+// TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*'
+// that's available in the font (find_invisible_char() in gtkentry.c).
+const char16 kPasswordReplacementChar = '*';
+
// An edit object holds enough information/state to undo/redo the
// change. Two edits are merged when possible, for example, when
// you type new characters in sequence. |Commit()| can be used to
@@ -289,10 +295,6 @@ TextfieldViewsModel::~TextfieldViewsModel() {
ClearComposition();
}
-const string16& TextfieldViewsModel::GetText() const {
- return render_text_->text();
-}
-
bool TextfieldViewsModel::SetText(const string16& text) {
bool changed = false;
if (HasCompositionText()) {
@@ -313,16 +315,23 @@ bool TextfieldViewsModel::SetText(const string16& text) {
new_cursor,
text,
0U);
- render_text_->SetCursorPosition(new_cursor);
+ render_text_->SetCursorPosition(ToDisplayIndex(new_cursor));
}
ClearSelection();
return changed;
}
+void TextfieldViewsModel::SetObscured(bool obscured) {
+ if (obscured != obscured_) {
+ obscured_ = obscured;
+ render_text_->SetText(GetDisplayText());
+ }
+}
+
void TextfieldViewsModel::Append(const string16& text) {
if (HasCompositionText())
ConfirmCompositionText();
- size_t save = GetCursorPosition();
+ size_t save = render_text_->GetCursorPosition();
MoveCursor(gfx::LINE_BREAK,
render_text_->GetVisualDirectionOfLogicalEnd(),
false);
@@ -343,8 +352,9 @@ bool TextfieldViewsModel::Delete() {
}
if (GetText().length() > GetCursorPosition()) {
size_t cursor_position = GetCursorPosition();
- size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme(
- cursor_position, gfx::CURSOR_FORWARD);
+ size_t next_grapheme_index =
+ FromDisplayIndex(render_text_->IndexOfAdjacentGrapheme(
+ ToDisplayIndex(cursor_position), gfx::CURSOR_FORWARD));
ExecuteAndRecordDelete(cursor_position, next_grapheme_index, true);
return true;
}
@@ -361,16 +371,17 @@ bool TextfieldViewsModel::Backspace() {
DeleteSelection();
return true;
}
- if (GetCursorPosition() > 0) {
- size_t cursor_position = GetCursorPosition();
- ExecuteAndRecordDelete(cursor_position, cursor_position - 1, true);
+ size_t cursor_position = GetCursorPosition();
+ if (cursor_position > 0) {
+ size_t previous_char = Utf16OffsetToIndex(GetText(), cursor_position, -1);
+ ExecuteAndRecordDelete(cursor_position, previous_char, true);
return true;
}
return false;
}
size_t TextfieldViewsModel::GetCursorPosition() const {
- return render_text_->GetCursorPosition();
+ return FromDisplayIndex(render_text_->GetCursorPosition());
}
void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type,
@@ -381,7 +392,8 @@ void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type,
render_text_->MoveCursor(break_type, direction, select);
}
-bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) {
+bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& sel) {
+ gfx::SelectionModel selection = ToDisplaySelection(sel);
if (HasCompositionText()) {
ConfirmCompositionText();
// ConfirmCompositionText() updates cursor position. Need to reflect it in
@@ -404,29 +416,30 @@ bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) {
}
string16 TextfieldViewsModel::GetSelectedText() const {
- return GetText().substr(render_text_->MinOfSelection(),
- (render_text_->MaxOfSelection() - render_text_->MinOfSelection()));
+ size_t lo = FromDisplayIndex(render_text_->MinOfSelection());
+ size_t hi = FromDisplayIndex(render_text_->MaxOfSelection());
+ return GetText().substr(lo, hi - lo);
}
void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const {
- range->set_start(render_text_->GetSelectionStart());
- range->set_end(render_text_->GetCursorPosition());
+ *range = FromDisplayRange(ui::Range(
+ render_text_->GetSelectionStart(), render_text_->GetCursorPosition()));
}
void TextfieldViewsModel::SelectRange(const ui::Range& range) {
if (HasCompositionText())
ConfirmCompositionText();
- render_text_->SelectRange(range);
+ render_text_->SelectRange(ToDisplayRange(range));
}
void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const {
- *sel = render_text_->selection_model();
+ *sel = FromDisplaySelection(render_text_->selection_model());
}
void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) {
if (HasCompositionText())
ConfirmCompositionText();
- render_text_->MoveCursorTo(sel);
+ render_text_->MoveCursorTo(ToDisplaySelection(sel));
}
void TextfieldViewsModel::SelectAll() {
@@ -498,7 +511,7 @@ bool TextfieldViewsModel::Redo() {
}
bool TextfieldViewsModel::Cut() {
- if (!HasCompositionText() && HasSelection()) {
+ if (!HasCompositionText() && HasSelection() && !is_obscured()) {
ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate
->GetClipboard()).WriteText(GetSelectedText());
// A trick to let undo/redo handle cursor correctly.
@@ -515,7 +528,7 @@ bool TextfieldViewsModel::Cut() {
}
bool TextfieldViewsModel::Copy() {
- if (!HasCompositionText() && HasSelection()) {
+ if (!HasCompositionText() && HasSelection() && !is_obscured()) {
ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate
->GetClipboard()).WriteText(GetSelectedText());
return true;
@@ -541,8 +554,9 @@ bool TextfieldViewsModel::HasSelection() const {
void TextfieldViewsModel::DeleteSelection() {
DCHECK(!HasCompositionText());
DCHECK(HasSelection());
- ExecuteAndRecordDelete(render_text_->GetSelectionStart(),
- render_text_->GetCursorPosition(), false);
+ ExecuteAndRecordDelete(FromDisplayIndex(render_text_->GetSelectionStart()),
+ FromDisplayIndex(render_text_->GetCursorPosition()),
+ false);
}
void TextfieldViewsModel::DeleteSelectionAndInsertTextAt(
@@ -578,9 +592,9 @@ void TextfieldViewsModel::SetCompositionText(
size_t cursor = GetCursorPosition();
string16 new_text = GetText();
- render_text_->SetText(new_text.insert(cursor, composition.text));
+ SetTextInternal(new_text.insert(cursor, composition.text));
ui::Range range(cursor, cursor + composition.text.length());
- render_text_->SetCompositionRange(range);
+ render_text_->SetCompositionRange(ToDisplayRange(range));
// TODO(msw): Support multiple composition underline ranges.
if (composition.selection.IsValid()) {
@@ -588,20 +602,20 @@ void TextfieldViewsModel::SetCompositionText(
std::min(range.start() + composition.selection.start(), range.end());
size_t end =
std::min(range.start() + composition.selection.end(), range.end());
- render_text_->SelectRange(ui::Range(start, end));
+ render_text_->SelectRange(ToDisplayRange(ui::Range(start, end)));
} else {
- render_text_->SetCursorPosition(range.end());
+ render_text_->SetCursorPosition(ToDisplayIndex(range.end()));
}
}
void TextfieldViewsModel::ConfirmCompositionText() {
DCHECK(HasCompositionText());
- ui::Range range = render_text_->GetCompositionRange();
+ ui::Range range = FromDisplayRange(render_text_->GetCompositionRange());
string16 text = GetText().substr(range.start(), range.length());
// TODO(oshima): current behavior on ChromeOS is a bit weird and not
// sure exactly how this should work. Find out and fix if necessary.
AddOrMergeEditHistory(new InsertEdit(false, text, range.start()));
- render_text_->SetCursorPosition(range.end());
+ render_text_->SetCursorPosition(ToDisplayIndex(range.end()));
ClearComposition();
if (delegate_)
delegate_->OnCompositionTextConfirmedOrCleared();
@@ -609,11 +623,11 @@ void TextfieldViewsModel::ConfirmCompositionText() {
void TextfieldViewsModel::CancelCompositionText() {
DCHECK(HasCompositionText());
- ui::Range range = render_text_->GetCompositionRange();
+ ui::Range range = FromDisplayRange(render_text_->GetCompositionRange());
ClearComposition();
string16 new_text = GetText();
- render_text_->SetText(new_text.erase(range.start(), range.length()));
- render_text_->SetCursorPosition(range.start());
+ SetTextInternal(new_text.erase(range.start(), range.length()));
+ render_text_->SetCursorPosition(ToDisplayIndex(range.start()));
if (delegate_)
delegate_->OnCompositionTextConfirmedOrCleared();
}
@@ -623,7 +637,7 @@ void TextfieldViewsModel::ClearComposition() {
}
void TextfieldViewsModel::GetCompositionTextRange(ui::Range* range) const {
- *range = ui::Range(render_text_->GetCompositionRange());
+ *range = ui::Range(FromDisplayRange(render_text_->GetCompositionRange()));
}
bool TextfieldViewsModel::HasCompositionText() const {
@@ -702,7 +716,7 @@ void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from,
void TextfieldViewsModel::ExecuteAndRecordReplaceSelection(
MergeType merge_type, const string16& new_text) {
- size_t new_text_start = render_text_->MinOfSelection();
+ size_t new_text_start = FromDisplayIndex(render_text_->MinOfSelection());
size_t new_cursor_pos = new_text_start + new_text.length();
ExecuteAndRecordReplace(merge_type,
GetCursorPosition(),
@@ -716,7 +730,7 @@ void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type,
size_t new_cursor_pos,
const string16& new_text,
size_t new_text_start) {
- size_t old_text_start = render_text_->MinOfSelection();
+ size_t old_text_start = FromDisplayIndex(render_text_->MinOfSelection());
bool backward =
render_text_->GetSelectionStart() > render_text_->GetCursorPosition();
Edit* edit = new ReplaceEdit(merge_type,
@@ -772,12 +786,62 @@ void TextfieldViewsModel::ModifyText(size_t delete_from,
string16 text = GetText();
ClearComposition();
if (delete_from != delete_to)
- render_text_->SetText(text.erase(delete_from, delete_to - delete_from));
+ SetTextInternal(text.erase(delete_from, delete_to - delete_from));
if (!new_text.empty())
- render_text_->SetText(text.insert(new_text_insert_at, new_text));
- render_text_->SetCursorPosition(new_cursor_pos);
+ SetTextInternal(text.insert(new_text_insert_at, new_text));
+ render_text_->SetCursorPosition(ToDisplayIndex(new_cursor_pos));
// TODO(oshima): mac selects the text that is just undone (but gtk doesn't).
// This looks fine feature and we may want to do the same.
}
+void TextfieldViewsModel::SetTextInternal(const string16& new_text) {
+ text_ = new_text;
+ render_text_->SetText(GetDisplayText());
+}
+
+string16 TextfieldViewsModel::GetDisplayText() const {
+ if (!obscured_)
+ return text_;
+ size_t obscured_text_length =
+ static_cast<size_t>(Utf16IndexToOffset(text_, 0, text_.length()));
+ return string16(obscured_text_length, internal::kPasswordReplacementChar);
+}
+
+size_t TextfieldViewsModel::FromDisplayIndex(size_t index) const {
+ if (!is_obscured())
+ return index;
+ return Utf16OffsetToIndex(GetText(), 0, index);
+}
+size_t TextfieldViewsModel::ToDisplayIndex(size_t index) const {
+ if (!is_obscured())
+ return index;
+ return Utf16IndexToOffset(GetText(), 0, index);
+}
+ui::Range TextfieldViewsModel::FromDisplayRange(ui::Range range) const {
+ if (!is_obscured() || !range.IsValid())
+ return range;
+ return ui::Range(FromDisplayIndex(range.start()),
+ FromDisplayIndex(range.end()));
+}
+ui::Range TextfieldViewsModel::ToDisplayRange(ui::Range range) const {
+ if (!is_obscured() || !range.IsValid())
+ return range;
+ return ui::Range(ToDisplayIndex(range.start()),
+ ToDisplayIndex(range.end()));
+}
+gfx::SelectionModel TextfieldViewsModel::FromDisplaySelection(
+ const gfx::SelectionModel& model) const {
+ return gfx::SelectionModel(FromDisplayIndex(model.selection_start()),
+ FromDisplayIndex(model.selection_end()),
+ FromDisplayIndex(model.caret_pos()),
+ model.caret_placement());
+}
+gfx::SelectionModel TextfieldViewsModel::ToDisplaySelection(
+ const gfx::SelectionModel& model) const {
+ return gfx::SelectionModel(ToDisplayIndex(model.selection_start()),
+ ToDisplayIndex(model.selection_end()),
+ ToDisplayIndex(model.caret_pos()),
+ model.caret_placement());
+}
+
} // namespace views
« no previous file with comments | « ui/views/controls/textfield/textfield_views_model.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698