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

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 cell data rect 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 =
groby-ooo-7-16 2015/05/20 01:02:30 Looks like nobody cares about raw_separator any mo
dschuyler 2015/05/21 00:38:39 I don't follow what ya mean...(?)
groby-ooo-7-16 2015/05/21 03:01:47 Never mind, my brain mis-parsed the next line. Ei
dschuyler 2015/05/26 18:40:19 Done.
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.
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());
groby-ooo-7-16 2015/05/20 01:02:30 We don't care about type just yet, I assume?
dschuyler 2015/05/21 00:38:39 Not for this CL, please.
groby-ooo-7-16 2015/05/21 03:01:47 Acknowledged.
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_ ? [contents_ size].width : 0;
groby-ooo-7-16 2015/05/20 01:02:30 return [contents_ size].width. See https://develop
dschuyler 2015/05/21 00:38:39 Good to know, thanks! Done.
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;
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_ ? [description_ size].width : 0;
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 - (void)setCellData:(OmniboxPopupCellData*)cellData {
353 cellData_.reset([cellData retain]);
354 }
355
356 - (id)copyWithZone:(NSZone*)zone {
357 // Note: Before changing this code, please check with groby, shess, or
358 // dschuyler about why it's written with swaps and local data.
359 base::scoped_nsobject<OmniboxPopupCellData> cellData;
360 cellData.swap(cellData_);
361 OmniboxPopupCell* copy = [super copyWithZone:zone];
362 copy->cellData_ = cellData;
363 cellData_.swap(cellData);
364 return copy;
365 }
366
367 // The default NSButtonCell drawing leaves the image flush left and
368 // the title next to the image. This spaces things out to line up
369 // with the star button and autocomplete field.
370 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
371 if ([self state] == NSOnState || [self isHighlighted]) {
372 if ([self state] == NSOnState)
373 [SelectedBackgroundColor() set];
374 else
375 [HoveredBackgroundColor() set];
376 NSBezierPath* path =
377 [NSBezierPath bezierPathWithRoundedRect:cellFrame
378 xRadius:kCellRoundingRadius
379 yRadius:kCellRoundingRadius];
380 [path fill];
381 }
382
383 [cellData_ drawMatchWithFrame:cellFrame inCell:self inView:controlView];
384 }
385
386 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
347 withFrame:(NSRect)cellFrame 387 withFrame:(NSRect)cellFrame
348 atOffset:(CGFloat)offset 388 atOffset:(CGFloat)offset
349 withMaxWidth:(int)maxWidth 389 withMaxWidth:(int)maxWidth
350 inView:(NSView*)controlView { 390 inView:(NSView*)controlView {
351 if (offset > NSWidth(cellFrame)) 391 if (offset > NSWidth(cellFrame))
352 return 0.0f; 392 return 0.0f;
353 NSRect renderRect = ShiftRect(cellFrame, offset); 393 NSRect renderRect = ShiftRect(cellFrame, offset);
354 renderRect.size.width = 394 renderRect.size.width =
355 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); 395 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth));
356 if (renderRect.size.width != 0) { 396 NSRect textRect =
357 [self drawTitle:as 397 [attributedString boundingRectWithSize:renderRect.size options:nil];
358 withFrame:FlipIfRTL(renderRect, cellFrame) 398 renderRect.origin.y +=
359 inView:controlView]; 399 std::floor((NSHeight(cellFrame) - NSHeight(textRect)) / 2.0);
360 } 400 if (renderRect.size.width != 0)
groby-ooo-7-16 2015/05/20 01:02:30 NSWidth(renderRect), please
dschuyler 2015/05/21 00:38:39 Done.
401 [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)];
361 return NSWidth(renderRect); 402 return NSWidth(renderRect);
362 } 403 }
363 404
364 - (CGFloat)getMatchContentsWidth {
365 NSAttributedString* contents = [self attributedTitle];
366 return contents ? [contents size].width : 0;
367 }
368
369
370 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { 405 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match {
371 const base::string16& inputText = base::UTF8ToUTF16( 406 const base::string16& inputText = base::UTF8ToUTF16(
372 match.GetAdditionalInfo(kACMatchPropertyInputText)); 407 match.GetAdditionalInfo(kACMatchPropertyInputText));
373 int contentsStartIndex = 0; 408 int contentsStartIndex = 0;
374 base::StringToInt( 409 base::StringToInt(
375 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), 410 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex),
376 &contentsStartIndex); 411 &contentsStartIndex);
377 // Ignore invalid state. 412 // Ignore invalid state.
378 if (!StartsWith(match.fill_into_edit, inputText, true) 413 if (!StartsWith(match.fill_into_edit, inputText, true)
379 || !EndsWith(match.fill_into_edit, match.contents, true) 414 || !EndsWith(match.fill_into_edit, match.contents, true)
380 || ((size_t)contentsStartIndex >= inputText.length())) { 415 || ((size_t)contentsStartIndex >= inputText.length())) {
381 return 0; 416 return 0;
382 } 417 }
383 bool isRTL = base::i18n::IsRTL(); 418 bool isRTL = base::i18n::IsRTL();
384 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == 419 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT ==
385 base::i18n::GetFirstStrongCharacterDirection(match.contents)); 420 base::i18n::GetFirstStrongCharacterDirection(match.contents));
386 421
387 // Color does not matter. 422 // Color does not matter.
388 NSAttributedString* as = CreateAttributedString(inputText, DimTextColor()); 423 NSAttributedString* attributedString =
389 base::scoped_nsobject<NSTextStorage> textStorage([[NSTextStorage alloc] 424 CreateAttributedString(inputText, DimTextColor());
390 initWithAttributedString:as]); 425 base::scoped_nsobject<NSTextStorage> textStorage(
426 [[NSTextStorage alloc] initWithAttributedString:attributedString]);
391 base::scoped_nsobject<NSLayoutManager> layoutManager( 427 base::scoped_nsobject<NSLayoutManager> layoutManager(
392 [[NSLayoutManager alloc] init]); 428 [[NSLayoutManager alloc] init]);
393 base::scoped_nsobject<NSTextContainer> textContainer( 429 base::scoped_nsobject<NSTextContainer> textContainer(
394 [[NSTextContainer alloc] init]); 430 [[NSTextContainer alloc] init]);
395 [layoutManager addTextContainer:textContainer]; 431 [layoutManager addTextContainer:textContainer];
396 [textStorage addLayoutManager:layoutManager]; 432 [textStorage addLayoutManager:layoutManager];
397 433
398 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex); 434 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex);
399 NSUInteger glyphIndex = 435 NSUInteger glyphIndex =
400 [layoutManager glyphIndexForCharacterAtIndex:charIndex]; 436 [layoutManager glyphIndexForCharacterAtIndex:charIndex];
401 437
402 // This offset is computed from the left edge of the glyph always from the 438 // 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. 439 // left edge of the string, irrespective of the directionality of UI or text.
404 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 440 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
405 441
406 CGFloat inputWidth = [as size].width; 442 CGFloat inputWidth = [attributedString size].width;
407 443
408 // The offset obtained above may need to be corrected because the left-most 444 // 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 445 // 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. 446 // subtract it from the offset of the glyph we obtained above.
411 CGFloat minOffset = glyphOffset; 447 CGFloat minOffset = glyphOffset;
412 448
413 // If content is RTL, we are interested in the right-edge of the glyph. 449 // If content is RTL, we are interested in the right-edge of the glyph.
414 // Unfortunately the bounding rect computation methods from NSLayoutManager or 450 // Unfortunately the bounding rect computation methods from NSLayoutManager or
415 // NSFont don't work correctly with bidirectional text. So we compute the 451 // 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 452 // glyph width by finding the closest glyph offset to the right of the glyph
417 // we are looking for. 453 // we are looking for.
418 CGFloat glyphWidth = inputWidth; 454 CGFloat glyphWidth = inputWidth;
419 455
420 for (NSUInteger i = 0; i < [as length]; i++) { 456 for (NSUInteger i = 0; i < [attributedString length]; i++) {
421 if (i == charIndex) continue; 457 if (i == charIndex) continue;
422 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i]; 458 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i];
423 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 459 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
424 minOffset = std::min(minOffset, offset); 460 minOffset = std::min(minOffset, offset);
425 if (offset > glyphOffset) 461 if (offset > glyphOffset)
426 glyphWidth = std::min(glyphWidth, offset - glyphOffset); 462 glyphWidth = std::min(glyphWidth, offset - glyphOffset);
427 } 463 }
428 glyphOffset -= minOffset; 464 glyphOffset -= minOffset;
429 if (glyphWidth == 0) 465 if (glyphWidth == 0)
430 glyphWidth = inputWidth - glyphOffset; 466 glyphWidth = inputWidth - glyphOffset;
431 if (isContentsRTL) 467 if (isContentsRTL)
432 glyphOffset += glyphWidth; 468 glyphOffset += glyphWidth;
433 return isRTL ? (inputWidth - glyphOffset) : glyphOffset; 469 return isRTL ? (inputWidth - glyphOffset) : glyphOffset;
434 } 470 }
435 471
436 @end 472 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698