OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 #include "views/bubble/bubble_delegate.h" | |
6 | |
7 #include "ui/base/animation/slide_animation.h" | |
8 #include "views/bubble/bubble_frame_view.h" | |
9 #include "views/widget/widget.h" | |
10 | |
11 // The duration of the fade animation in milliseconds. | |
12 static const int kHideFadeDurationMS = 200; | |
13 | |
14 namespace views { | |
15 | |
16 namespace { | |
17 | |
18 // Create a widget to host the bubble. | |
19 Widget* CreateBubbleWidget(BubbleDelegateView* bubble, Widget* parent) { | |
20 Widget* bubble_widget = new Widget(); | |
21 Widget::InitParams bubble_params(Widget::InitParams::TYPE_BUBBLE); | |
22 bubble_params.delegate = bubble; | |
23 bubble_params.transparent = true; | |
24 bubble_params.parent_widget = parent; | |
25 #if defined(OS_WIN) && !defined(USE_AURA) | |
26 bubble_params.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
27 bubble_params.transparent = false; | |
28 #endif | |
29 bubble_widget->Init(bubble_params); | |
30 return bubble_widget; | |
31 } | |
32 | |
33 #if defined(OS_WIN) && !defined(USE_AURA) | |
34 // The border widget's delegate, needed for transparent Windows native controls. | |
35 // TODO(msw): Remove this when Windows native controls are no longer needed. | |
36 class VIEWS_EXPORT BubbleBorderDelegateView : public WidgetDelegateView { | |
37 public: | |
38 explicit BubbleBorderDelegateView(BubbleDelegateView* bubble) | |
39 : bubble_(bubble) {} | |
40 virtual ~BubbleBorderDelegateView() {} | |
41 | |
42 // WidgetDelegateView overrides: | |
43 virtual bool CanActivate() const OVERRIDE; | |
44 virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; | |
45 | |
46 private: | |
47 BubbleDelegateView* bubble_; | |
48 | |
49 DISALLOW_COPY_AND_ASSIGN(BubbleBorderDelegateView); | |
50 }; | |
51 | |
52 bool BubbleBorderDelegateView::CanActivate() const { return false; } | |
53 | |
54 NonClientFrameView* BubbleBorderDelegateView::CreateNonClientFrameView() { | |
55 return bubble_->CreateNonClientFrameView(); | |
56 } | |
57 | |
58 // Create a widget to host the bubble's border. | |
59 Widget* CreateBorderWidget(BubbleDelegateView* bubble, Widget* parent) { | |
60 Widget* border_widget = new Widget(); | |
61 Widget::InitParams border_params(Widget::InitParams::TYPE_BUBBLE); | |
62 border_params.delegate = new BubbleBorderDelegateView(bubble); | |
63 border_params.transparent = true; | |
64 border_params.parent_widget = parent; | |
65 if (!border_params.parent_widget) | |
66 border_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
67 border_widget->Init(border_params); | |
68 return border_widget; | |
69 } | |
70 #endif | |
71 | |
72 } // namespace | |
73 | |
74 BubbleDelegateView::BubbleDelegateView() | |
75 : close_on_esc_(true), | |
76 close_on_deactivate_(true), | |
77 allow_bubble_offscreen_(false), | |
78 anchor_view_(NULL), | |
79 arrow_location_(BubbleBorder::TOP_LEFT), | |
80 color_(SK_ColorWHITE), | |
81 border_widget_(NULL), | |
82 use_focusless_(false) { | |
83 set_background(views::Background::CreateSolidBackground(color_)); | |
84 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, 0)); | |
85 } | |
86 | |
87 BubbleDelegateView::BubbleDelegateView( | |
88 View* anchor_view, | |
89 BubbleBorder::ArrowLocation arrow_location, | |
90 const SkColor& color) | |
91 : close_on_esc_(true), | |
92 close_on_deactivate_(true), | |
93 allow_bubble_offscreen_(false), | |
94 anchor_view_(anchor_view), | |
95 arrow_location_(arrow_location), | |
96 color_(color), | |
97 original_opacity_(255), | |
98 border_widget_(NULL), | |
99 use_focusless_(false) { | |
100 set_background(views::Background::CreateSolidBackground(color_)); | |
101 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, 0)); | |
102 } | |
103 | |
104 BubbleDelegateView::~BubbleDelegateView() { | |
105 if (border_widget_) | |
106 border_widget_->Close(); | |
107 } | |
108 | |
109 // static | |
110 Widget* BubbleDelegateView::CreateBubble(BubbleDelegateView* bubble_delegate) { | |
111 bubble_delegate->Init(); | |
112 Widget* parent = bubble_delegate->anchor_view() ? | |
113 bubble_delegate->anchor_view()->GetWidget() : NULL; | |
114 Widget* bubble_widget = CreateBubbleWidget(bubble_delegate, parent); | |
115 | |
116 #if defined(OS_WIN) && !defined(USE_AURA) | |
117 // First set the contents view to initialize view bounds for widget sizing. | |
118 bubble_widget->SetContentsView(bubble_delegate->GetContentsView()); | |
119 bubble_delegate->border_widget_ = CreateBorderWidget(bubble_delegate, parent); | |
120 #endif | |
121 | |
122 bubble_delegate->SizeToContents(); | |
123 bubble_widget->AddObserver(bubble_delegate); | |
124 if (parent && parent->GetTopLevelWidget()) | |
125 parent->GetTopLevelWidget()->DisableInactiveRendering(); | |
126 return bubble_widget; | |
127 } | |
128 | |
129 View* BubbleDelegateView::GetInitiallyFocusedView() { | |
130 return this; | |
131 } | |
132 | |
133 BubbleDelegateView* BubbleDelegateView::AsBubbleDelegate() { | |
134 return this; | |
135 } | |
136 | |
137 View* BubbleDelegateView::GetContentsView() { | |
138 return this; | |
139 } | |
140 | |
141 NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView() { | |
142 return new BubbleFrameView(GetArrowLocation(), | |
143 GetPreferredSize(), | |
144 GetColor(), | |
145 allow_bubble_offscreen_); | |
146 } | |
147 | |
148 void BubbleDelegateView::OnWidgetActivationChanged(Widget* widget, | |
149 bool active) { | |
150 if (close_on_deactivate() && widget == GetWidget() && !active) { | |
151 GetWidget()->RemoveObserver(this); | |
152 GetWidget()->Close(); | |
153 } | |
154 } | |
155 | |
156 gfx::Point BubbleDelegateView::GetAnchorPoint() { | |
157 if (!anchor_view()) | |
158 return gfx::Point(); | |
159 | |
160 BubbleBorder::ArrowLocation location = GetArrowLocation(); | |
161 gfx::Point anchor(anchor_view()->bounds().CenterPoint()); | |
162 // By default, pick the middle of |anchor_view_|'s edge opposite the arrow. | |
163 if (BubbleBorder::is_arrow_on_horizontal(location)) { | |
164 anchor.SetPoint(anchor_view()->width() / 2, | |
165 BubbleBorder::is_arrow_on_top(location) ? anchor_view()->height() : 0); | |
166 } else if (BubbleBorder::has_arrow(location)) { | |
167 anchor.SetPoint( | |
168 BubbleBorder::is_arrow_on_left(location) ? anchor_view()->width() : 0, | |
169 anchor_view_->height() / 2); | |
170 } | |
171 View::ConvertPointToScreen(anchor_view(), &anchor); | |
172 return anchor; | |
173 } | |
174 | |
175 BubbleBorder::ArrowLocation BubbleDelegateView::GetArrowLocation() const { | |
176 return arrow_location_; | |
177 } | |
178 | |
179 SkColor BubbleDelegateView::GetColor() const { | |
180 return color_; | |
181 } | |
182 | |
183 void BubbleDelegateView::Show() { | |
184 if (border_widget_) | |
185 border_widget_->Show(); | |
186 GetWidget()->Show(); | |
187 GetFocusManager()->SetFocusedView(GetInitiallyFocusedView()); | |
188 } | |
189 | |
190 void BubbleDelegateView::StartFade(bool fade_in) { | |
191 fade_animation_.reset(new ui::SlideAnimation(this)); | |
192 fade_animation_->SetSlideDuration(kHideFadeDurationMS); | |
193 fade_animation_->Reset(fade_in ? 0.0 : 1.0); | |
194 if (fade_in) { | |
195 original_opacity_ = 0; | |
196 if (border_widget_) | |
197 border_widget_->SetOpacity(original_opacity_); | |
198 GetWidget()->SetOpacity(original_opacity_); | |
199 Show(); | |
200 fade_animation_->Show(); | |
201 } else { | |
202 original_opacity_ = 255; | |
203 fade_animation_->Hide(); | |
204 } | |
205 } | |
206 | |
207 void BubbleDelegateView::ResetFade() { | |
208 fade_animation_.reset(); | |
209 if (border_widget_) | |
210 border_widget_->SetOpacity(original_opacity_); | |
211 GetWidget()->SetOpacity(original_opacity_); | |
212 } | |
213 | |
214 bool BubbleDelegateView::AcceleratorPressed( | |
215 const ui::Accelerator& accelerator) { | |
216 if (!close_on_esc() || accelerator.key_code() != ui::VKEY_ESCAPE) | |
217 return false; | |
218 if (fade_animation_.get()) | |
219 fade_animation_->Reset(); | |
220 GetWidget()->Close(); | |
221 return true; | |
222 } | |
223 | |
224 void BubbleDelegateView::AnimationEnded(const ui::Animation* animation) { | |
225 if (animation != fade_animation_.get()) | |
226 return; | |
227 bool closed = fade_animation_->GetCurrentValue() == 0; | |
228 fade_animation_->Reset(); | |
229 if (closed) | |
230 GetWidget()->Close(); | |
231 } | |
232 | |
233 void BubbleDelegateView::AnimationProgressed(const ui::Animation* animation) { | |
234 if (animation != fade_animation_.get()) | |
235 return; | |
236 DCHECK(fade_animation_->is_animating()); | |
237 unsigned char opacity = fade_animation_->GetCurrentValue() * 255; | |
238 #if defined(OS_WIN) && !defined(USE_AURA) | |
239 // Explicitly set the content Widget's layered style and set transparency via | |
240 // SetLayeredWindowAttributes. This is done because initializing the Widget as | |
241 // transparent and setting opacity via UpdateLayeredWindow doesn't support | |
242 // hosting child native Windows controls. | |
243 const HWND hwnd = GetWidget()->GetNativeView(); | |
244 const DWORD style = GetWindowLong(hwnd, GWL_EXSTYLE); | |
245 if ((opacity == 255) == !!(style & WS_EX_LAYERED)) | |
246 SetWindowLong(hwnd, GWL_EXSTYLE, style ^ WS_EX_LAYERED); | |
247 SetLayeredWindowAttributes(hwnd, 0, opacity, LWA_ALPHA); | |
248 // Update the border widget's opacity. | |
249 border_widget_->SetOpacity(opacity); | |
250 border_widget_->non_client_view()->SchedulePaint(); | |
251 #endif | |
252 GetWidget()->SetOpacity(opacity); | |
253 SchedulePaint(); | |
254 } | |
255 | |
256 void BubbleDelegateView::Init() {} | |
257 | |
258 void BubbleDelegateView::SizeToContents() { | |
259 #if defined(OS_WIN) && !defined(USE_AURA) | |
260 border_widget_->SetBounds(GetBubbleBounds()); | |
261 GetWidget()->SetBounds(GetBubbleClientBounds()); | |
262 #else | |
263 GetWidget()->SetBounds(GetBubbleBounds()); | |
264 #endif | |
265 } | |
266 | |
267 BubbleFrameView* BubbleDelegateView::GetBubbleFrameView() const { | |
268 const Widget* widget = border_widget_ ? border_widget_ : GetWidget(); | |
269 return static_cast<BubbleFrameView*>(widget->non_client_view()->frame_view()); | |
270 } | |
271 | |
272 gfx::Rect BubbleDelegateView::GetBubbleBounds() { | |
273 // The argument rect has its origin at the bubble's arrow anchor point; | |
274 // its size is the preferred size of the bubble's client view (this view). | |
275 return GetBubbleFrameView()->GetWindowBoundsForClientBounds( | |
276 gfx::Rect(GetAnchorPoint(), GetPreferredSize())); | |
277 } | |
278 | |
279 #if defined(OS_WIN) && !defined(USE_AURA) | |
280 gfx::Rect BubbleDelegateView::GetBubbleClientBounds() const { | |
281 gfx::Rect client_bounds(GetBubbleFrameView()->GetBoundsForClientView()); | |
282 client_bounds.Offset(border_widget_->GetWindowScreenBounds().origin()); | |
283 return client_bounds; | |
284 } | |
285 #endif | |
286 | |
287 } // namespace views | |
OLD | NEW |