| Index: chrome/browser/ui/cocoa/autofill/autofill_loading_shield_controller.mm
|
| diff --git a/chrome/browser/ui/cocoa/autofill/autofill_loading_shield_controller.mm b/chrome/browser/ui/cocoa/autofill/autofill_loading_shield_controller.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..09e2f08cab53092c6455fc9389b535568de4dff4
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/cocoa/autofill/autofill_loading_shield_controller.mm
|
| @@ -0,0 +1,162 @@
|
| +// Copyright 2013 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/autofill/autofill_loading_shield_controller.h"
|
| +
|
| +#include <cmath>
|
| +
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
|
| +#include "chrome/browser/ui/autofill/loading_animation.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +#include "ui/gfx/animation/animation_delegate.h"
|
| +
|
| +namespace {
|
| +
|
| +// Horizontal spacing between the animated dots.
|
| +const CGFloat kDotsHorizontalPadding = 3;
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +// A C++ bridge class for driving the animation.
|
| +class AutofillLoadingAnimationBridge : public gfx::AnimationDelegate {
|
| + public:
|
| + AutofillLoadingAnimationBridge(AutofillLoadingShieldController* controller,
|
| + int font_size)
|
| + : animation_(this, font_size),
|
| + controller_(controller) {}
|
| + virtual ~AutofillLoadingAnimationBridge() {}
|
| +
|
| + // gfx::AnimationDelegate implementation.
|
| + virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
|
| + DCHECK_EQ(animation, &animation_);
|
| + [controller_ relayoutDotsForSteppedAnimation:animation_];
|
| + }
|
| +
|
| + autofill::LoadingAnimation* animation() { return &animation_; }
|
| +
|
| + private:
|
| + autofill::LoadingAnimation animation_;
|
| + AutofillLoadingShieldController* const controller_; // weak, owns |this|
|
| +};
|
| +
|
| +
|
| +@implementation AutofillLoadingShieldController
|
| +
|
| +- (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
|
| + if (self = [super initWithNibName:nil bundle:nil]) {
|
| + delegate_ = delegate;
|
| +
|
| + gfx::Font font = ui::ResourceBundle::GetSharedInstance().
|
| + GetFont(ui::ResourceBundle::BaseFont).DeriveFont(8);
|
| + NSFont* native_font = font.GetNativeFont();
|
| + animationDriver_.reset(
|
| + new AutofillLoadingAnimationBridge(self, font.GetHeight()));
|
| +
|
| + base::scoped_nsobject<NSBox> view([[NSBox alloc] initWithFrame:NSZeroRect]);
|
| + [view setBoxType:NSBoxCustom];
|
| + [view setBorderType:NSNoBorder];
|
| + [view setContentViewMargins:NSZeroSize];
|
| + [view setTitlePosition:NSNoTitle];
|
| +
|
| + message_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]);
|
| + [message_ setFont:native_font];
|
| + [message_ setEditable:NO];
|
| + [message_ setBordered:NO];
|
| + [message_ setDrawsBackground:NO];
|
| + [view addSubview:message_];
|
| +
|
| + dots_.reset([[NSArray alloc] initWithArray:@[
|
| + [[NSTextField alloc] initWithFrame:NSZeroRect],
|
| + [[NSTextField alloc] initWithFrame:NSZeroRect],
|
| + [[NSTextField alloc] initWithFrame:NSZeroRect] ]]);
|
| + NSInteger tag = 0;
|
| + for (NSTextField* dot in dots_.get()) {
|
| + [dot setFont:native_font];
|
| + [dot setEditable:NO];
|
| + [dot setBordered:NO];
|
| + [dot setDrawsBackground:NO];
|
| + [dot setStringValue:@"."];
|
| + [dot setTag:tag];
|
| + [dot sizeToFit];
|
| + [view addSubview:dot];
|
| +
|
| + ++tag;
|
| + }
|
| +
|
| + [self setView:view];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)update {
|
| + NSString* newMessage = @"";
|
| + if (delegate_->ShouldShowSpinner())
|
| + newMessage = base::SysUTF16ToNSString(delegate_->SpinnerText());
|
| +
|
| + if ([newMessage isEqualToString:[message_ stringValue]])
|
| + return;
|
| +
|
| + [message_ setStringValue:newMessage];
|
| + [message_ sizeToFit];
|
| +
|
| + if ([newMessage length] > 0) {
|
| + [[self view] setHidden:NO];
|
| + animationDriver_->animation()->Start();
|
| + } else {
|
| + [[self view] setHidden:YES];
|
| + animationDriver_->animation()->Reset();
|
| + }
|
| +
|
| + NSWindowController* delegate = [[[self view] window] windowController];
|
| + if ([delegate respondsToSelector:@selector(requestRelayout)])
|
| + [delegate performSelector:@selector(requestRelayout)];
|
| +}
|
| +
|
| +- (NSSize)preferredSize {
|
| + NOTREACHED(); // Only implemented as part of AutofillLayout protocol.
|
| + return NSZeroSize;
|
| +}
|
| +
|
| +- (void)performLayout {
|
| + if ([[self view] isHidden])
|
| + return;
|
| +
|
| + NSRect bounds = [[self view] bounds];
|
| + NSRect messageFrame = [message_ frame];
|
| +
|
| + NSSize size = messageFrame.size;
|
| + for (NSView* dot in dots_.get()) {
|
| + size.width += NSWidth([dot frame]) + kDotsHorizontalPadding;
|
| + size.height = std::max(size.height, NSHeight([dot frame]));
|
| + }
|
| +
|
| + // The message + dots should be centered in the view.
|
| + messageFrame.origin.x = std::ceil((NSWidth(bounds) - size.width) / 2.0);
|
| + messageFrame.origin.y = std::ceil((NSHeight(bounds) - size.height) / 2.0);
|
| + [message_ setFrameOrigin:messageFrame.origin];
|
| +
|
| + NSView* previousView = message_;
|
| + for (NSView* dot in dots_.get()) {
|
| + NSPoint dotFrameOrigin =
|
| + NSMakePoint(NSMaxX([previousView frame]) + kDotsHorizontalPadding,
|
| + messageFrame.origin.y);
|
| + [dot setFrameOrigin:dotFrameOrigin];
|
| +
|
| + previousView = dot;
|
| + }
|
| +}
|
| +
|
| +- (void)relayoutDotsForSteppedAnimation:
|
| + (const autofill::LoadingAnimation&)animation {
|
| + for (NSView* dot in dots_.get()) {
|
| + NSPoint origin = [dot frame].origin;
|
| + origin.y = [message_ frame].origin.y -
|
| + animation.GetCurrentValueForDot([dot tag]);
|
| + [dot setFrameOrigin:origin];
|
| + }
|
| +}
|
| +
|
| +@end
|
|
|