Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / autofill / autofill_loading_shield_controller.mm
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
3 // found in the LICENSE file.
4
5 #import "chrome/browser/ui/cocoa/autofill/autofill_loading_shield_controller.h"
6
7 #include <cmath>
8
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
11 #include "chrome/browser/ui/autofill/loading_animation.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/gfx/animation/animation_delegate.h"
14 #include "ui/gfx/font_list.h"
15
16 namespace {
17
18 // Horizontal spacing between the animated dots.
19 // Using a negative spacing creates an ellipsis-like effect.
20 // TODO(isherman): Consider using the recipe below instead:
21 //   Create NSBezierPath
22 //   -[NSBezierPath appendBezierPathWithGlyph:inFont:]
23 //   -[NSBezierPath bounds]
24 const CGFloat kDotsHorizontalPadding = -6;
25
26 }  // namespace
27
28
29 // A C++ bridge class for driving the animation.
30 class AutofillLoadingAnimationBridge : public gfx::AnimationDelegate {
31  public:
32   AutofillLoadingAnimationBridge(AutofillLoadingShieldController* controller,
33                                  int font_size)
34       : animation_(this, font_size),
35         controller_(controller) {}
36   virtual ~AutofillLoadingAnimationBridge() {}
37
38   // gfx::AnimationDelegate implementation.
39   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
40     DCHECK_EQ(animation, &animation_);
41     [controller_ relayoutDotsForSteppedAnimation:animation_];
42   }
43
44   autofill::LoadingAnimation* animation() { return &animation_; }
45
46  private:
47   autofill::LoadingAnimation animation_;
48   AutofillLoadingShieldController* const controller_;  // weak, owns |this|
49 };
50
51
52 @implementation AutofillLoadingShieldController
53
54 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
55   if (self = [super initWithNibName:nil bundle:nil]) {
56     delegate_ = delegate;
57
58     const gfx::FontList& font_list =
59         ui::ResourceBundle::GetSharedInstance().GetFontList(
60             ui::ResourceBundle::LargeFont);
61     NSFont* native_font = font_list.GetPrimaryFont().GetNativeFont();
62     animationDriver_.reset(
63         new AutofillLoadingAnimationBridge(self, font_list.GetHeight()));
64
65     base::scoped_nsobject<NSBox> view([[NSBox alloc] initWithFrame:NSZeroRect]);
66     [view setBoxType:NSBoxCustom];
67     [view setBorderType:NSNoBorder];
68     [view setContentViewMargins:NSZeroSize];
69     [view setTitlePosition:NSNoTitle];
70
71     message_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]);
72     [message_ setFont:native_font];
73     [message_ setEditable:NO];
74     [message_ setBordered:NO];
75     [message_ setDrawsBackground:NO];
76     [view addSubview:message_];
77
78     dots_.reset([[NSArray alloc] initWithArray:@[
79          [[NSTextField alloc] initWithFrame:NSZeroRect],
80          [[NSTextField alloc] initWithFrame:NSZeroRect],
81          [[NSTextField alloc] initWithFrame:NSZeroRect] ]]);
82     NSInteger tag = 0;
83     for (NSTextField* dot in dots_.get()) {
84       [dot setFont:native_font];
85       [dot setEditable:NO];
86       [dot setBordered:NO];
87       [dot setDrawsBackground:NO];
88       [dot setStringValue:@"."];
89       [dot setTag:tag];
90       [dot sizeToFit];
91       [view addSubview:dot];
92
93       ++tag;
94     }
95
96     [self setView:view];
97   }
98   return self;
99 }
100
101 - (void)update {
102   NSString* newMessage = @"";
103   if (delegate_->ShouldShowSpinner())
104     newMessage = base::SysUTF16ToNSString(delegate_->SpinnerText());
105
106   if ([newMessage isEqualToString:[message_ stringValue]])
107     return;
108
109   [message_ setStringValue:newMessage];
110   [message_ sizeToFit];
111
112   if ([newMessage length] > 0) {
113     [[self view] setHidden:NO];
114     animationDriver_->animation()->Start();
115   } else {
116     [[self view] setHidden:YES];
117     animationDriver_->animation()->Reset();
118   }
119
120   NSWindowController* delegate = [[[self view] window] windowController];
121   if ([delegate respondsToSelector:@selector(requestRelayout)])
122     [delegate performSelector:@selector(requestRelayout)];
123 }
124
125 - (NSSize)preferredSize {
126   NOTREACHED();  // Only implemented as part of AutofillLayout protocol.
127   return NSZeroSize;
128 }
129
130 - (void)performLayout {
131   if ([[self view] isHidden])
132     return;
133
134   NSRect bounds = [[self view] bounds];
135   NSRect messageFrame = [message_ frame];
136
137   NSSize size = messageFrame.size;
138   for (NSView* dot in dots_.get()) {
139     size.width += NSWidth([dot frame]) + kDotsHorizontalPadding;
140     size.height = std::max(size.height, NSHeight([dot frame]));
141   }
142
143   // The message + dots should be centered in the view.
144   messageFrame.origin.x = std::ceil((NSWidth(bounds) - size.width) / 2.0);
145   messageFrame.origin.y = std::ceil((NSHeight(bounds) - size.height) / 2.0);
146   [message_ setFrameOrigin:messageFrame.origin];
147
148   NSView* previousView = message_;
149   for (NSView* dot in dots_.get()) {
150     NSPoint dotFrameOrigin =
151         NSMakePoint(NSMaxX([previousView frame]) + kDotsHorizontalPadding,
152                     messageFrame.origin.y);
153     [dot setFrameOrigin:dotFrameOrigin];
154
155     previousView = dot;
156   }
157 }
158
159 - (void)relayoutDotsForSteppedAnimation:
160     (const autofill::LoadingAnimation&)animation {
161   for (NSView* dot in dots_.get()) {
162     NSPoint origin = [dot frame].origin;
163     origin.y = [message_ frame].origin.y -
164         animation.GetCurrentValueForDot([dot tag]);
165     [dot setFrameOrigin:origin];
166   }
167 }
168
169 @end