Index: chrome/browser/autocomplete/autocomplete_edit_view_mac.mm |
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm |
index dc1a6f5eac8c87bc9e7c6ccc497db3fea8665391..0a22805b31cb63121c3eef57a962ea11686ea27b 100644 |
--- a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm |
+++ b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm |
@@ -12,6 +12,41 @@ |
namespace { |
+// TODO(shess): This is ugly, find a better way. Using it right now |
+// so that I can crib from gtk and still be able to see that I'm using |
+// the same values easily. |
+const NSColor* ColorWithRGBBytes(int rr, int gg, int bb) { |
+ DCHECK_LE(rr, 255); |
+ DCHECK_LE(bb, 255); |
+ DCHECK_LE(gg, 255); |
+ return [NSColor colorWithCalibratedRed:static_cast<float>(rr)/255.0 |
+ green:static_cast<float>(gg)/255.0 |
+ blue:static_cast<float>(bb)/255.0 |
+ alpha:1.0]; |
+} |
+const NSColor* SecureBackgroundColor() { |
+ return ColorWithRGBBytes(255, 245, 195); // Yellow |
+} |
+const NSColor* NormalBackgroundColor() { |
+ return [NSColor controlBackgroundColor]; |
+} |
+const NSColor* InsecureBackgroundColor() { |
+ return [NSColor controlBackgroundColor]; |
+} |
+ |
+const NSColor* HostTextColor() { |
+ return [NSColor blackColor]; |
+} |
+const NSColor* BaseTextColor() { |
+ return [NSColor darkGrayColor]; |
+} |
+const NSColor* SecureSchemeColor() { |
+ return ColorWithRGBBytes(0x00, 0x96, 0x14); |
+} |
+const NSColor* InsecureSchemeColor() { |
+ return ColorWithRGBBytes(0xc8, 0x00, 0x00); |
+} |
+ |
// Store's the model and view state across tab switches. |
struct AutocompleteEditViewMacState { |
AutocompleteEditViewMacState(const AutocompleteEditModel::State model_state, |
@@ -44,6 +79,13 @@ const AutocompleteEditViewMacState* GetStateFromTab(const TabContents* tab) { |
return GetStateAccessor()->GetProperty(tab->property_bag()); |
} |
+// Helper to make converting url_parse ranges to NSRange easier to |
+// read. |
+NSRange ComponentToNSRange(const url_parse::Component& component) { |
+ return NSMakeRange(static_cast<NSInteger>(component.begin), |
+ static_cast<NSInteger>(component.len)); |
+} |
+ |
} // namespace |
// Thin Obj-C bridge class that is the delegate of the omnibox field. |
@@ -77,6 +119,9 @@ AutocompleteEditViewMac::AutocompleteEditViewMac( |
DCHECK(command_updater); |
DCHECK(field); |
[field_ setDelegate:edit_helper_]; |
+ |
+ // Needed so that editing doesn't lose the styling. |
+ [field_ setAllowsEditingTextAttributes:YES]; |
} |
AutocompleteEditViewMac::~AutocompleteEditViewMac() { |
@@ -137,9 +182,7 @@ void AutocompleteEditViewMac::Update( |
// not restoring focus is an oversight, or intentional for some |
// subtle reason. |
if (state->has_focus) { |
- FocusLocation(); |
- DCHECK([field_ currentEditor]); |
- [[field_ currentEditor] setSelectedRange:state->selection]; |
+ SetSelectedRange(state->selection); |
} |
} |
} else if (user_visible) { |
@@ -151,10 +194,7 @@ void AutocompleteEditViewMac::Update( |
} else { |
// TODO(shess): Figure out how this case is used, to make sure |
// we're getting the selection and popup right. |
- // UpdateAndStyleText() approximates the inner part of Revertall() |
- // which under GTK is called EmphasizeURLComponents(), and is |
- // expected to change when I start feeding in the styling code. |
- UpdateAndStyleText(text, 0); |
+ UpdateAndStyleText(text); |
} |
} |
@@ -187,7 +227,9 @@ void AutocompleteEditViewMac::SetUserText(const std::wstring& text, |
const std::wstring& display_text, |
bool update_popup) { |
model_->SetUserText(text); |
- UpdateAndStyleText(display_text, display_text.size()); |
+ // TODO(shess): TODO below from gtk. |
+ // TODO(deanm): something about selection / focus change here. |
+ UpdateAndStyleText(display_text); |
if (update_popup) { |
UpdatePopup(); |
} |
@@ -198,24 +240,40 @@ NSRange AutocompleteEditViewMac::GetSelectedRange() const { |
return [[field_ currentEditor] selectedRange]; |
} |
+void AutocompleteEditViewMac::SetSelectedRange(const NSRange range) { |
+ // TODO(shess): Check if we should steal focus or not. We can't set |
+ // the selection without focus, though. |
+ FocusLocation(); |
+ |
+ // TODO(shess): What if it didn't get first responder, and there is |
+ // no field editor? This will do nothing. Well, at least it won't |
+ // crash. Think of something more productive to do, or prove that |
+ // it cannot occur and DCHECK appropriately. |
+ [[field_ currentEditor] setSelectedRange:range]; |
+} |
+ |
void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const std::wstring& text, |
size_t caret_pos) { |
- UpdateAndStyleText(text, text.size()); |
+ DCHECK_LE(caret_pos, text.size()); |
+ UpdateAndStyleText(text); |
+ SetSelectedRange(NSMakeRange(caret_pos, caret_pos)); |
} |
void AutocompleteEditViewMac::SelectAll(bool reversed) { |
- // TODO(shess): Figure out what reversed implies. The gtk version |
+ // TODO(shess): Figure out what |reversed| implies. The gtk version |
// has it imply inverting the selection front to back, but I don't |
// even know if that makes sense for Mac. |
- UpdateAndStyleText(GetText(), 0); |
+ |
+ // TODO(shess): Verify that we should be stealing focus at this |
+ // point. |
+ SetSelectedRange(NSMakeRange(0, GetText().size())); |
} |
void AutocompleteEditViewMac::RevertAll() { |
ClosePopup(); |
model_->Revert(); |
- std::wstring tt = GetText(); |
- UpdateAndStyleText(tt, 0); |
+ UpdateAndStyleText(GetText()); |
controller_->OnChanged(); |
} |
@@ -238,61 +296,56 @@ void AutocompleteEditViewMac::ClosePopup() { |
} |
void AutocompleteEditViewMac::UpdateAndStyleText( |
- const std::wstring& display_text, size_t user_text_length) { |
- NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys: |
- [field_ font], NSFontAttributeName, |
- nil]; |
+ const std::wstring& display_text) { |
NSString* ss = base::SysWideToNSString(display_text); |
NSMutableAttributedString* as = |
- [[[NSMutableAttributedString alloc] initWithString:ss |
- attributes:attributes] |
- autorelease]; |
+ [[[NSMutableAttributedString alloc] initWithString:ss] autorelease]; |
+ [as addAttribute:NSFontAttributeName value:[field_ font] |
+ range:NSMakeRange(0, [as length])]; |
url_parse::Parsed parts; |
AutocompleteInput::Parse(display_text, model_->GetDesiredTLD(), |
&parts, NULL); |
- bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); |
+ const bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); |
if (emphasize) { |
- // TODO(shess): Pull color out as a constant. |
- [as addAttribute:NSForegroundColorAttributeName |
- value:[NSColor greenColor] |
- range:NSMakeRange((NSInteger)parts.host.begin, |
- (NSInteger)parts.host.len)]; |
+ [as addAttribute:NSForegroundColorAttributeName value:BaseTextColor() |
+ range:NSMakeRange(0, [as length])]; |
+ |
+ [as addAttribute:NSForegroundColorAttributeName value:HostTextColor() |
+ range:ComponentToNSRange(parts.host)]; |
} |
// TODO(shess): GTK has this as a member var, figure out why. |
- ToolbarModel::SecurityLevel scheme_security_level = |
+ // [Could it be to not change if no change? If so, I'm guessing |
+ // AppKit may already handle that.] |
+ const ToolbarModel::SecurityLevel scheme_security_level = |
toolbar_model_->GetSchemeSecurityLevel(); |
+ if (scheme_security_level == ToolbarModel::SECURE) { |
+ [field_ setBackgroundColor:SecureBackgroundColor()]; |
+ } else if (scheme_security_level == ToolbarModel::NORMAL) { |
+ [field_ setBackgroundColor:NormalBackgroundColor()]; |
+ } else if (scheme_security_level == ToolbarModel::INSECURE) { |
+ [field_ setBackgroundColor:InsecureBackgroundColor()]; |
+ } else { |
+ NOTREACHED() << "Unexpected scheme_security_level: " |
+ << scheme_security_level; |
+ } |
+ |
// Emphasize the scheme for security UI display purposes (if necessary). |
if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() && |
(scheme_security_level != ToolbarModel::NORMAL)) { |
- // TODO(shess): Pull colors out as constants. |
NSColor* color; |
if (scheme_security_level == ToolbarModel::SECURE) { |
- color = [NSColor blueColor]; |
+ color = SecureSchemeColor(); |
} else { |
- color = [NSColor blackColor]; |
+ color = InsecureSchemeColor(); |
} |
[as addAttribute:NSForegroundColorAttributeName value:color |
- range:NSMakeRange((NSInteger)parts.scheme.begin, |
- (NSInteger)parts.scheme.len)]; |
+ range:ComponentToNSRange(parts.scheme)]; |
} |
- // TODO(shess): Check that this updates the model's sense of focus |
- // correctly. |
[field_ setObjectValue:as]; |
- if (![field_ currentEditor]) { |
- [field_ becomeFirstResponder]; |
- DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); |
- } |
- |
- NSRange selected_range = NSMakeRange(user_text_length, [as length]); |
- // TODO(shess): What if it didn't get first responder, and there is |
- // no field editor? This will do nothing. Well, at least it won't |
- // crash. Think of something more productive to do, or prove that |
- // it cannot occur and DCHECK appropriately. |
- [[field_ currentEditor] setSelectedRange:selected_range]; |
} |
void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged( |
@@ -301,10 +354,11 @@ void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged( |
// the popup, will be restored if they hit escape. Figure out if |
// that is for certain it. |
if (save_original_selection) { |
+ saved_temporary_selection_ = GetSelectedRange(); |
saved_temporary_text_ = GetText(); |
} |
- UpdateAndStyleText(display_text, display_text.size()); |
+ SetWindowTextAndCaretPos(display_text, display_text.size()); |
} |
bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged( |
@@ -316,13 +370,16 @@ bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged( |
return false; |
} |
- UpdateAndStyleText(display_text, user_text_length); |
+ UpdateAndStyleText(display_text); |
+ DCHECK_LE(user_text_length, display_text.size()); |
+ SetSelectedRange(NSMakeRange(user_text_length, display_text.size())); |
return true; |
} |
void AutocompleteEditViewMac::OnRevertTemporaryText() { |
- UpdateAndStyleText(saved_temporary_text_, saved_temporary_text_.size()); |
+ UpdateAndStyleText(saved_temporary_text_); |
saved_temporary_text_.clear(); |
+ SetSelectedRange(saved_temporary_selection_); |
} |
void AutocompleteEditViewMac::OnBeforePossibleChange() { |
@@ -331,14 +388,14 @@ void AutocompleteEditViewMac::OnBeforePossibleChange() { |
} |
bool AutocompleteEditViewMac::OnAfterPossibleChange() { |
- NSRange new_selection(GetSelectedRange()); |
- std::wstring new_text(GetText()); |
+ const NSRange new_selection(GetSelectedRange()); |
+ const std::wstring new_text(GetText()); |
const size_t length = new_text.length(); |
- bool selection_differs = !NSEqualRanges(new_selection, |
- selection_before_change_); |
- bool at_end_of_edit = (length == new_selection.location); |
- bool text_differs = (new_text != text_before_change_); |
+ const bool selection_differs = !NSEqualRanges(new_selection, |
+ selection_before_change_); |
+ const bool at_end_of_edit = (length == new_selection.location); |
+ const bool text_differs = (new_text != text_before_change_); |
// When the user has deleted text, we don't allow inline |
// autocomplete. This is assumed if the text has gotten shorter AND |
@@ -350,21 +407,33 @@ bool AutocompleteEditViewMac::OnAfterPossibleChange() { |
// and other methods to provide positive knowledge that a delete |
// occured, rather than intuiting it from context. Consider whether |
// that would be a stronger approach. |
- bool just_deleted_text = |
+ const bool just_deleted_text = |
(length < text_before_change_.length() && |
new_selection.location <= selection_before_change_.location); |
- bool something_changed = model_->OnAfterPossibleChange(new_text, |
+ const bool something_changed = model_->OnAfterPossibleChange(new_text, |
selection_differs, text_differs, just_deleted_text, at_end_of_edit); |
- // TODO(shess): Restyle the text if something_changed. Not fixing |
- // now because styling is currently broken. |
+ // Restyle if the user changed something. |
+ // TODO(shess): Does this need to diver deeper to avoid flashing? |
+ // For instance, if the user types "/" after the hostname, will it |
+ // show as HostTextColor() for an instant before being replaced by |
+ // BaseTextColor(). This could probably be done by subclassing the |
+ // cell and intercepting draw requests so that we draw the right |
+ // thing every time. |
+ // NOTE(shess): That kind of thing would also help with when the |
+ // flashing from when the user's typing replaces the selection which |
+ // is then re-created with the new autocomplete results. |
+ if (something_changed) { |
+ UpdateAndStyleText(new_text); |
+ SetSelectedRange(new_selection); |
+ } |
return something_changed; |
} |
void AutocompleteEditViewMac::OnUpOrDownKeyPressed(bool up, bool by_page) { |
- int count = by_page ? model_->result().size() : 1; |
+ const int count = by_page ? model_->result().size() : 1; |
model_->OnUpOrDownKeyPressed(up ? -count : count); |
} |
void AutocompleteEditViewMac::OnEscapeKeyPressed() { |
@@ -387,6 +456,7 @@ void AutocompleteEditViewMac::FocusLocation() { |
// current selection. |
if (![field_ currentEditor]) { |
[[field_ window] makeFirstResponder:field_]; |
+ DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); |
} |
} |