OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" | 5 #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" |
6 | 6 |
7 #include "base/sys_string_conversions.h" | 7 #include "base/sys_string_conversions.h" |
8 #include "chrome/browser/autocomplete/autocomplete_edit.h" | 8 #include "chrome/browser/autocomplete/autocomplete_edit.h" |
9 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" | 9 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" |
10 #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" | 10 #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" |
11 #include "chrome/browser/tab_contents/tab_contents.h" | 11 #include "chrome/browser/tab_contents/tab_contents.h" |
12 | 12 |
13 namespace { | 13 namespace { |
14 | 14 |
| 15 // TODO(shess): This is ugly, find a better way. Using it right now |
| 16 // so that I can crib from gtk and still be able to see that I'm using |
| 17 // the same values easily. |
| 18 const NSColor* ColorWithRGBBytes(int rr, int gg, int bb) { |
| 19 DCHECK_LE(rr, 255); |
| 20 DCHECK_LE(bb, 255); |
| 21 DCHECK_LE(gg, 255); |
| 22 return [NSColor colorWithCalibratedRed:static_cast<float>(rr)/255.0 |
| 23 green:static_cast<float>(gg)/255.0 |
| 24 blue:static_cast<float>(bb)/255.0 |
| 25 alpha:1.0]; |
| 26 } |
| 27 const NSColor* SecureBackgroundColor() { |
| 28 return ColorWithRGBBytes(255, 245, 195); // Yellow |
| 29 } |
| 30 const NSColor* NormalBackgroundColor() { |
| 31 return [NSColor controlBackgroundColor]; |
| 32 } |
| 33 const NSColor* InsecureBackgroundColor() { |
| 34 return [NSColor controlBackgroundColor]; |
| 35 } |
| 36 |
| 37 const NSColor* HostTextColor() { |
| 38 return [NSColor blackColor]; |
| 39 } |
| 40 const NSColor* BaseTextColor() { |
| 41 return [NSColor darkGrayColor]; |
| 42 } |
| 43 const NSColor* SecureSchemeColor() { |
| 44 return ColorWithRGBBytes(0x00, 0x96, 0x14); |
| 45 } |
| 46 const NSColor* InsecureSchemeColor() { |
| 47 return ColorWithRGBBytes(0xc8, 0x00, 0x00); |
| 48 } |
| 49 |
15 // Store's the model and view state across tab switches. | 50 // Store's the model and view state across tab switches. |
16 struct AutocompleteEditViewMacState { | 51 struct AutocompleteEditViewMacState { |
17 AutocompleteEditViewMacState(const AutocompleteEditModel::State model_state, | 52 AutocompleteEditViewMacState(const AutocompleteEditModel::State model_state, |
18 const bool has_focus, const NSRange& selection) | 53 const bool has_focus, const NSRange& selection) |
19 : model_state(model_state), | 54 : model_state(model_state), |
20 has_focus(has_focus), | 55 has_focus(has_focus), |
21 selection(selection) { | 56 selection(selection) { |
22 } | 57 } |
23 | 58 |
24 const AutocompleteEditModel::State model_state; | 59 const AutocompleteEditModel::State model_state; |
(...skipping 12 matching lines...) Expand all Loading... |
37 | 72 |
38 // Accessors for storing and getting the state from the tab. | 73 // Accessors for storing and getting the state from the tab. |
39 void StoreStateToTab(TabContents* tab, | 74 void StoreStateToTab(TabContents* tab, |
40 const AutocompleteEditViewMacState& state) { | 75 const AutocompleteEditViewMacState& state) { |
41 GetStateAccessor()->SetProperty(tab->property_bag(), state); | 76 GetStateAccessor()->SetProperty(tab->property_bag(), state); |
42 } | 77 } |
43 const AutocompleteEditViewMacState* GetStateFromTab(const TabContents* tab) { | 78 const AutocompleteEditViewMacState* GetStateFromTab(const TabContents* tab) { |
44 return GetStateAccessor()->GetProperty(tab->property_bag()); | 79 return GetStateAccessor()->GetProperty(tab->property_bag()); |
45 } | 80 } |
46 | 81 |
| 82 // Helper to make converting url_parse ranges to NSRange easier to |
| 83 // read. |
| 84 NSRange ComponentToNSRange(const url_parse::Component& component) { |
| 85 return NSMakeRange(static_cast<NSInteger>(component.begin), |
| 86 static_cast<NSInteger>(component.len)); |
| 87 } |
| 88 |
47 } // namespace | 89 } // namespace |
48 | 90 |
49 // Thin Obj-C bridge class that is the delegate of the omnibox field. | 91 // Thin Obj-C bridge class that is the delegate of the omnibox field. |
50 // It intercepts various control delegate methods and vectors them to | 92 // It intercepts various control delegate methods and vectors them to |
51 // the edit view. | 93 // the edit view. |
52 | 94 |
53 @interface AutocompleteFieldDelegate : NSObject { | 95 @interface AutocompleteFieldDelegate : NSObject { |
54 @private | 96 @private |
55 AutocompleteEditViewMac* edit_view_; // weak, owns us. | 97 AutocompleteEditViewMac* edit_view_; // weak, owns us. |
56 } | 98 } |
(...skipping 13 matching lines...) Expand all Loading... |
70 toolbar_model_(toolbar_model), | 112 toolbar_model_(toolbar_model), |
71 command_updater_(command_updater), | 113 command_updater_(command_updater), |
72 field_(field), | 114 field_(field), |
73 edit_helper_([[AutocompleteFieldDelegate alloc] initWithEditView:this]) { | 115 edit_helper_([[AutocompleteFieldDelegate alloc] initWithEditView:this]) { |
74 DCHECK(controller); | 116 DCHECK(controller); |
75 DCHECK(toolbar_model); | 117 DCHECK(toolbar_model); |
76 DCHECK(profile); | 118 DCHECK(profile); |
77 DCHECK(command_updater); | 119 DCHECK(command_updater); |
78 DCHECK(field); | 120 DCHECK(field); |
79 [field_ setDelegate:edit_helper_]; | 121 [field_ setDelegate:edit_helper_]; |
| 122 |
| 123 // Needed so that editing doesn't lose the styling. |
| 124 [field_ setAllowsEditingTextAttributes:YES]; |
80 } | 125 } |
81 | 126 |
82 AutocompleteEditViewMac::~AutocompleteEditViewMac() { | 127 AutocompleteEditViewMac::~AutocompleteEditViewMac() { |
83 // TODO(shess): Having to be aware of destructor ordering in this | 128 // TODO(shess): Having to be aware of destructor ordering in this |
84 // way seems brittle. There must be a better way. | 129 // way seems brittle. There must be a better way. |
85 | 130 |
86 // Destroy popup view before this object in case it tries to call us | 131 // Destroy popup view before this object in case it tries to call us |
87 // back in the destructor. Likewise for destroying the model before | 132 // back in the destructor. Likewise for destroying the model before |
88 // this object. | 133 // this object. |
89 popup_view_.reset(); | 134 popup_view_.reset(); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 // Should restore the user's text via SetUserText(). | 175 // Should restore the user's text via SetUserText(). |
131 model_->RestoreState(state->model_state); | 176 model_->RestoreState(state->model_state); |
132 | 177 |
133 // Restore user's selection. | 178 // Restore user's selection. |
134 // TODO(shess): The model_ does not restore the focus state. If | 179 // TODO(shess): The model_ does not restore the focus state. If |
135 // field_ was in focus when we switched away, I presume it | 180 // field_ was in focus when we switched away, I presume it |
136 // should be in focus when we switch back. Figure out if model_ | 181 // should be in focus when we switch back. Figure out if model_ |
137 // not restoring focus is an oversight, or intentional for some | 182 // not restoring focus is an oversight, or intentional for some |
138 // subtle reason. | 183 // subtle reason. |
139 if (state->has_focus) { | 184 if (state->has_focus) { |
140 FocusLocation(); | 185 SetSelectedRange(state->selection); |
141 DCHECK([field_ currentEditor]); | |
142 [[field_ currentEditor] setSelectedRange:state->selection]; | |
143 } | 186 } |
144 } | 187 } |
145 } else if (user_visible) { | 188 } else if (user_visible) { |
146 // Restore everything to the baseline look. | 189 // Restore everything to the baseline look. |
147 RevertAll(); | 190 RevertAll(); |
148 // TODO(shess): Figure out how this case is used, to make sure | 191 // TODO(shess): Figure out how this case is used, to make sure |
149 // we're getting the selection and popup right. | 192 // we're getting the selection and popup right. |
150 | 193 |
151 } else { | 194 } else { |
152 // TODO(shess): Figure out how this case is used, to make sure | 195 // TODO(shess): Figure out how this case is used, to make sure |
153 // we're getting the selection and popup right. | 196 // we're getting the selection and popup right. |
154 // UpdateAndStyleText() approximates the inner part of Revertall() | 197 UpdateAndStyleText(text); |
155 // which under GTK is called EmphasizeURLComponents(), and is | |
156 // expected to change when I start feeding in the styling code. | |
157 UpdateAndStyleText(text, 0); | |
158 } | 198 } |
159 } | 199 } |
160 | 200 |
161 void AutocompleteEditViewMac::OpenURL(const GURL& url, | 201 void AutocompleteEditViewMac::OpenURL(const GURL& url, |
162 WindowOpenDisposition disposition, | 202 WindowOpenDisposition disposition, |
163 PageTransition::Type transition, | 203 PageTransition::Type transition, |
164 const GURL& alternate_nav_url, | 204 const GURL& alternate_nav_url, |
165 size_t selected_line, | 205 size_t selected_line, |
166 const std::wstring& keyword) { | 206 const std::wstring& keyword) { |
167 // TODO(shess): Why is the caller passing an invalid url in the | 207 // TODO(shess): Why is the caller passing an invalid url in the |
(...skipping 12 matching lines...) Expand all Loading... |
180 } | 220 } |
181 | 221 |
182 std::wstring AutocompleteEditViewMac::GetText() const { | 222 std::wstring AutocompleteEditViewMac::GetText() const { |
183 return base::SysNSStringToWide([field_ stringValue]); | 223 return base::SysNSStringToWide([field_ stringValue]); |
184 } | 224 } |
185 | 225 |
186 void AutocompleteEditViewMac::SetUserText(const std::wstring& text, | 226 void AutocompleteEditViewMac::SetUserText(const std::wstring& text, |
187 const std::wstring& display_text, | 227 const std::wstring& display_text, |
188 bool update_popup) { | 228 bool update_popup) { |
189 model_->SetUserText(text); | 229 model_->SetUserText(text); |
190 UpdateAndStyleText(display_text, display_text.size()); | 230 // TODO(shess): TODO below from gtk. |
| 231 // TODO(deanm): something about selection / focus change here. |
| 232 UpdateAndStyleText(display_text); |
191 if (update_popup) { | 233 if (update_popup) { |
192 UpdatePopup(); | 234 UpdatePopup(); |
193 } | 235 } |
194 } | 236 } |
195 | 237 |
196 NSRange AutocompleteEditViewMac::GetSelectedRange() const { | 238 NSRange AutocompleteEditViewMac::GetSelectedRange() const { |
197 DCHECK([field_ currentEditor]); | 239 DCHECK([field_ currentEditor]); |
198 return [[field_ currentEditor] selectedRange]; | 240 return [[field_ currentEditor] selectedRange]; |
199 } | 241 } |
200 | 242 |
| 243 void AutocompleteEditViewMac::SetSelectedRange(const NSRange range) { |
| 244 // TODO(shess): Check if we should steal focus or not. We can't set |
| 245 // the selection without focus, though. |
| 246 FocusLocation(); |
| 247 |
| 248 // TODO(shess): What if it didn't get first responder, and there is |
| 249 // no field editor? This will do nothing. Well, at least it won't |
| 250 // crash. Think of something more productive to do, or prove that |
| 251 // it cannot occur and DCHECK appropriately. |
| 252 [[field_ currentEditor] setSelectedRange:range]; |
| 253 } |
| 254 |
201 void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const std::wstring& text, | 255 void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const std::wstring& text, |
202 size_t caret_pos) { | 256 size_t caret_pos) { |
203 UpdateAndStyleText(text, text.size()); | 257 DCHECK_LE(caret_pos, text.size()); |
| 258 UpdateAndStyleText(text); |
| 259 SetSelectedRange(NSMakeRange(caret_pos, caret_pos)); |
204 } | 260 } |
205 | 261 |
206 void AutocompleteEditViewMac::SelectAll(bool reversed) { | 262 void AutocompleteEditViewMac::SelectAll(bool reversed) { |
207 // TODO(shess): Figure out what reversed implies. The gtk version | 263 // TODO(shess): Figure out what |reversed| implies. The gtk version |
208 // has it imply inverting the selection front to back, but I don't | 264 // has it imply inverting the selection front to back, but I don't |
209 // even know if that makes sense for Mac. | 265 // even know if that makes sense for Mac. |
210 UpdateAndStyleText(GetText(), 0); | 266 |
| 267 // TODO(shess): Verify that we should be stealing focus at this |
| 268 // point. |
| 269 SetSelectedRange(NSMakeRange(0, GetText().size())); |
211 } | 270 } |
212 | 271 |
213 void AutocompleteEditViewMac::RevertAll() { | 272 void AutocompleteEditViewMac::RevertAll() { |
214 ClosePopup(); | 273 ClosePopup(); |
215 model_->Revert(); | 274 model_->Revert(); |
216 | 275 |
217 std::wstring tt = GetText(); | 276 UpdateAndStyleText(GetText()); |
218 UpdateAndStyleText(tt, 0); | |
219 controller_->OnChanged(); | 277 controller_->OnChanged(); |
220 } | 278 } |
221 | 279 |
222 void AutocompleteEditViewMac::UpdatePopup() { | 280 void AutocompleteEditViewMac::UpdatePopup() { |
223 model_->SetInputInProgress(true); | 281 model_->SetInputInProgress(true); |
224 if (!model_->has_focus()) | 282 if (!model_->has_focus()) |
225 return; | 283 return; |
226 | 284 |
227 // TODO(shess): | 285 // TODO(shess): |
228 // Shouldn't inline autocomplete when the caret/selection isn't at | 286 // Shouldn't inline autocomplete when the caret/selection isn't at |
229 // the end of the text. | 287 // the end of the text. |
230 // | 288 // |
231 // One option would seem to be to check for a non-nil field | 289 // One option would seem to be to check for a non-nil field |
232 // editor, and check it's selected range against its length. | 290 // editor, and check it's selected range against its length. |
233 model_->StartAutocomplete(false); | 291 model_->StartAutocomplete(false); |
234 } | 292 } |
235 | 293 |
236 void AutocompleteEditViewMac::ClosePopup() { | 294 void AutocompleteEditViewMac::ClosePopup() { |
237 popup_view_->GetModel()->StopAutocomplete(); | 295 popup_view_->GetModel()->StopAutocomplete(); |
238 } | 296 } |
239 | 297 |
240 void AutocompleteEditViewMac::UpdateAndStyleText( | 298 void AutocompleteEditViewMac::UpdateAndStyleText( |
241 const std::wstring& display_text, size_t user_text_length) { | 299 const std::wstring& display_text) { |
242 NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys: | |
243 [field_ font], NSFontAttributeName, | |
244 nil]; | |
245 NSString* ss = base::SysWideToNSString(display_text); | 300 NSString* ss = base::SysWideToNSString(display_text); |
246 NSMutableAttributedString* as = | 301 NSMutableAttributedString* as = |
247 [[[NSMutableAttributedString alloc] initWithString:ss | 302 [[[NSMutableAttributedString alloc] initWithString:ss] autorelease]; |
248 attributes:attributes] | 303 [as addAttribute:NSFontAttributeName value:[field_ font] |
249 autorelease]; | 304 range:NSMakeRange(0, [as length])]; |
250 | 305 |
251 url_parse::Parsed parts; | 306 url_parse::Parsed parts; |
252 AutocompleteInput::Parse(display_text, model_->GetDesiredTLD(), | 307 AutocompleteInput::Parse(display_text, model_->GetDesiredTLD(), |
253 &parts, NULL); | 308 &parts, NULL); |
254 bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); | 309 const bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); |
255 if (emphasize) { | 310 if (emphasize) { |
256 // TODO(shess): Pull color out as a constant. | 311 [as addAttribute:NSForegroundColorAttributeName value:BaseTextColor() |
257 [as addAttribute:NSForegroundColorAttributeName | 312 range:NSMakeRange(0, [as length])]; |
258 value:[NSColor greenColor] | 313 |
259 range:NSMakeRange((NSInteger)parts.host.begin, | 314 [as addAttribute:NSForegroundColorAttributeName value:HostTextColor() |
260 (NSInteger)parts.host.len)]; | 315 range:ComponentToNSRange(parts.host)]; |
261 } | 316 } |
262 | 317 |
263 // TODO(shess): GTK has this as a member var, figure out why. | 318 // TODO(shess): GTK has this as a member var, figure out why. |
264 ToolbarModel::SecurityLevel scheme_security_level = | 319 // [Could it be to not change if no change? If so, I'm guessing |
| 320 // AppKit may already handle that.] |
| 321 const ToolbarModel::SecurityLevel scheme_security_level = |
265 toolbar_model_->GetSchemeSecurityLevel(); | 322 toolbar_model_->GetSchemeSecurityLevel(); |
266 | 323 |
| 324 if (scheme_security_level == ToolbarModel::SECURE) { |
| 325 [field_ setBackgroundColor:SecureBackgroundColor()]; |
| 326 } else if (scheme_security_level == ToolbarModel::NORMAL) { |
| 327 [field_ setBackgroundColor:NormalBackgroundColor()]; |
| 328 } else if (scheme_security_level == ToolbarModel::INSECURE) { |
| 329 [field_ setBackgroundColor:InsecureBackgroundColor()]; |
| 330 } else { |
| 331 NOTREACHED() << "Unexpected scheme_security_level: " |
| 332 << scheme_security_level; |
| 333 } |
| 334 |
267 // Emphasize the scheme for security UI display purposes (if necessary). | 335 // Emphasize the scheme for security UI display purposes (if necessary). |
268 if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() && | 336 if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() && |
269 (scheme_security_level != ToolbarModel::NORMAL)) { | 337 (scheme_security_level != ToolbarModel::NORMAL)) { |
270 // TODO(shess): Pull colors out as constants. | |
271 NSColor* color; | 338 NSColor* color; |
272 if (scheme_security_level == ToolbarModel::SECURE) { | 339 if (scheme_security_level == ToolbarModel::SECURE) { |
273 color = [NSColor blueColor]; | 340 color = SecureSchemeColor(); |
274 } else { | 341 } else { |
275 color = [NSColor blackColor]; | 342 color = InsecureSchemeColor(); |
276 } | 343 } |
277 [as addAttribute:NSForegroundColorAttributeName value:color | 344 [as addAttribute:NSForegroundColorAttributeName value:color |
278 range:NSMakeRange((NSInteger)parts.scheme.begin, | 345 range:ComponentToNSRange(parts.scheme)]; |
279 (NSInteger)parts.scheme.len)]; | |
280 } | 346 } |
281 | 347 |
282 // TODO(shess): Check that this updates the model's sense of focus | |
283 // correctly. | |
284 [field_ setObjectValue:as]; | 348 [field_ setObjectValue:as]; |
285 if (![field_ currentEditor]) { | |
286 [field_ becomeFirstResponder]; | |
287 DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); | |
288 } | |
289 | |
290 NSRange selected_range = NSMakeRange(user_text_length, [as length]); | |
291 // TODO(shess): What if it didn't get first responder, and there is | |
292 // no field editor? This will do nothing. Well, at least it won't | |
293 // crash. Think of something more productive to do, or prove that | |
294 // it cannot occur and DCHECK appropriately. | |
295 [[field_ currentEditor] setSelectedRange:selected_range]; | |
296 } | 349 } |
297 | 350 |
298 void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged( | 351 void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged( |
299 const std::wstring& display_text, bool save_original_selection) { | 352 const std::wstring& display_text, bool save_original_selection) { |
300 // TODO(shess): I believe this is for when the user arrows around | 353 // TODO(shess): I believe this is for when the user arrows around |
301 // the popup, will be restored if they hit escape. Figure out if | 354 // the popup, will be restored if they hit escape. Figure out if |
302 // that is for certain it. | 355 // that is for certain it. |
303 if (save_original_selection) { | 356 if (save_original_selection) { |
| 357 saved_temporary_selection_ = GetSelectedRange(); |
304 saved_temporary_text_ = GetText(); | 358 saved_temporary_text_ = GetText(); |
305 } | 359 } |
306 | 360 |
307 UpdateAndStyleText(display_text, display_text.size()); | 361 SetWindowTextAndCaretPos(display_text, display_text.size()); |
308 } | 362 } |
309 | 363 |
310 bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged( | 364 bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged( |
311 const std::wstring& display_text, size_t user_text_length) { | 365 const std::wstring& display_text, size_t user_text_length) { |
312 // TODO(shess): Make sure that this actually works. The round trip | 366 // TODO(shess): Make sure that this actually works. The round trip |
313 // to native form and back may mean that it's the same but not the | 367 // to native form and back may mean that it's the same but not the |
314 // same. | 368 // same. |
315 if (display_text == GetText()) { | 369 if (display_text == GetText()) { |
316 return false; | 370 return false; |
317 } | 371 } |
318 | 372 |
319 UpdateAndStyleText(display_text, user_text_length); | 373 UpdateAndStyleText(display_text); |
| 374 DCHECK_LE(user_text_length, display_text.size()); |
| 375 SetSelectedRange(NSMakeRange(user_text_length, display_text.size())); |
320 return true; | 376 return true; |
321 } | 377 } |
322 | 378 |
323 void AutocompleteEditViewMac::OnRevertTemporaryText() { | 379 void AutocompleteEditViewMac::OnRevertTemporaryText() { |
324 UpdateAndStyleText(saved_temporary_text_, saved_temporary_text_.size()); | 380 UpdateAndStyleText(saved_temporary_text_); |
325 saved_temporary_text_.clear(); | 381 saved_temporary_text_.clear(); |
| 382 SetSelectedRange(saved_temporary_selection_); |
326 } | 383 } |
327 | 384 |
328 void AutocompleteEditViewMac::OnBeforePossibleChange() { | 385 void AutocompleteEditViewMac::OnBeforePossibleChange() { |
329 selection_before_change_ = GetSelectedRange(); | 386 selection_before_change_ = GetSelectedRange(); |
330 text_before_change_ = GetText(); | 387 text_before_change_ = GetText(); |
331 } | 388 } |
332 | 389 |
333 bool AutocompleteEditViewMac::OnAfterPossibleChange() { | 390 bool AutocompleteEditViewMac::OnAfterPossibleChange() { |
334 NSRange new_selection(GetSelectedRange()); | 391 const NSRange new_selection(GetSelectedRange()); |
335 std::wstring new_text(GetText()); | 392 const std::wstring new_text(GetText()); |
336 const size_t length = new_text.length(); | 393 const size_t length = new_text.length(); |
337 | 394 |
338 bool selection_differs = !NSEqualRanges(new_selection, | 395 const bool selection_differs = !NSEqualRanges(new_selection, |
339 selection_before_change_); | 396 selection_before_change_); |
340 bool at_end_of_edit = (length == new_selection.location); | 397 const bool at_end_of_edit = (length == new_selection.location); |
341 bool text_differs = (new_text != text_before_change_); | 398 const bool text_differs = (new_text != text_before_change_); |
342 | 399 |
343 // When the user has deleted text, we don't allow inline | 400 // When the user has deleted text, we don't allow inline |
344 // autocomplete. This is assumed if the text has gotten shorter AND | 401 // autocomplete. This is assumed if the text has gotten shorter AND |
345 // the selection has shifted towards the front of the text. During | 402 // the selection has shifted towards the front of the text. During |
346 // normal typing the text will almost always be shorter (as the new | 403 // normal typing the text will almost always be shorter (as the new |
347 // input replaces the autocomplete suggestion), but in that case the | 404 // input replaces the autocomplete suggestion), but in that case the |
348 // selection point will have moved towards the end of the text. | 405 // selection point will have moved towards the end of the text. |
349 // TODO(shess): In our implementation, we can catch -deleteBackward: | 406 // TODO(shess): In our implementation, we can catch -deleteBackward: |
350 // and other methods to provide positive knowledge that a delete | 407 // and other methods to provide positive knowledge that a delete |
351 // occured, rather than intuiting it from context. Consider whether | 408 // occured, rather than intuiting it from context. Consider whether |
352 // that would be a stronger approach. | 409 // that would be a stronger approach. |
353 bool just_deleted_text = | 410 const bool just_deleted_text = |
354 (length < text_before_change_.length() && | 411 (length < text_before_change_.length() && |
355 new_selection.location <= selection_before_change_.location); | 412 new_selection.location <= selection_before_change_.location); |
356 | 413 |
357 bool something_changed = model_->OnAfterPossibleChange(new_text, | 414 const bool something_changed = model_->OnAfterPossibleChange(new_text, |
358 selection_differs, text_differs, just_deleted_text, at_end_of_edit); | 415 selection_differs, text_differs, just_deleted_text, at_end_of_edit); |
359 | 416 |
360 // TODO(shess): Restyle the text if something_changed. Not fixing | 417 // Restyle if the user changed something. |
361 // now because styling is currently broken. | 418 // TODO(shess): Does this need to diver deeper to avoid flashing? |
| 419 // For instance, if the user types "/" after the hostname, will it |
| 420 // show as HostTextColor() for an instant before being replaced by |
| 421 // BaseTextColor(). This could probably be done by subclassing the |
| 422 // cell and intercepting draw requests so that we draw the right |
| 423 // thing every time. |
| 424 // NOTE(shess): That kind of thing would also help with when the |
| 425 // flashing from when the user's typing replaces the selection which |
| 426 // is then re-created with the new autocomplete results. |
| 427 if (something_changed) { |
| 428 UpdateAndStyleText(new_text); |
| 429 SetSelectedRange(new_selection); |
| 430 } |
362 | 431 |
363 return something_changed; | 432 return something_changed; |
364 } | 433 } |
365 | 434 |
366 void AutocompleteEditViewMac::OnUpOrDownKeyPressed(bool up, bool by_page) { | 435 void AutocompleteEditViewMac::OnUpOrDownKeyPressed(bool up, bool by_page) { |
367 int count = by_page ? model_->result().size() : 1; | 436 const int count = by_page ? model_->result().size() : 1; |
368 model_->OnUpOrDownKeyPressed(up ? -count : count); | 437 model_->OnUpOrDownKeyPressed(up ? -count : count); |
369 } | 438 } |
370 void AutocompleteEditViewMac::OnEscapeKeyPressed() { | 439 void AutocompleteEditViewMac::OnEscapeKeyPressed() { |
371 model_->OnEscapeKeyPressed(); | 440 model_->OnEscapeKeyPressed(); |
372 } | 441 } |
373 void AutocompleteEditViewMac::OnSetFocus(bool f) { | 442 void AutocompleteEditViewMac::OnSetFocus(bool f) { |
374 model_->OnSetFocus(f); | 443 model_->OnSetFocus(f); |
375 } | 444 } |
376 void AutocompleteEditViewMac::OnKillFocus() { | 445 void AutocompleteEditViewMac::OnKillFocus() { |
377 model_->OnKillFocus(); | 446 model_->OnKillFocus(); |
378 } | 447 } |
379 void AutocompleteEditViewMac::AcceptInput( | 448 void AutocompleteEditViewMac::AcceptInput( |
380 WindowOpenDisposition disposition, bool for_drop) { | 449 WindowOpenDisposition disposition, bool for_drop) { |
381 model_->AcceptInput(disposition, for_drop); | 450 model_->AcceptInput(disposition, for_drop); |
382 } | 451 } |
383 | 452 |
384 void AutocompleteEditViewMac::FocusLocation() { | 453 void AutocompleteEditViewMac::FocusLocation() { |
385 // -makeFirstResponder: will select the entire field_. If we're | 454 // -makeFirstResponder: will select the entire field_. If we're |
386 // already firstResponder, it's likely that we want to retain the | 455 // already firstResponder, it's likely that we want to retain the |
387 // current selection. | 456 // current selection. |
388 if (![field_ currentEditor]) { | 457 if (![field_ currentEditor]) { |
389 [[field_ window] makeFirstResponder:field_]; | 458 [[field_ window] makeFirstResponder:field_]; |
| 459 DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); |
390 } | 460 } |
391 } | 461 } |
392 | 462 |
393 @implementation AutocompleteFieldDelegate | 463 @implementation AutocompleteFieldDelegate |
394 | 464 |
395 - initWithEditView:(AutocompleteEditViewMac*)view { | 465 - initWithEditView:(AutocompleteEditViewMac*)view { |
396 self = [super init]; | 466 self = [super init]; |
397 if (self) { | 467 if (self) { |
398 edit_view_ = view; | 468 edit_view_ = view; |
399 } | 469 } |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 } | 525 } |
456 | 526 |
457 - (void)controlTextDidEndEditing:(NSNotification*)aNotification { | 527 - (void)controlTextDidEndEditing:(NSNotification*)aNotification { |
458 edit_view_->OnKillFocus(); | 528 edit_view_->OnKillFocus(); |
459 | 529 |
460 // TODO(shess): Figure out where the selection belongs. On GTK, | 530 // TODO(shess): Figure out where the selection belongs. On GTK, |
461 // it's set to the start of the text. | 531 // it's set to the start of the text. |
462 } | 532 } |
463 | 533 |
464 @end | 534 @end |
OLD | NEW |