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

Side by Side Diff: chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm

Issue 1099403005: [AiS] changing mac omnibox suggestions form NSMatrix to NSTableView (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Removed withView arg from highlightRowAt Created 5 years, 7 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 unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h" 5 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 9
10 #include "base/i18n/rtl.h" 10 #include "base/i18n/rtl.h"
11 #include "base/mac/scoped_nsobject.h" 11 #include "base/mac/scoped_nsobject.h"
12 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h" 13 #include "base/strings/string_util.h"
14 #include "base/strings/sys_string_conversions.h" 14 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" 16 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h"
17 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" 17 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
18 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" 18 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
19 #include "chrome/grit/generated_resources.h" 19 #include "chrome/grit/generated_resources.h"
20 #include "components/omnibox/suggestion_answer.h" 20 #include "components/omnibox/suggestion_answer.h"
21 #include "ui/base/l10n/l10n_util.h" 21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/gfx/font.h" 22 #include "ui/gfx/font.h"
23 23
24 namespace { 24 namespace {
25 25
26 // How much to adjust the cell sizing up from the default determined
27 // by the font.
28 const CGFloat kCellHeightAdjust = 6.0;
29
26 // How far to offset image column from the left. 30 // How far to offset image column from the left.
27 const CGFloat kImageXOffset = 5.0; 31 const CGFloat kImageXOffset = 5.0;
28 32
29 // How far to offset the text column from the left. 33 // How far to offset the text column from the left.
30 const CGFloat kTextStartOffset = 28.0; 34 const CGFloat kTextStartOffset = 28.0;
31 35
32 // Rounding radius of selection and hover background on popup items. 36 // Rounding radius of selection and hover background on popup items.
33 const CGFloat kCellRoundingRadius = 2.0; 37 const CGFloat kCellRoundingRadius = 2.0;
34 38
35 // Flips the given |rect| in context of the given |frame|. 39 // Flips the given |rect| in context of the given |frame|.
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 value:DimTextColor() 152 value:DimTextColor()
149 range:range]; 153 range:range];
150 } 154 }
151 } 155 }
152 156
153 return as; 157 return as;
154 } 158 }
155 159
156 } // namespace 160 } // namespace
157 161
158 @implementation OmniboxPopupCell 162 @interface OmniboxPopupCell ()
163 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
164 withFrame:(NSRect)cellFrame
165 atOffset:(CGFloat)offset
166 withMaxWidth:(int)maxWidth
167 inView:(NSView*)controlView;
168 @end
159 169
160 - (id)init { 170 @implementation OmniboxPopupCellData
161 self = [super init];
162 if (self) {
163 [self setImagePosition:NSImageLeft];
164 [self setBordered:NO];
165 [self setButtonType:NSRadioButton];
166 171
167 // Without this highlighting messes up white areas of images. 172 - (id)initWithMatch:(const AutocompleteMatch&)match image:(NSImage*)image {
168 [self setHighlightsBy:NSNoCellMask]; 173 if ((self = [super init])) {
174 image_.reset([image retain]);
169 175
170 const base::string16& raw_separator = 176 const base::string16& raw_separator =
171 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); 177 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
172 separator_.reset( 178 separator_.reset(
173 [CreateAttributedString(raw_separator, DimTextColor()) retain]); 179 [CreateAttributedString(raw_separator, DimTextColor()) retain]);
180
181 isContentsRTL_ =
182 (base::i18n::RIGHT_TO_LEFT ==
183 base::i18n::GetFirstStrongCharacterDirection(match.contents));
184 matchType_ = match.type;
185
186 // Prefix may not have any characters with strong directionality, and may
187 // take
188 // the UI directionality. But prefix needs to appear in continuation of the
189 // contents so we force the directionality.
Scott Hess - ex-Googler 2015/05/21 20:40:26 Reformat comment.
dschuyler 2015/05/26 18:40:20 Done.
190 NSTextAlignment textAlignment =
191 isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment;
192 prefix_.reset(
193 [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo(
194 kACMatchPropertyContentsPrefix)),
195 ContentTextColor(), textAlignment) retain]);
196
197 contents_.reset([CreateClassifiedAttributedString(
198 match.contents, ContentTextColor(), match.contents_class) retain]);
199
200 if (match.answer) {
201 base::string16 answerString;
202 DCHECK(!match.answer->second_line().text_fields().empty());
203 for (const SuggestionAnswer::TextField& textField :
204 match.answer->second_line().text_fields())
205 answerString.append(textField.text());
206 const base::string16 space(base::ASCIIToUTF16(" "));
207 const SuggestionAnswer::TextField* textField =
208 match.answer->second_line().additional_text();
209 if (textField)
210 answerString.append(space).append(textField->text());
211 textField = match.answer->second_line().status_text();
212 if (textField)
213 answerString.append(space).append(textField->text());
214 description_.reset([CreateClassifiedAttributedString(
215 answerString, DimTextColor(), match.description_class) retain]);
216 } else if (match.description.empty()) {
217 description_.reset();
218 } else {
219 description_.reset([CreateClassifiedAttributedString(
220 match.description, DimTextColor(), match.description_class) retain]);
221 }
174 } 222 }
175 return self; 223 return self;
176 } 224 }
177 225
178 - (void)setMatch:(const AutocompleteMatch&)match { 226 - (void)setContents:(NSAttributedString*)contents {
179 match_ = match; 227 contents_.reset([contents retain]);
180 NSAttributedString *contents = CreateClassifiedAttributedString( 228 }
181 match_.contents, ContentTextColor(), match_.contents_class);
182 [self setAttributedTitle:contents];
183 229
184 if (match_.answer) { 230 - (void)setImage:(NSImage*)image {
185 base::string16 answerString; 231 image_.reset([image retain]);
186 DCHECK(!match_.answer->second_line().text_fields().empty());
187 for (const SuggestionAnswer::TextField& textField :
188 match_.answer->second_line().text_fields())
189 answerString += textField.text();
190 const base::char16 space(' ');
191 const SuggestionAnswer::TextField* textField =
192 match_.answer->second_line().additional_text();
193 if (textField)
194 answerString += space + textField->text();
195 textField = match_.answer->second_line().status_text();
196 if (textField)
197 answerString += space + textField->text();
198 description_.reset([CreateClassifiedAttributedString(
199 answerString, DimTextColor(), match_.description_class) retain]);
200 } else if (match_.description.empty()) {
201 description_.reset();
202 } else {
203 description_.reset([CreateClassifiedAttributedString(
204 match_.description, DimTextColor(), match_.description_class) retain]);
205 }
206 } 232 }
207 233
208 - (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth { 234 - (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth {
209 maxMatchContentsWidth_ = maxMatchContentsWidth; 235 maxMatchContentsWidth_ = maxMatchContentsWidth;
210 } 236 }
211 237
212 - (void)setContentsOffset:(CGFloat)contentsOffset { 238 - (void)setContentsOffset:(CGFloat)contentsOffset {
213 contentsOffset_ = contentsOffset; 239 contentsOffset_ = contentsOffset;
214 } 240 }
215 241
216 // The default NSButtonCell drawing leaves the image flush left and 242 - (CGFloat)getMatchContentsWidth {
217 // the title next to the image. This spaces things out to line up 243 return [contents_ size].width;
218 // with the star button and autocomplete field. 244 }
219 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { 245
220 if ([self state] == NSOnState || [self isHighlighted]) { 246 - (CGFloat)rowHeight {
221 if ([self state] == NSOnState) 247 return [image_ size].height + kCellHeightAdjust;
Scott Hess - ex-Googler 2015/05/21 20:40:26 I realize that the current version doesn't have th
dschuyler 2015/05/26 18:40:20 Done.
222 [SelectedBackgroundColor() set]; 248 }
223 else 249
224 [HoveredBackgroundColor() set]; 250 - (void)drawMatchWithFrame:(NSRect)cellFrame
225 NSBezierPath* path = 251 inCell:(OmniboxPopupCell*)cell
226 [NSBezierPath bezierPathWithRoundedRect:cellFrame 252 inView:(NSView*)controlView {
227 xRadius:kCellRoundingRadius 253 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
228 yRadius:kCellRoundingRadius]; 254 CGFloat contentsWidth = [self getMatchContentsWidth];
229 [path fill]; 255 CGFloat separatorWidth = [separator_ size].width;
230 } 256 CGFloat descriptionWidth = [description_ size].width;
257 int contentsMaxWidth, descriptionMaxWidth;
258 OmniboxPopupModel::ComputeMatchMaxWidths(
259 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth),
260 ceilf(remainingWidth), !AutocompleteMatch::IsSearchType(matchType_),
261 &contentsMaxWidth, &descriptionMaxWidth);
231 262
232 // Put the image centered vertically but in a fixed column. 263 // Put the image centered vertically but in a fixed column.
233 NSImage* image = [self image]; 264 if (image_) {
234 if (image) {
235 NSRect imageRect = cellFrame; 265 NSRect imageRect = cellFrame;
236 imageRect.size = [image size]; 266 imageRect.size = [image_ size];
237 imageRect.origin.y += 267 imageRect.origin.y +=
238 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0); 268 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0);
239 imageRect.origin.x += kImageXOffset; 269 imageRect.origin.x += kImageXOffset;
240 [image drawInRect:FlipIfRTL(imageRect, cellFrame) 270 [image_ drawInRect:FlipIfRTL(imageRect, cellFrame)
241 fromRect:NSZeroRect // Entire image 271 fromRect:NSZeroRect
242 operation:NSCompositeSourceOver 272 operation:NSCompositeSourceOver
243 fraction:1.0 273 fraction:1.0
244 respectFlipped:YES 274 respectFlipped:YES
245 hints:nil]; 275 hints:nil];
246 } 276 }
247 277
248 [self drawMatchWithFrame:cellFrame inView:controlView];
249 }
250
251 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
252 NSAttributedString* contents = [self attributedTitle];
253
254 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
255 CGFloat contentsWidth = [self getMatchContentsWidth];
256 CGFloat separatorWidth = [separator_ size].width;
257 CGFloat descriptionWidth = description_.get() ? [description_ size].width : 0;
258 int contentsMaxWidth, descriptionMaxWidth;
259 OmniboxPopupModel::ComputeMatchMaxWidths(
260 ceilf(contentsWidth),
261 ceilf(separatorWidth),
262 ceilf(descriptionWidth),
263 ceilf(remainingWidth),
264 !AutocompleteMatch::IsSearchType(match_.type),
265 &contentsMaxWidth,
266 &descriptionMaxWidth);
267
268 CGFloat offset = kTextStartOffset; 278 CGFloat offset = kTextStartOffset;
269 if (match_.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { 279 if (matchType_ == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
270 // Infinite suggestions are rendered with a prefix (usually ellipsis), which 280 // Infinite suggestions are rendered with a prefix (usually ellipsis), which
271 // appear vertically stacked. 281 // appear vertically stacked.
272 offset += [self drawMatchPrefixWithFrame:cellFrame 282 offset += [self drawMatchPrefixWithFrame:cellFrame
283 inCell:cell
273 inView:controlView 284 inView:controlView
274 withContentsMaxWidth:&contentsMaxWidth]; 285 withContentsMaxWidth:&contentsMaxWidth];
275 } 286 }
276 offset += [self drawMatchPart:contents 287 offset += [cell drawMatchPart:contents_
277 withFrame:cellFrame 288 withFrame:cellFrame
278 atOffset:offset 289 atOffset:offset
279 withMaxWidth:contentsMaxWidth 290 withMaxWidth:contentsMaxWidth
280 inView:controlView]; 291 inView:controlView];
281 292
282 if (descriptionMaxWidth != 0) { 293 if (descriptionMaxWidth != 0) {
283 offset += [self drawMatchPart:separator_ 294 offset += [cell drawMatchPart:separator_
284 withFrame:cellFrame 295 withFrame:cellFrame
285 atOffset:offset 296 atOffset:offset
286 withMaxWidth:separatorWidth 297 withMaxWidth:separatorWidth
287 inView:controlView]; 298 inView:controlView];
288 offset += [self drawMatchPart:description_ 299 offset += [cell drawMatchPart:description_
289 withFrame:cellFrame 300 withFrame:cellFrame
290 atOffset:offset 301 atOffset:offset
291 withMaxWidth:descriptionMaxWidth 302 withMaxWidth:descriptionMaxWidth
292 inView:controlView]; 303 inView:controlView];
293 } 304 }
294 } 305 }
295 306
296 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame 307 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
308 inCell:(OmniboxPopupCell*)cell
297 inView:(NSView*)controlView 309 inView:(NSView*)controlView
298 withContentsMaxWidth:(int*)contentsMaxWidth { 310 withContentsMaxWidth:(int*)contentsMaxWidth {
299 CGFloat offset = 0.0f; 311 CGFloat offset = 0.0f;
300 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); 312 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
301 bool isRTL = base::i18n::IsRTL();
302 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT ==
303 base::i18n::GetFirstStrongCharacterDirection(match_.contents));
304 // Prefix may not have any characters with strong directionality, and may take
305 // the UI directionality. But prefix needs to appear in continuation of the
306 // contents so we force the directionality.
307 NSTextAlignment textAlignment = isContentsRTL ?
308 NSRightTextAlignment : NSLeftTextAlignment;
309 prefix_.reset([CreateAttributedString(base::UTF8ToUTF16(
310 match_.GetAdditionalInfo(kACMatchPropertyContentsPrefix)),
311 ContentTextColor(), textAlignment) retain]);
312 CGFloat prefixWidth = [prefix_ size].width; 313 CGFloat prefixWidth = [prefix_ size].width;
313 314
314 CGFloat prefixOffset = 0.0f; 315 CGFloat prefixOffset = 0.0f;
315 if (isRTL != isContentsRTL) { 316 if (base::i18n::IsRTL() != isContentsRTL_) {
316 // The contents is rendered between the contents offset extending towards 317 // The contents is rendered between the contents offset extending towards
317 // the start edge, while prefix is rendered in opposite direction. Ideally 318 // the start edge, while prefix is rendered in opposite direction. Ideally
318 // the prefix should be rendered at |contentsOffset_|. If that is not 319 // the prefix should be rendered at |contentsOffset_|. If that is not
319 // sufficient to render the widest suggestion, we increase it to 320 // sufficient to render the widest suggestion, we increase it to
320 // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to 321 // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to
321 // accommodate that, we reduce the offset so that the prefix gets rendered. 322 // accommodate that, we reduce the offset so that the prefix gets rendered.
322 prefixOffset = std::min( 323 prefixOffset = std::min(
323 remainingWidth - prefixWidth, std::max(contentsOffset_, 324 remainingWidth - prefixWidth, std::max(contentsOffset_,
324 maxMatchContentsWidth_)); 325 maxMatchContentsWidth_));
325 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth); 326 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth);
326 } else { // The direction of contents is same as UI direction. 327 } else { // The direction of contents is same as UI direction.
327 // Ideally the offset should be |contentsOffset_|. If the max total width 328 // Ideally the offset should be |contentsOffset_|. If the max total width
328 // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the 329 // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the
329 // |remainingWidth|, then we shift the offset to the left , so that all 330 // |remainingWidth|, then we shift the offset to the left , so that all
330 // postfix suggestions are visible. 331 // postfix suggestions are visible.
331 // We have to render the prefix, so offset has to be at least |prefixWidth|. 332 // We have to render the prefix, so offset has to be at least |prefixWidth|.
332 offset = std::max(prefixWidth, 333 offset = std::max(
334 prefixWidth,
333 std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_)); 335 std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_));
334 prefixOffset = offset - prefixWidth; 336 prefixOffset = offset - prefixWidth;
335 } 337 }
336 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), 338 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth),
337 *contentsMaxWidth); 339 *contentsMaxWidth);
338 [self drawMatchPart:prefix_ 340 [cell drawMatchPart:prefix_
339 withFrame:cellFrame 341 withFrame:cellFrame
340 atOffset:prefixOffset + kTextStartOffset 342 atOffset:prefixOffset + kTextStartOffset
341 withMaxWidth:prefixWidth 343 withMaxWidth:prefixWidth
342 inView:controlView]; 344 inView:controlView];
343 return offset; 345 return offset;
344 } 346 }
345 347
346 - (CGFloat)drawMatchPart:(NSAttributedString*)as 348 @end
349
350 @implementation OmniboxPopupCell
351
352 - (id)copyWithZone:(NSZone*)zone {
353 OmniboxPopupCell* copy = [super copyWithZone:zone];
354 // The representedObject is set to nil in the copy.
Scott Hess - ex-Googler 2015/05/21 20:40:27 What did they do, put _all_ of the special edge ca
groby-ooo-7-16 2015/05/22 05:03:07 Hey, at least this one is documented. (Under repre
dschuyler 2015/05/26 18:40:19 Acknowledged.
dschuyler 2015/05/26 18:40:19 I'm looking into the objectValue vs representedObj
355 [copy setRepresentedObject:[self representedObject]];
356 return copy;
357 }
358
359 // The default NSButtonCell drawing leaves the image flush left and
groby-ooo-7-16 2015/05/21 03:01:48 Comment doesn't apply any more - it's just an NSCe
dschuyler 2015/05/26 18:40:19 Done.
360 // the title next to the image. This spaces things out to line up
361 // with the star button and autocomplete field.
362 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
363 if ([self state] == NSOnState || [self isHighlighted]) {
364 if ([self state] == NSOnState)
365 [SelectedBackgroundColor() set];
366 else
367 [HoveredBackgroundColor() set];
368 NSBezierPath* path =
369 [NSBezierPath bezierPathWithRoundedRect:cellFrame
370 xRadius:kCellRoundingRadius
371 yRadius:kCellRoundingRadius];
372 [path fill];
373 }
374
375 base::scoped_nsobject<OmniboxPopupCellData> cellData(
376 [self representedObject]);
groby-ooo-7-16 2015/05/21 03:01:48 No need to scope - representedObject (and objectVa
Scott Hess - ex-Googler 2015/05/21 20:40:26 Scoping is wrong, because -representedObject is no
dschuyler 2015/05/26 18:40:20 Done.
dschuyler 2015/05/26 18:40:20 Acknowledged.
377 [cellData drawMatchWithFrame:cellFrame inCell:self inView:controlView];
378 }
379
380 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
347 withFrame:(NSRect)cellFrame 381 withFrame:(NSRect)cellFrame
348 atOffset:(CGFloat)offset 382 atOffset:(CGFloat)offset
349 withMaxWidth:(int)maxWidth 383 withMaxWidth:(int)maxWidth
350 inView:(NSView*)controlView { 384 inView:(NSView*)controlView {
351 if (offset > NSWidth(cellFrame)) 385 if (offset > NSWidth(cellFrame))
352 return 0.0f; 386 return 0.0f;
353 NSRect renderRect = ShiftRect(cellFrame, offset); 387 NSRect renderRect = ShiftRect(cellFrame, offset);
354 renderRect.size.width = 388 renderRect.size.width =
355 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); 389 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth));
356 if (renderRect.size.width != 0) { 390 NSRect textRect =
357 [self drawTitle:as 391 [attributedString boundingRectWithSize:renderRect.size options:nil];
358 withFrame:FlipIfRTL(renderRect, cellFrame) 392 renderRect.origin.y +=
359 inView:controlView]; 393 std::floor((NSHeight(cellFrame) - NSHeight(textRect)) / 2.0);
360 } 394 if (NSWidth(renderRect) != 0)
Scott Hess - ex-Googler 2015/05/21 20:40:26 I'd say > 0.0. Just because I'm that paranoid.
groby-ooo-7-16 2015/05/22 05:03:07 Let's be properly paranoid and say !NSIsEmptyRect(
dschuyler 2015/05/26 18:40:20 Done.
395 [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)];
361 return NSWidth(renderRect); 396 return NSWidth(renderRect);
362 } 397 }
363 398
364 - (CGFloat)getMatchContentsWidth {
365 NSAttributedString* contents = [self attributedTitle];
366 return contents ? [contents size].width : 0;
367 }
368
369
370 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { 399 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match {
371 const base::string16& inputText = base::UTF8ToUTF16( 400 const base::string16& inputText = base::UTF8ToUTF16(
372 match.GetAdditionalInfo(kACMatchPropertyInputText)); 401 match.GetAdditionalInfo(kACMatchPropertyInputText));
373 int contentsStartIndex = 0; 402 int contentsStartIndex = 0;
374 base::StringToInt( 403 base::StringToInt(
375 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), 404 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex),
376 &contentsStartIndex); 405 &contentsStartIndex);
377 // Ignore invalid state. 406 // Ignore invalid state.
378 if (!StartsWith(match.fill_into_edit, inputText, true) 407 if (!StartsWith(match.fill_into_edit, inputText, true)
379 || !EndsWith(match.fill_into_edit, match.contents, true) 408 || !EndsWith(match.fill_into_edit, match.contents, true)
380 || ((size_t)contentsStartIndex >= inputText.length())) { 409 || ((size_t)contentsStartIndex >= inputText.length())) {
381 return 0; 410 return 0;
382 } 411 }
383 bool isRTL = base::i18n::IsRTL(); 412 bool isRTL = base::i18n::IsRTL();
384 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == 413 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT ==
385 base::i18n::GetFirstStrongCharacterDirection(match.contents)); 414 base::i18n::GetFirstStrongCharacterDirection(match.contents));
386 415
387 // Color does not matter. 416 // Color does not matter.
388 NSAttributedString* as = CreateAttributedString(inputText, DimTextColor()); 417 NSAttributedString* attributedString =
389 base::scoped_nsobject<NSTextStorage> textStorage([[NSTextStorage alloc] 418 CreateAttributedString(inputText, DimTextColor());
390 initWithAttributedString:as]); 419 base::scoped_nsobject<NSTextStorage> textStorage(
420 [[NSTextStorage alloc] initWithAttributedString:attributedString]);
391 base::scoped_nsobject<NSLayoutManager> layoutManager( 421 base::scoped_nsobject<NSLayoutManager> layoutManager(
392 [[NSLayoutManager alloc] init]); 422 [[NSLayoutManager alloc] init]);
393 base::scoped_nsobject<NSTextContainer> textContainer( 423 base::scoped_nsobject<NSTextContainer> textContainer(
394 [[NSTextContainer alloc] init]); 424 [[NSTextContainer alloc] init]);
395 [layoutManager addTextContainer:textContainer]; 425 [layoutManager addTextContainer:textContainer];
396 [textStorage addLayoutManager:layoutManager]; 426 [textStorage addLayoutManager:layoutManager];
397 427
398 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex); 428 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex);
399 NSUInteger glyphIndex = 429 NSUInteger glyphIndex =
400 [layoutManager glyphIndexForCharacterAtIndex:charIndex]; 430 [layoutManager glyphIndexForCharacterAtIndex:charIndex];
401 431
402 // This offset is computed from the left edge of the glyph always from the 432 // This offset is computed from the left edge of the glyph always from the
403 // left edge of the string, irrespective of the directionality of UI or text. 433 // left edge of the string, irrespective of the directionality of UI or text.
404 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 434 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
405 435
406 CGFloat inputWidth = [as size].width; 436 CGFloat inputWidth = [attributedString size].width;
407 437
408 // The offset obtained above may need to be corrected because the left-most 438 // The offset obtained above may need to be corrected because the left-most
409 // glyph may not have 0 offset. So we find the offset of left-most glyph, and 439 // glyph may not have 0 offset. So we find the offset of left-most glyph, and
410 // subtract it from the offset of the glyph we obtained above. 440 // subtract it from the offset of the glyph we obtained above.
411 CGFloat minOffset = glyphOffset; 441 CGFloat minOffset = glyphOffset;
412 442
413 // If content is RTL, we are interested in the right-edge of the glyph. 443 // If content is RTL, we are interested in the right-edge of the glyph.
414 // Unfortunately the bounding rect computation methods from NSLayoutManager or 444 // Unfortunately the bounding rect computation methods from NSLayoutManager or
415 // NSFont don't work correctly with bidirectional text. So we compute the 445 // NSFont don't work correctly with bidirectional text. So we compute the
416 // glyph width by finding the closest glyph offset to the right of the glyph 446 // glyph width by finding the closest glyph offset to the right of the glyph
417 // we are looking for. 447 // we are looking for.
418 CGFloat glyphWidth = inputWidth; 448 CGFloat glyphWidth = inputWidth;
419 449
420 for (NSUInteger i = 0; i < [as length]; i++) { 450 for (NSUInteger i = 0; i < [attributedString length]; i++) {
421 if (i == charIndex) continue; 451 if (i == charIndex) continue;
422 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i]; 452 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i];
423 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 453 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
424 minOffset = std::min(minOffset, offset); 454 minOffset = std::min(minOffset, offset);
425 if (offset > glyphOffset) 455 if (offset > glyphOffset)
426 glyphWidth = std::min(glyphWidth, offset - glyphOffset); 456 glyphWidth = std::min(glyphWidth, offset - glyphOffset);
427 } 457 }
428 glyphOffset -= minOffset; 458 glyphOffset -= minOffset;
429 if (glyphWidth == 0) 459 if (glyphWidth == 0)
430 glyphWidth = inputWidth - glyphOffset; 460 glyphWidth = inputWidth - glyphOffset;
431 if (isContentsRTL) 461 if (isContentsRTL)
432 glyphOffset += glyphWidth; 462 glyphOffset += glyphWidth;
433 return isRTL ? (inputWidth - glyphOffset) : glyphOffset; 463 return isRTL ? (inputWidth - glyphOffset) : glyphOffset;
434 } 464 }
435 465
436 @end 466 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698