| 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 "chrome/browser/ui/views/bubble/bubble.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "chrome/browser/ui/views/bubble/border_contents.h" | |
| 10 #include "chrome/common/chrome_notification_types.h" | |
| 11 #include "ui/base/animation/slide_animation.h" | |
| 12 #include "ui/base/keycodes/keyboard_codes.h" | |
| 13 #include "ui/gfx/color_utils.h" | |
| 14 #include "ui/views/layout/fill_layout.h" | |
| 15 #include "ui/views/widget/widget.h" | |
| 16 #include "ui/views/window/client_view.h" | |
| 17 | |
| 18 #if defined(OS_CHROMEOS) && defined(TOOLKIT_USES_GTK) | |
| 19 #include "chrome/browser/chromeos/legacy_window_manager/wm_ipc.h" | |
| 20 #include "third_party/cros_system_api/window_manager/chromeos_wm_ipc_enums.h" | |
| 21 #endif | |
| 22 | |
| 23 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 24 #include "chrome/browser/ui/views/bubble/border_widget_win.h" | |
| 25 #endif | |
| 26 | |
| 27 using std::vector; | |
| 28 | |
| 29 // How long the fade should last for. | |
| 30 static const int kHideFadeDurationMS = 200; | |
| 31 | |
| 32 // Background color of the bubble. | |
| 33 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 34 const SkColor Bubble::kBackgroundColor = | |
| 35 color_utils::GetSysSkColor(COLOR_WINDOW); | |
| 36 #else | |
| 37 // TODO(beng): source from theme provider. | |
| 38 const SkColor Bubble::kBackgroundColor = SK_ColorWHITE; | |
| 39 #endif | |
| 40 | |
| 41 // BubbleDelegate --------------------------------------------------------- | |
| 42 | |
| 43 string16 BubbleDelegate::GetAccessibleName() { | |
| 44 return string16(); | |
| 45 } | |
| 46 | |
| 47 // Bubble ----------------------------------------------------------------- | |
| 48 | |
| 49 // static | |
| 50 Bubble* Bubble::Show(views::Widget* parent, | |
| 51 const gfx::Rect& position_relative_to, | |
| 52 views::BubbleBorder::ArrowLocation arrow_location, | |
| 53 views::BubbleBorder::BubbleAlignment alignment, | |
| 54 views::View* contents, | |
| 55 BubbleDelegate* delegate) { | |
| 56 Bubble* bubble = new Bubble; | |
| 57 bubble->InitBubble(parent, position_relative_to, arrow_location, alignment, | |
| 58 contents, delegate); | |
| 59 | |
| 60 // Register the Escape accelerator for closing. | |
| 61 bubble->RegisterEscapeAccelerator(); | |
| 62 | |
| 63 if (delegate) | |
| 64 delegate->BubbleShown(); | |
| 65 | |
| 66 return bubble; | |
| 67 } | |
| 68 | |
| 69 #if defined(OS_CHROMEOS) | |
| 70 // static | |
| 71 Bubble* Bubble::ShowFocusless( | |
| 72 views::Widget* parent, | |
| 73 const gfx::Rect& position_relative_to, | |
| 74 views::BubbleBorder::ArrowLocation arrow_location, | |
| 75 views::BubbleBorder::BubbleAlignment alignment, | |
| 76 views::View* contents, | |
| 77 BubbleDelegate* delegate, | |
| 78 bool show_while_screen_is_locked) { | |
| 79 Bubble* bubble = new Bubble(views::Widget::InitParams::TYPE_POPUP, | |
| 80 show_while_screen_is_locked); | |
| 81 bubble->InitBubble(parent, position_relative_to, arrow_location, alignment, | |
| 82 contents, delegate); | |
| 83 return bubble; | |
| 84 } | |
| 85 #endif | |
| 86 | |
| 87 void Bubble::Close() { | |
| 88 if (show_status_ != kOpen) | |
| 89 return; | |
| 90 | |
| 91 show_status_ = kClosing; | |
| 92 | |
| 93 if (fade_away_on_close_) | |
| 94 FadeOut(); | |
| 95 else | |
| 96 DoClose(false); | |
| 97 } | |
| 98 | |
| 99 void Bubble::AnimationEnded(const ui::Animation* animation) { | |
| 100 if (static_cast<int>(animation_->GetCurrentValue()) == 0) { | |
| 101 // When fading out we just need to close the bubble at the end | |
| 102 DoClose(false); | |
| 103 } else { | |
| 104 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 105 // When fading in we need to remove the layered window style flag, since | |
| 106 // that style prevents some bubble content from working properly. | |
| 107 SetWindowLong(GWL_EXSTYLE, GetWindowLong(GWL_EXSTYLE) & ~WS_EX_LAYERED); | |
| 108 #endif | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 void Bubble::AnimationProgressed(const ui::Animation* animation) { | |
| 113 // Set the opacity for the main contents window. | |
| 114 unsigned char opacity = static_cast<unsigned char>( | |
| 115 animation_->GetCurrentValue() * 255); | |
| 116 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 117 SetLayeredWindowAttributes(GetNativeView(), 0, | |
| 118 static_cast<byte>(opacity), LWA_ALPHA); | |
| 119 contents_->SchedulePaint(); | |
| 120 | |
| 121 // Also fade in/out the bubble border window. | |
| 122 border_->SetOpacity(opacity); | |
| 123 border_->border_contents()->SchedulePaint(); | |
| 124 #else | |
| 125 SetOpacity(opacity); | |
| 126 border_contents_->SchedulePaint(); | |
| 127 #endif | |
| 128 } | |
| 129 | |
| 130 Bubble::Bubble() | |
| 131 : | |
| 132 #if defined(USE_AURA) | |
| 133 views::NativeWidgetAura(new views::Widget), | |
| 134 #elif defined(OS_WIN) | |
| 135 views::NativeWidgetWin(new views::Widget), | |
| 136 #elif defined(TOOLKIT_USES_GTK) | |
| 137 views::NativeWidgetGtk(new views::Widget), | |
| 138 #endif | |
| 139 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 140 border_(NULL), | |
| 141 #else | |
| 142 border_contents_(NULL), | |
| 143 #endif | |
| 144 delegate_(NULL), | |
| 145 show_status_(kOpen), | |
| 146 fade_away_on_close_(false), | |
| 147 close_on_deactivate_(true), | |
| 148 #if defined(TOOLKIT_USES_GTK) | |
| 149 type_(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS), | |
| 150 #endif | |
| 151 #if defined(OS_CHROMEOS) | |
| 152 show_while_screen_is_locked_(false), | |
| 153 #endif | |
| 154 arrow_location_(views::BubbleBorder::NONE), | |
| 155 contents_(NULL), | |
| 156 accelerator_registered_(false) { | |
| 157 } | |
| 158 | |
| 159 #if defined(OS_CHROMEOS) | |
| 160 Bubble::Bubble(views::Widget::InitParams::Type type, | |
| 161 bool show_while_screen_is_locked) | |
| 162 #if defined(USE_AURA) | |
| 163 : views::NativeWidgetAura(new views::Widget), | |
| 164 #else | |
| 165 : views::NativeWidgetGtk(new views::Widget), | |
| 166 #endif | |
| 167 border_contents_(NULL), | |
| 168 delegate_(NULL), | |
| 169 show_status_(kOpen), | |
| 170 fade_away_on_close_(false), | |
| 171 #if defined(TOOLKIT_USES_GTK) | |
| 172 type_(type), | |
| 173 #endif | |
| 174 show_while_screen_is_locked_(show_while_screen_is_locked), | |
| 175 arrow_location_(views::BubbleBorder::NONE), | |
| 176 contents_(NULL), | |
| 177 accelerator_registered_(false) { | |
| 178 } | |
| 179 #endif | |
| 180 | |
| 181 Bubble::~Bubble() { | |
| 182 } | |
| 183 | |
| 184 void Bubble::InitBubble(views::Widget* parent, | |
| 185 const gfx::Rect& position_relative_to, | |
| 186 views::BubbleBorder::ArrowLocation arrow_location, | |
| 187 views::BubbleBorder::BubbleAlignment alignment, | |
| 188 views::View* contents, | |
| 189 BubbleDelegate* delegate) { | |
| 190 delegate_ = delegate; | |
| 191 position_relative_to_ = position_relative_to; | |
| 192 arrow_location_ = arrow_location; | |
| 193 contents_ = contents; | |
| 194 const bool fade_in = delegate_ && delegate_->FadeInOnShow(); | |
| 195 | |
| 196 // Create the main window. | |
| 197 #if defined(USE_AURA) | |
| 198 views::Widget* parent_window = parent->GetTopLevelWidget(); | |
| 199 if (parent_window) | |
| 200 parent_window->DisableInactiveRendering(); | |
| 201 views::Widget::InitParams params; | |
| 202 params.transparent = true; | |
| 203 params.parent_widget = parent; | |
| 204 params.native_widget = this; | |
| 205 GetWidget()->Init(params); | |
| 206 if (fade_in) | |
| 207 SetOpacity(0); | |
| 208 #elif defined(OS_WIN) | |
| 209 views::Widget* parent_window = parent->GetTopLevelWidget(); | |
| 210 if (parent_window) | |
| 211 parent_window->DisableInactiveRendering(); | |
| 212 set_window_style(WS_POPUP | WS_CLIPCHILDREN); | |
| 213 int extended_style = WS_EX_TOOLWINDOW; | |
| 214 // During FadeIn we need to turn on the layered window style to deal with | |
| 215 // transparency. This flag needs to be reset after fading in is complete. | |
| 216 if (fade_in) | |
| 217 extended_style |= WS_EX_LAYERED; | |
| 218 set_window_ex_style(extended_style); | |
| 219 | |
| 220 DCHECK(!border_); | |
| 221 border_ = new BorderWidgetWin(); | |
| 222 | |
| 223 border_->InitBorderWidgetWin(CreateBorderContents(), parent->GetNativeView()); | |
| 224 border_->border_contents()->SetBackgroundColor(kBackgroundColor); | |
| 225 border_->border_contents()->SetAlignment(alignment); | |
| 226 | |
| 227 // We make the BorderWidgetWin the owner of the Bubble HWND, so that the | |
| 228 // latter is displayed on top of the former. | |
| 229 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); | |
| 230 params.parent = border_->GetNativeView(); | |
| 231 params.native_widget = this; | |
| 232 GetWidget()->Init(params); | |
| 233 | |
| 234 if (fade_in) { | |
| 235 border_->SetOpacity(0); | |
| 236 GetWidget()->SetOpacity(0); | |
| 237 } | |
| 238 SetWindowText(GetNativeView(), delegate_->GetAccessibleName().c_str()); | |
| 239 #elif defined(TOOLKIT_USES_GTK) | |
| 240 views::Widget::InitParams params(type_); | |
| 241 params.transparent = true; | |
| 242 params.parent_widget = parent; | |
| 243 params.native_widget = this; | |
| 244 GetWidget()->Init(params); | |
| 245 if (fade_in) | |
| 246 SetOpacity(0); | |
| 247 #if defined(OS_CHROMEOS) && defined(TOOLKIT_USES_GTK) | |
| 248 { | |
| 249 vector<int> params; | |
| 250 params.push_back(show_while_screen_is_locked_ ? 1 : 0); | |
| 251 chromeos::WmIpc::instance()->SetWindowType( | |
| 252 GetNativeView(), | |
| 253 chromeos::WM_IPC_WINDOW_CHROME_INFO_BUBBLE, | |
| 254 ¶ms); | |
| 255 } | |
| 256 #endif | |
| 257 #endif | |
| 258 | |
| 259 // Create a View to hold the contents of the main window. | |
| 260 views::View* contents_view = new views::View; | |
| 261 // We add |contents_view| to ourselves before the AddChildView() call below so | |
| 262 // that when |contents| gets added, it will already have a widget, and thus | |
| 263 // any NativeButtons it creates in ViewHierarchyChanged() will be functional | |
| 264 // (e.g. calling SetChecked() on checkboxes is safe). | |
| 265 GetWidget()->SetContentsView(contents_view); | |
| 266 // Adding |contents| as a child has to be done before we call | |
| 267 // contents->GetPreferredSize() below, since some supplied views don't | |
| 268 // actually initialize themselves until they're added to a hierarchy. | |
| 269 contents_view->AddChildView(contents); | |
| 270 | |
| 271 // Calculate and set the bounds for all windows and views. | |
| 272 gfx::Rect window_bounds; | |
| 273 | |
| 274 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 275 // Initialize and position the border window. | |
| 276 window_bounds = border_->SizeAndGetBounds(position_relative_to, | |
| 277 arrow_location, | |
| 278 contents->GetPreferredSize()); | |
| 279 | |
| 280 // Make |contents| take up the entire contents view. | |
| 281 contents_view->SetLayoutManager(new views::FillLayout); | |
| 282 | |
| 283 // Paint the background color behind the contents. | |
| 284 contents_view->set_background( | |
| 285 views::Background::CreateSolidBackground(kBackgroundColor)); | |
| 286 #else | |
| 287 // Create a view to paint the border and background. | |
| 288 border_contents_ = CreateBorderContents(); | |
| 289 border_contents_->Init(); | |
| 290 border_contents_->SetBackgroundColor(kBackgroundColor); | |
| 291 border_contents_->SetAlignment(alignment); | |
| 292 gfx::Rect contents_bounds; | |
| 293 border_contents_->SizeAndGetBounds(position_relative_to, | |
| 294 arrow_location, false, contents->GetPreferredSize(), | |
| 295 &contents_bounds, &window_bounds); | |
| 296 // This new view must be added before |contents| so it will paint under it. | |
| 297 contents_view->AddChildViewAt(border_contents_, 0); | |
| 298 | |
| 299 // |contents_view| has no layout manager, so we have to explicitly position | |
| 300 // its children. | |
| 301 border_contents_->SetBoundsRect( | |
| 302 gfx::Rect(gfx::Point(), window_bounds.size())); | |
| 303 contents->SetBoundsRect(contents_bounds); | |
| 304 #endif | |
| 305 GetWidget()->SetBounds(window_bounds); | |
| 306 | |
| 307 // Show the window. | |
| 308 #if defined(USE_AURA) | |
| 309 GetWidget()->Show(); | |
| 310 #elif defined(OS_WIN) | |
| 311 border_->ShowWindow(SW_SHOW); | |
| 312 ShowWindow(SW_SHOW); | |
| 313 #elif defined(TOOLKIT_USES_GTK) | |
| 314 GetWidget()->Show(); | |
| 315 #endif | |
| 316 | |
| 317 if (fade_in) | |
| 318 FadeIn(); | |
| 319 } | |
| 320 | |
| 321 void Bubble::RegisterEscapeAccelerator() { | |
| 322 GetWidget()->GetFocusManager()->RegisterAccelerator( | |
| 323 ui::Accelerator(ui::VKEY_ESCAPE, false, false, false), this); | |
| 324 accelerator_registered_ = true; | |
| 325 } | |
| 326 | |
| 327 void Bubble::UnregisterEscapeAccelerator() { | |
| 328 DCHECK(accelerator_registered_); | |
| 329 GetWidget()->GetFocusManager()->UnregisterAccelerator( | |
| 330 ui::Accelerator(ui::VKEY_ESCAPE, false, false, false), this); | |
| 331 accelerator_registered_ = false; | |
| 332 } | |
| 333 | |
| 334 BorderContents* Bubble::CreateBorderContents() { | |
| 335 return new BorderContents(); | |
| 336 } | |
| 337 | |
| 338 void Bubble::SizeToContents() { | |
| 339 gfx::Rect window_bounds; | |
| 340 | |
| 341 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 342 // Initialize and position the border window. | |
| 343 window_bounds = border_->SizeAndGetBounds(position_relative_to_, | |
| 344 arrow_location_, | |
| 345 contents_->GetPreferredSize()); | |
| 346 #else | |
| 347 gfx::Rect contents_bounds; | |
| 348 border_contents_->SizeAndGetBounds(position_relative_to_, | |
| 349 arrow_location_, false, contents_->GetPreferredSize(), | |
| 350 &contents_bounds, &window_bounds); | |
| 351 // |contents_view| has no layout manager, so we have to explicitly position | |
| 352 // its children. | |
| 353 border_contents_->SetBoundsRect( | |
| 354 gfx::Rect(gfx::Point(), window_bounds.size())); | |
| 355 contents_->SetBoundsRect(contents_bounds); | |
| 356 #endif | |
| 357 GetWidget()->SetBounds(window_bounds); | |
| 358 } | |
| 359 | |
| 360 #if defined(USE_AURA) | |
| 361 void Bubble::OnLostActive() { | |
| 362 GetWidget()->Close(); | |
| 363 } | |
| 364 #elif defined(OS_WIN) | |
| 365 void Bubble::OnActivate(UINT action, BOOL minimized, HWND window) { | |
| 366 // The popup should close when it is deactivated. | |
| 367 if (action == WA_INACTIVE) { | |
| 368 if (close_on_deactivate_) | |
| 369 GetWidget()->Close(); | |
| 370 } else if (action == WA_ACTIVE) { | |
| 371 DCHECK(GetWidget()->GetRootView()->has_children()); | |
| 372 GetWidget()->GetRootView()->child_at(0)->RequestFocus(); | |
| 373 } | |
| 374 } | |
| 375 #elif defined(TOOLKIT_USES_GTK) | |
| 376 void Bubble::OnActiveChanged() { | |
| 377 if (!GetWidget()->IsActive()) | |
| 378 GetWidget()->Close(); | |
| 379 } | |
| 380 #endif | |
| 381 | |
| 382 void Bubble::DoClose(bool closed_by_escape) { | |
| 383 if (show_status_ == kClosed) | |
| 384 return; | |
| 385 | |
| 386 if (accelerator_registered_) | |
| 387 UnregisterEscapeAccelerator(); | |
| 388 if (delegate_) | |
| 389 delegate_->BubbleClosing(this, closed_by_escape); | |
| 390 FOR_EACH_OBSERVER(Observer, observer_list_, OnBubbleClosing()); | |
| 391 show_status_ = kClosed; | |
| 392 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 393 border_->Close(); | |
| 394 #endif | |
| 395 #if defined(USE_AURA) | |
| 396 NativeWidgetAura::Close(); | |
| 397 #elif defined(OS_WIN) | |
| 398 NativeWidgetWin::Close(); | |
| 399 #elif defined(TOOLKIT_USES_GTK) | |
| 400 NativeWidgetGtk::Close(); | |
| 401 #endif | |
| 402 } | |
| 403 | |
| 404 void Bubble::FadeIn() { | |
| 405 Fade(true); // |fade_in|. | |
| 406 } | |
| 407 | |
| 408 void Bubble::FadeOut() { | |
| 409 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 410 // The contents window cannot have the layered flag on by default, since its | |
| 411 // content doesn't always work inside a layered window, but when animating it | |
| 412 // is ok to set that style on the window for the purpose of fading it out. | |
| 413 SetWindowLong(GWL_EXSTYLE, GetWindowLong(GWL_EXSTYLE) | WS_EX_LAYERED); | |
| 414 // This must be the very next call, otherwise we can get flicker on close. | |
| 415 SetLayeredWindowAttributes(GetNativeView(), 0, | |
| 416 static_cast<byte>(255), LWA_ALPHA); | |
| 417 #elif defined(USE_AURA) | |
| 418 NOTIMPLEMENTED(); | |
| 419 #endif | |
| 420 | |
| 421 Fade(false); // |fade_in|. | |
| 422 } | |
| 423 | |
| 424 void Bubble::Fade(bool fade_in) { | |
| 425 animation_.reset(new ui::SlideAnimation(this)); | |
| 426 animation_->SetSlideDuration(kHideFadeDurationMS); | |
| 427 animation_->SetTweenType(ui::Tween::LINEAR); | |
| 428 | |
| 429 animation_->Reset(fade_in ? 0.0 : 1.0); | |
| 430 if (fade_in) | |
| 431 animation_->Show(); | |
| 432 else | |
| 433 animation_->Hide(); | |
| 434 } | |
| 435 | |
| 436 bool Bubble::AcceleratorPressed(const ui::Accelerator& accelerator) { | |
| 437 if (!delegate_ || delegate_->CloseOnEscape()) { | |
| 438 DoClose(true); | |
| 439 return true; | |
| 440 } | |
| 441 return false; | |
| 442 } | |
| OLD | NEW |