1 // Copyright (c) 2012 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 "ui/views/bubble/bubble_delegate.h" 6 7 #include "ui/base/accessibility/accessible_view_state.h" 8 #include "ui/gfx/animation/slide_animation.h" 9 #include "ui/gfx/color_utils.h" 10 #include "ui/gfx/rect.h" 11 #include "ui/native_theme/native_theme.h" 12 #include "ui/views/bubble/bubble_frame_view.h" 13 #include "ui/views/focus/view_storage.h" 14 #include "ui/views/widget/widget.h" 15 #include "ui/views/widget/widget_observer.h" 16 17 #if defined(OS_WIN) 18 #include "ui/base/win/shell.h" 19 #endif 20 21 // The duration of the fade animation in milliseconds. 22 static const int kHideFadeDurationMS = 200; 23 24 // The defaut margin between the content and the inside border, in pixels. 25 static const int kDefaultMargin = 6; 26 27 namespace views { 28 29 namespace { 30 31 // Create a widget to host the bubble. 32 Widget* CreateBubbleWidget(BubbleDelegateView* bubble) { 33 Widget* bubble_widget = new Widget(); 34 Widget::InitParams bubble_params(Widget::InitParams::TYPE_BUBBLE); 35 bubble_params.delegate = bubble; 36 bubble_params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; 37 bubble_params.accept_events = bubble->accept_events(); 38 if (bubble->parent_window()) 39 bubble_params.parent = bubble->parent_window(); 40 else if (bubble->anchor_widget()) 41 bubble_params.parent = bubble->anchor_widget()->GetNativeView(); 42 bubble_params.can_activate = bubble->CanActivate(); 43 bubble->OnBeforeBubbleWidgetInit(&bubble_params, bubble_widget); 44 bubble_widget->Init(bubble_params); 45 return bubble_widget; 46 } 47 48 } // namespace 49 50 BubbleDelegateView::BubbleDelegateView() 51 : close_on_esc_(true), 52 close_on_deactivate_(true), 53 anchor_view_storage_id_(ViewStorage::GetInstance()->CreateStorageID()), 54 anchor_widget_(NULL), 55 move_with_anchor_(false), 56 arrow_(BubbleBorder::TOP_LEFT), 57 shadow_(BubbleBorder::SMALL_SHADOW), 58 color_explicitly_set_(false), 59 margins_(kDefaultMargin, kDefaultMargin, kDefaultMargin, kDefaultMargin), 60 original_opacity_(255), 61 use_focusless_(false), 62 accept_events_(true), 63 border_accepts_events_(true), 64 adjust_if_offscreen_(true), 65 parent_window_(NULL) { 66 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); 67 UpdateColorsFromTheme(GetNativeTheme()); 68 } 69 70 BubbleDelegateView::BubbleDelegateView( 71 View* anchor_view, 72 BubbleBorder::Arrow arrow) 73 : close_on_esc_(true), 74 close_on_deactivate_(true), 75 anchor_view_storage_id_(ViewStorage::GetInstance()->CreateStorageID()), 76 anchor_widget_(NULL), 77 move_with_anchor_(false), 78 arrow_(arrow), 79 shadow_(BubbleBorder::SMALL_SHADOW), 80 color_explicitly_set_(false), 81 margins_(kDefaultMargin, kDefaultMargin, kDefaultMargin, kDefaultMargin), 82 original_opacity_(255), 83 use_focusless_(false), 84 accept_events_(true), 85 border_accepts_events_(true), 86 adjust_if_offscreen_(true), 87 parent_window_(NULL) { 88 SetAnchorView(anchor_view); 89 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); 90 UpdateColorsFromTheme(GetNativeTheme()); 91 } 92 93 BubbleDelegateView::~BubbleDelegateView() { 94 SetLayoutManager(NULL); 95 SetAnchorView(NULL); 96 } 97 98 // static 99 Widget* BubbleDelegateView::CreateBubble(BubbleDelegateView* bubble_delegate) { 100 bubble_delegate->Init(); 101 // Get the latest anchor widget from the anchor view at bubble creation time. 102 bubble_delegate->SetAnchorView(bubble_delegate->GetAnchorView()); 103 Widget* bubble_widget = CreateBubbleWidget(bubble_delegate); 104 105 #if defined(OS_WIN) && defined(USE_AURA) 106 // If glass is enabled, the bubble is allowed to extend outside the bounds of 107 // the parent frame and let DWM handle compositing. If not, then we don't 108 // want to allow the bubble to extend the frame because it will be clipped. 109 bubble_delegate->set_adjust_if_offscreen(ui::win::IsAeroGlassEnabled()); 110 #endif 111 112 bubble_delegate->SizeToContents(); 113 bubble_widget->AddObserver(bubble_delegate); 114 return bubble_widget; 115 } 116 117 BubbleDelegateView* BubbleDelegateView::AsBubbleDelegate() { 118 return this; 119 } 120 121 bool BubbleDelegateView::CanActivate() const { 122 return !use_focusless(); 123 } 124 125 bool BubbleDelegateView::ShouldShowCloseButton() const { 126 return false; 127 } 128 129 View* BubbleDelegateView::GetContentsView() { 130 return this; 131 } 132 133 NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView( 134 Widget* widget) { 135 BubbleFrameView* frame = new BubbleFrameView(margins()); 136 BubbleBorder::Arrow adjusted_arrow = arrow(); 137 if (base::i18n::IsRTL()) 138 adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow); 139 frame->SetBubbleBorder(new BubbleBorder(adjusted_arrow, shadow(), color())); 140 return frame; 141 } 142 143 void BubbleDelegateView::GetAccessibleState(ui::AccessibleViewState* state) { 144 state->role = ui::AccessibilityTypes::ROLE_DIALOG; 145 } 146 147 void BubbleDelegateView::OnWidgetDestroying(Widget* widget) { 148 if (anchor_widget() == widget) 149 SetAnchorView(NULL); 150 } 151 152 void BubbleDelegateView::OnWidgetVisibilityChanging(Widget* widget, 153 bool visible) { 154 #if defined(OS_WIN) 155 // On Windows we need to handle this before the bubble is visible or hidden. 156 // Please see the comment on the OnWidgetVisibilityChanging function. On 157 // other platforms it is fine to handle it after the bubble is shown/hidden. 158 HandleVisibilityChanged(widget, visible); 159 #endif 160 } 161 162 void BubbleDelegateView::OnWidgetVisibilityChanged(Widget* widget, 163 bool visible) { 164 #if !defined(OS_WIN) 165 HandleVisibilityChanged(widget, visible); 166 #endif 167 } 168 169 void BubbleDelegateView::OnWidgetActivationChanged(Widget* widget, 170 bool active) { 171 if (close_on_deactivate() && widget == GetWidget() && !active) 172 GetWidget()->Close(); 173 } 174 175 void BubbleDelegateView::OnWidgetBoundsChanged(Widget* widget, 176 const gfx::Rect& new_bounds) { 177 if (anchor_widget() == widget) { 178 if (move_with_anchor()) 179 SizeToContents(); 180 else 181 GetWidget()->Close(); 182 } 183 } 184 185 View* BubbleDelegateView::GetAnchorView() const { 186 return ViewStorage::GetInstance()->RetrieveView(anchor_view_storage_id_); 187 } 188 189 gfx::Rect BubbleDelegateView::GetAnchorRect() { 190 if (!GetAnchorView()) 191 return anchor_rect_; 192 193 anchor_rect_ = GetAnchorView()->GetBoundsInScreen(); 194 anchor_rect_.Inset(anchor_view_insets_); 195 return anchor_rect_; 196 } 197 198 void BubbleDelegateView::OnBeforeBubbleWidgetInit(Widget::InitParams* params, 199 Widget* widget) const { 200 } 201 202 void BubbleDelegateView::StartFade(bool fade_in) { 203 #if defined(USE_AURA) 204 // Use AURA's window layer animation instead of fading. This ensures that 205 // hosts which rely on the layer animation callbacks to close the window 206 // work correctly. 207 if (fade_in) 208 GetWidget()->Show(); 209 else 210 GetWidget()->Close(); 211 #else 212 fade_animation_.reset(new gfx::SlideAnimation(this)); 213 fade_animation_->SetSlideDuration(GetFadeDuration()); 214 fade_animation_->Reset(fade_in ? 0.0 : 1.0); 215 if (fade_in) { 216 original_opacity_ = 0; 217 GetWidget()->SetOpacity(original_opacity_); 218 GetWidget()->Show(); 219 fade_animation_->Show(); 220 } else { 221 original_opacity_ = 255; 222 fade_animation_->Hide(); 223 } 224 #endif 225 } 226 227 void BubbleDelegateView::ResetFade() { 228 fade_animation_.reset(); 229 GetWidget()->SetOpacity(original_opacity_); 230 } 231 232 void BubbleDelegateView::SetAlignment(BubbleBorder::BubbleAlignment alignment) { 233 GetBubbleFrameView()->bubble_border()->set_alignment(alignment); 234 SizeToContents(); 235 } 236 237 void BubbleDelegateView::SetArrowPaintType( 238 BubbleBorder::ArrowPaintType paint_type) { 239 GetBubbleFrameView()->bubble_border()->set_paint_arrow(paint_type); 240 SizeToContents(); 241 } 242 243 void BubbleDelegateView::OnAnchorBoundsChanged() { 244 SizeToContents(); 245 } 246 247 bool BubbleDelegateView::AcceleratorPressed( 248 const ui::Accelerator& accelerator) { 249 if (!close_on_esc() || accelerator.key_code() != ui::VKEY_ESCAPE) 250 return false; 251 if (fade_animation_.get()) 252 fade_animation_->Reset(); 253 GetWidget()->Close(); 254 return true; 255 } 256 257 void BubbleDelegateView::OnNativeThemeChanged(const ui::NativeTheme* theme) { 258 UpdateColorsFromTheme(theme); 259 } 260 261 void BubbleDelegateView::AnimationEnded(const gfx::Animation* animation) { 262 if (animation != fade_animation_.get()) 263 return; 264 bool closed = fade_animation_->GetCurrentValue() == 0; 265 fade_animation_->Reset(); 266 if (closed) 267 GetWidget()->Close(); 268 } 269 270 void BubbleDelegateView::AnimationProgressed(const gfx::Animation* animation) { 271 if (animation != fade_animation_.get()) 272 return; 273 DCHECK(fade_animation_->is_animating()); 274 GetWidget()->SetOpacity(fade_animation_->GetCurrentValue() * 255); 275 } 276 277 void BubbleDelegateView::Init() {} 278 279 void BubbleDelegateView::SetAnchorView(View* anchor_view) { 280 // When the anchor view gets set the associated anchor widget might 281 // change as well. 282 if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) { 283 if (anchor_widget()) { 284 anchor_widget_->RemoveObserver(this); 285 anchor_widget_ = NULL; 286 } 287 if (anchor_view) { 288 anchor_widget_ = anchor_view->GetWidget(); 289 if (anchor_widget_) 290 anchor_widget_->AddObserver(this); 291 } 292 } 293 294 // Remove the old storage item and set the new (if there is one). 295 ViewStorage* view_storage = ViewStorage::GetInstance(); 296 if (view_storage->RetrieveView(anchor_view_storage_id_)) 297 view_storage->RemoveView(anchor_view_storage_id_); 298 if (anchor_view) 299 view_storage->StoreView(anchor_view_storage_id_, anchor_view); 300 301 if (GetWidget()) 302 OnAnchorBoundsChanged(); 303 } 304 305 void BubbleDelegateView::SetAnchorRect(const gfx::Rect& rect) { 306 anchor_rect_ = rect; 307 if (GetWidget()) 308 OnAnchorBoundsChanged(); 309 } 310 311 void BubbleDelegateView::SizeToContents() { 312 GetWidget()->SetBounds(GetBubbleBounds()); 313 } 314 315 BubbleFrameView* BubbleDelegateView::GetBubbleFrameView() const { 316 const NonClientView* view = 317 GetWidget() ? GetWidget()->non_client_view() : NULL; 318 return view ? static_cast<BubbleFrameView*>(view->frame_view()) : NULL; 319 } 320 321 gfx::Rect BubbleDelegateView::GetBubbleBounds() { 322 // The argument rect has its origin at the bubble's arrow anchor point; 323 // its size is the preferred size of the bubble's client view (this view). 324 return GetBubbleFrameView()->GetUpdatedWindowBounds(GetAnchorRect(), 325 GetPreferredSize(), adjust_if_offscreen_); 326 } 327 328 int BubbleDelegateView::GetFadeDuration() { 329 return kHideFadeDurationMS; 330 } 331 332 void BubbleDelegateView::UpdateColorsFromTheme(const ui::NativeTheme* theme) { 333 if (!color_explicitly_set_) { 334 color_ = GetNativeTheme()->GetSystemColor( 335 ui::NativeTheme::kColorId_WindowBackground); 336 } 337 set_background(Background::CreateSolidBackground(color())); 338 BubbleFrameView* frame_view = GetBubbleFrameView(); 339 if (frame_view) 340 frame_view->bubble_border()->set_background_color(color()); 341 } 342 343 void BubbleDelegateView::HandleVisibilityChanged(Widget* widget, bool visible) { 344 if (widget == GetWidget() && visible && anchor_widget() && 345 anchor_widget()->GetTopLevelWidget()) { 346 anchor_widget()->GetTopLevelWidget()->DisableInactiveRendering(); 347 } 348 } 349 350 } // namespace views 351