| Index: chrome/browser/ui/cocoa/location_bar/secure_verbose_bubble_decoration.mm
|
| diff --git a/chrome/browser/ui/cocoa/location_bar/secure_verbose_bubble_decoration.mm b/chrome/browser/ui/cocoa/location_bar/secure_verbose_bubble_decoration.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f4e7b4657b608d1ff8a7c2609b3e96b12b2fced4
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/cocoa/location_bar/secure_verbose_bubble_decoration.mm
|
| @@ -0,0 +1,347 @@
|
| +// Copyright (c) 2011 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.
|
| +
|
| +#import "chrome/browser/ui/cocoa/location_bar/secure_verbose_bubble_decoration.h"
|
| +
|
| +#import "base/logging.h"
|
| +#import "base/mac/mac_util.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
|
| +#import "chrome/browser/ui/cocoa/location_bar/location_icon_decoration.h"
|
| +#import "chrome/browser/ui/cocoa/themed_window.h"
|
| +#include "grit/theme_resources.h"
|
| +#include "skia/ext/skia_utils_mac.h"
|
| +#import "ui/base/cocoa/nsview_additions.h"
|
| +#include "ui/base/material_design/material_design_controller.h"
|
| +#include "ui/gfx/animation/tween.h"
|
| +#include "ui/gfx/color_palette.h"
|
| +#include "ui/gfx/font_list.h"
|
| +#include "ui/gfx/geometry/cubic_bezier.h"
|
| +#include "ui/gfx/image/image_skia_util_mac.h"
|
| +#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
|
| +#include "ui/gfx/text_elider.h"
|
| +
|
| +namespace {
|
| +
|
| +// Duration of animation.
|
| +const NSTimeInterval kShowInterval = 0.33;
|
| +const NSTimeInterval kRetainInterval = 5;
|
| +const NSTimeInterval kHideInterval = 0.25;
|
| +const NSTimeInterval kAnimationDuration =
|
| + kShowInterval + kRetainInterval + kHideInterval;
|
| +
|
| +// Interval of the animation timer, 60Hz.
|
| +const NSTimeInterval kAnimationInterval = 1.0 / 60.0;
|
| +
|
| +// Transformation values at the beginning of the animation.
|
| +const CGFloat kStartScale = 0.25;
|
| +const CGFloat kStartXOffset = -15.0;
|
| +
|
| +// Padding between the icon and label.
|
| +CGFloat kIconLabelPadding = 4.0;
|
| +
|
| +// Inset for the background.
|
| +const CGFloat kBackgroundYInset = 4.0;
|
| +
|
| +// The offset of the text's baseline on a retina screen.
|
| +const CGFloat kRetinaBaselineOffset = 0.5;
|
| +
|
| +// Different states in which the animation can be.
|
| +// - kAnimatingIn: The text fades in and expands out.
|
| +// - kAnimationDelay: The text should be displayed in full size.
|
| +// - kAnimatingOut: The text fades out and shrinks.
|
| +// - kAnimationFinished: The text is hidden.
|
| +enum AnimationState {
|
| + kAnimatingIn,
|
| + kAnimationDelay,
|
| + kAnimatingOut,
|
| + kAnimationFinished,
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +@interface SecureVerboseAnimation : NSObject {
|
| + @private
|
| + SecureVerboseBubbleDecoration* owner_; // Weak, owns this.
|
| + double progress_; // Counter, [0..1], with aninmation progress.
|
| + double duration_;
|
| + NSTimer* timer_; // Animation timer. Owns this, owned by the run loop.
|
| + AnimationState state_;
|
| +}
|
| +
|
| +// Designated initializer. |owner| must not be nil. Animation timer will start
|
| +// as soon as the object is created.
|
| +- (id)initWithOwner:(SecureVerboseBubbleDecoration*)owner;
|
| +
|
| +// Call when |owner| is going away or the animation needs to be stopped.
|
| +// Ensures that any dangling references are cleared. Can be called multiple
|
| +// times.
|
| +- (void)stopAnimation;
|
| +
|
| +// Returns the current progress of the animation with the curve applied to it.
|
| +// A value in the range of [0, 1].
|
| +- (double)animationProgress;
|
| +
|
| +// Returns the animation interval based on the current state.
|
| +- (double)animationInterval;
|
| +
|
| +// Returns true if the animation is running.
|
| +- (BOOL)isRunning;
|
| +
|
| +// Returns the current state of the animation.
|
| +- (AnimationState)animationState;
|
| +
|
| +@end
|
| +
|
| +@implementation SecureVerboseAnimation
|
| +
|
| +- (id)initWithOwner:(SecureVerboseBubbleDecoration*)owner {
|
| + self = [super init];
|
| + if (self) {
|
| + owner_ = owner;
|
| + timer_ = [NSTimer scheduledTimerWithTimeInterval:kAnimationInterval
|
| + target:self
|
| + selector:@selector(timerFired:)
|
| + userInfo:nil
|
| + repeats:YES];
|
| + state_ = kAnimatingIn;
|
| + duration_ = 0;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)dealloc {
|
| + DCHECK(!timer_);
|
| + [super dealloc];
|
| +}
|
| +
|
| +// Clear weak references and stop the timer.
|
| +- (void)stopAnimation {
|
| + state_ = kAnimationFinished;
|
| + [timer_ invalidate];
|
| + timer_ = nil;
|
| +}
|
| +
|
| +- (double)animationInterval {
|
| + switch (state_) {
|
| + case kAnimatingIn:
|
| + return kShowInterval;
|
| + case kAnimatingOut:
|
| + return kHideInterval;
|
| + case kAnimationDelay:
|
| + return kRetainInterval;
|
| + case kAnimationFinished:
|
| + return 0;
|
| + }
|
| +}
|
| +
|
| +- (BOOL)isRunning {
|
| + return timer_ != nil;
|
| +}
|
| +
|
| +- (AnimationState)animationState {
|
| + return state_;
|
| +}
|
| +
|
| +- (void)timerFired:(NSTimer*)timer {
|
| + duration_ += kAnimationInterval;
|
| +
|
| + // Increment animation progress, normalized to [0..1].
|
| + progress_ += kAnimationInterval / [self animationInterval];
|
| + progress_ = std::min(progress_, 1.0);
|
| +
|
| + // Check if the animation state has changed. If it has changed, then
|
| + // reset the progress back to 0.
|
| + AnimationState currentState = state_;
|
| + if (duration_ <= kShowInterval)
|
| + currentState = kAnimatingIn;
|
| + else if (duration_ >= kAnimationDuration - kHideInterval)
|
| + currentState = kAnimatingOut;
|
| + else
|
| + currentState = kAnimationDelay;
|
| +
|
| + if (state_ != currentState) {
|
| + state_ = currentState;
|
| + progress_ = 0;
|
| + }
|
| +
|
| + // Stop timer if it has reached the end of its life.
|
| + if (duration_ >= kAnimationDuration)
|
| + [self stopAnimation];
|
| +
|
| + owner_->OnAnimationProgressed();
|
| +}
|
| +
|
| +- (double)animationProgress {
|
| + switch (state_) {
|
| + case kAnimatingIn:
|
| + return gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN,
|
| + progress_);
|
| + case kAnimatingOut:
|
| + return 1 - gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN_EXPO,
|
| + progress_);
|
| + case kAnimationDelay:
|
| + return 1.0;
|
| + case kAnimationFinished:
|
| + return 0;
|
| + }
|
| +}
|
| +
|
| +@end
|
| +
|
| +//////////////////////////////////////////////////////////////////
|
| +// SecureVerboseBubbleDecoration, public:
|
| +
|
| +SecureVerboseBubbleDecoration::SecureVerboseBubbleDecoration(
|
| + LocationIconDecoration* location_icon,
|
| + LocationBarViewMac* owner)
|
| + : EVBubbleDecoration(location_icon),
|
| + label_color_(gfx::kGoogleGreen700),
|
| + owner_(owner) {}
|
| +
|
| +SecureVerboseBubbleDecoration::~SecureVerboseBubbleDecoration() {
|
| + // Just in case the timer is still holding onto the animation object, force
|
| + // cleanup so it can't get back to |this|.
|
| + [animation_ stopAnimation];
|
| +}
|
| +
|
| +void SecureVerboseBubbleDecoration::SetLabelColor(SkColor color) {
|
| + label_color_ = color;
|
| +}
|
| +
|
| +void SecureVerboseBubbleDecoration::OnAnimationProgressed() {
|
| + owner_->Layout();
|
| +}
|
| +
|
| +void SecureVerboseBubbleDecoration::StartAnimation() {
|
| + // For testing only
|
| + if (animation_.get() && [animation_ isRunning])
|
| + return;
|
| +
|
| + animation_.reset([[SecureVerboseAnimation alloc] initWithOwner:this]);
|
| +}
|
| +
|
| +//////////////////////////////////////////////////////////////////
|
| +// SecureVerboseBubbleDecoration::LocationBarDecoration:
|
| +
|
| +CGFloat SecureVerboseBubbleDecoration::GetWidthForSpace(CGFloat width) {
|
| + CGFloat locationIconWidth = location_icon_->GetWidthForSpace(width);
|
| + CGFloat textWidth =
|
| + EVBubbleDecoration::GetWidthForSpace(width) - locationIconWidth;
|
| +
|
| + return (textWidth * GetAnimationProgress()) + locationIconWidth;
|
| +}
|
| +
|
| +void SecureVerboseBubbleDecoration::DrawInFrame(NSRect frame,
|
| + NSView* control_view) {
|
| + const NSRect decoration_frame = NSInsetRect(frame, 0.0, kBackgroundYInset);
|
| + CGFloat textOffset = NSMinX(decoration_frame);
|
| + if (image_) {
|
| + CGFloat imageAlpha =
|
| + animation_.get() && [animation_ animationState] == kAnimatingIn
|
| + ? GetAnimationProgress()
|
| + : 1.0;
|
| +
|
| + // 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:imageAlpha
|
| + respectFlipped:YES
|
| + hints:nil];
|
| + textOffset = NSMaxX(imageRect) + kIconLabelPadding;
|
| + }
|
| +
|
| + // Set the text color and draw the text.
|
| + if (label_) {
|
| + bool in_dark_mode = [[control_view window] inIncognitoModeWithSystemTheme];
|
| + NSColor* text_color =
|
| + in_dark_mode ? skia::SkColorToSRGBNSColor(kMaterialDarkModeTextColor)
|
| + : GetBackgroundBorderColor();
|
| + SetTextColor(text_color);
|
| +
|
| + // Transform the coordinate system to adjust the baseline on Retina.
|
| + // This is the only way to get fractional adjustments.
|
| + gfx::ScopedNSGraphicsContextSaveGState saveGraphicsState;
|
| + CGFloat lineWidth = [control_view cr_lineWidth];
|
| + if (lineWidth < 1) {
|
| + NSAffineTransform* transform = [NSAffineTransform transform];
|
| + [transform translateXBy:0 yBy:kRetinaBaselineOffset];
|
| + [transform concat];
|
| + }
|
| +
|
| + base::scoped_nsobject<NSAttributedString> str([[NSAttributedString alloc]
|
| + initWithString:label_
|
| + attributes:attributes_]);
|
| +
|
| + // Calculate the text frame based on the text height and offsets.
|
| + NSRect textRect = frame;
|
| + CGFloat textHeight = [str size].height;
|
| +
|
| + textRect.origin.x = textOffset;
|
| + textRect.origin.y = roundf(NSMidY(textRect) - textHeight / 2.0) - 1;
|
| + textRect.size.width = NSMaxX(decoration_frame) - NSMinX(textRect);
|
| + textRect.size.height = textHeight;
|
| +
|
| + NSAffineTransform* transform = [NSAffineTransform transform];
|
| + CGFloat progress = GetAnimationProgress();
|
| +
|
| + // Apply transformations so that the text animation:
|
| + // - Scales from 0.75 to 1.
|
| + // - Translates the X position to its origin after it got scaled, and
|
| + // before moving in a position from from -15 to 0
|
| + // - Translates the Y position so that the text is centered vertically.
|
| + double scale = gfx::Tween::DoubleValueBetween(progress, kStartScale, 1.0);
|
| +
|
| + double xOriginOffset = NSMinX(textRect) * (1 - scale);
|
| + double yOriginOffset = NSMinY(textRect) * (1 - scale);
|
| + double xOffset = gfx::Tween::DoubleValueBetween(progress, kStartXOffset, 0);
|
| + double yOffset = NSHeight(textRect) * (1 - scale) / 2.0;
|
| +
|
| + [transform translateXBy:xOffset + xOriginOffset
|
| + yBy:yOffset + yOriginOffset];
|
| + [transform scaleBy:scale];
|
| + [transform concat];
|
| +
|
| + // Draw the label.
|
| + [str drawInRect:textRect];
|
| +
|
| + // Draw the divider.
|
| + NSBezierPath* line = [NSBezierPath bezierPath];
|
| + [line setLineWidth:1];
|
| + [line moveToPoint:NSMakePoint(NSMaxX(decoration_frame) - DividerPadding(),
|
| + NSMinY(decoration_frame))];
|
| + [line lineToPoint:NSMakePoint(NSMaxX(decoration_frame) - DividerPadding(),
|
| + NSMaxY(decoration_frame))];
|
| +
|
| + NSColor* divider_color = GetDividerColor(in_dark_mode);
|
| + CGFloat divider_alpha =
|
| + [divider_color alphaComponent] * GetAnimationProgress();
|
| + divider_color = [divider_color colorWithAlphaComponent:divider_alpha];
|
| + [divider_color set];
|
| + [line stroke];
|
| + }
|
| +}
|
| +
|
| +//////////////////////////////////////////////////////////////////
|
| +// SecureVerboseBubbleDecoration::BubbleDecoration:
|
| +
|
| +NSColor* SecureVerboseBubbleDecoration::GetBackgroundBorderColor() {
|
| + return skia::SkColorToSRGBNSColor(
|
| + SkColorSetA(label_color_, 255.0 * GetAnimationProgress()));
|
| +}
|
| +
|
| +//////////////////////////////////////////////////////////////////
|
| +// SecureVerboseBubbleDecoration, private:
|
| +
|
| +CGFloat SecureVerboseBubbleDecoration::GetAnimationProgress() const {
|
| + return !animation_.get() || ![animation_ isRunning]
|
| + ? 0.0
|
| + : [animation_ animationProgress];
|
| +}
|
|
|