// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <cmath> #import "chrome/browser/ui/cocoa/location_bar/bubble_decoration.h" #include "base/logging.h" #include "base/mac/foundation_util.h" #import "chrome/browser/ui/cocoa/themed_window.h" #import "ui/base/cocoa/nsview_additions.h" #include "ui/base/material_design/material_design_controller.h" namespace { // This is used to increase the right margin of this decoration. const CGFloat kRightSideMargin = 1.0; // Padding between the icon/label and bubble edges. CGFloat BubblePadding() { return ui::MaterialDesignController::IsModeMaterial() ? 7 : 3; } // Padding between the icon and label. const CGFloat kIconLabelPadding = 4.0; // Inset for the background. const CGFloat kBackgroundYInset = 4.0; } // namespace BubbleDecoration::BubbleDecoration() { attributes_.reset([[NSMutableDictionary alloc] init]); [attributes_ setObject:GetFont() forKey:NSFontAttributeName]; } BubbleDecoration::~BubbleDecoration() { } CGFloat BubbleDecoration::GetWidthForImageAndLabel(NSImage* image, NSString* label) { if (!image && !label) return kOmittedWidth; const CGFloat image_width = image ? [image size].width : 0.0; if (!label) return BubblePadding() + image_width; // The bubble needs to take up an integral number of pixels. // Generally -sizeWithAttributes: seems to overestimate rather than // underestimate, so floor() seems to work better. const CGFloat label_width = std::floor([label sizeWithAttributes:attributes_].width); return BubblePadding() + image_width + kIconLabelPadding + label_width; } NSRect BubbleDecoration::GetImageRectInFrame(NSRect frame) { NSRect imageRect = NSInsetRect(frame, 0.0, kBackgroundYInset); if (image_) { // Center the image vertically. const NSSize imageSize = [image_ size]; imageRect.origin.y += std::floor((NSHeight(frame) - imageSize.height) / 2.0); imageRect.size = imageSize; } return imageRect; } CGFloat BubbleDecoration::GetWidthForSpace(CGFloat width) { const CGFloat all_width = GetWidthForImageAndLabel(image_, label_); if (all_width <= width) return all_width; const CGFloat image_width = GetWidthForImageAndLabel(image_, nil); if (image_width <= width) return image_width; return kOmittedWidth; } void BubbleDecoration::DrawInFrame(NSRect frame, NSView* control_view) { const NSRect decoration_frame = NSInsetRect(frame, 0.0, kBackgroundYInset); CGFloat textOffset = NSMinX(decoration_frame); if (image_) { // Center the image vertically. const NSSize imageSize = [image_ size]; NSRect imageRect = decoration_frame; imageRect.origin.y += std::floor((NSHeight(decoration_frame) - imageSize.height) / 2.0); imageRect.size = imageSize; [image_ drawInRect:imageRect fromRect:NSZeroRect // Entire image operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; textOffset = NSMaxX(imageRect) + kIconLabelPadding; } if (label_) { NSRect textRect = frame; textRect.origin.x = textOffset; textRect.size.width = NSMaxX(decoration_frame) - NSMinX(textRect); DrawLabel(label_, attributes_, textRect); } } void BubbleDecoration::DrawWithBackgroundInFrame(NSRect background_frame, NSRect frame, NSView* control_view) { NSRect rect = NSInsetRect(background_frame, 0, 1); rect.size.width -= kRightSideMargin; if (ui::MaterialDesignController::IsModeMaterial()) { CGFloat lineWidth = [control_view cr_lineWidth]; rect = NSInsetRect(rect, lineWidth / 2., lineWidth / 2.); NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:3 yRadius:3]; [path setLineWidth:lineWidth]; bool inDarkMode = [[control_view window] inIncognitoModeWithSystemTheme]; if (inDarkMode) { [[NSColor whiteColor] set]; [path fill]; } else { [GetBackgroundBorderColor() set]; [path stroke]; } } else { ui::DrawNinePartImage( rect, GetBubbleImageIds(), NSCompositeSourceOver, 1.0, true); } DrawInFrame(frame, control_view); } NSImage* BubbleDecoration::GetImage() { return image_; } void BubbleDecoration::SetImage(NSImage* image) { image_.reset([image retain]); } void BubbleDecoration::SetLabel(NSString* label) { // If the initializer was called with |nil|, then the code cannot // process a label. DCHECK(attributes_); if (attributes_) label_.reset([label copy]); } void BubbleDecoration::SetTextColor(NSColor* text_color) { [attributes_ setObject:text_color forKey:NSForegroundColorAttributeName]; }