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 "ash/desktop_background/desktop_background_view.h" 6 7 #include <limits> 8 9 #include "ash/ash_export.h" 10 #include "ash/desktop_background/desktop_background_controller.h" 11 #include "ash/desktop_background/desktop_background_widget_controller.h" 12 #include "ash/desktop_background/user_wallpaper_delegate.h" 13 #include "ash/display/display_manager.h" 14 #include "ash/root_window_controller.h" 15 #include "ash/session/session_state_delegate.h" 16 #include "ash/shell.h" 17 #include "ash/shell_window_ids.h" 18 #include "ash/wm/overview/window_selector_controller.h" 19 #include "ash/wm/window_animations.h" 20 #include "base/message_loop/message_loop.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "ui/aura/window_event_dispatcher.h" 23 #include "ui/compositor/layer.h" 24 #include "ui/gfx/canvas.h" 25 #include "ui/gfx/image/image.h" 26 #include "ui/gfx/size_conversions.h" 27 #include "ui/gfx/transform.h" 28 #include "ui/views/widget/widget.h" 29 30 namespace ash { 31 namespace { 32 33 // For our scaling ratios we need to round positive numbers. 34 int RoundPositive(double x) { 35 return static_cast<int>(floor(x + 0.5)); 36 } 37 38 // A view that controls the child view's layer so that the layer 39 // always has the same size as the display's original, un-scaled size 40 // in DIP. The layer then transformed to fit to the virtual screen 41 // size when laid-out. 42 // This is to avoid scaling the image at painting time, then scaling 43 // it back to the screen size in the compositor. 44 class LayerControlView : public views::View { 45 public: 46 explicit LayerControlView(views::View* view) { 47 AddChildView(view); 48 view->SetPaintToLayer(true); 49 } 50 51 // Overrides views::View. 52 virtual void Layout() OVERRIDE { 53 gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow( 54 GetWidget()->GetNativeView()); 55 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); 56 DisplayInfo info = display_manager->GetDisplayInfo(display.id()); 57 float ui_scale = info.GetEffectiveUIScale(); 58 gfx::SizeF pixel_size = display.size(); 59 pixel_size.Scale(1.0f / ui_scale); 60 gfx::Size rounded_size = gfx::ToCeiledSize(pixel_size); 61 DCHECK_EQ(1, child_count()); 62 views::View* child = child_at(0); 63 child->SetBounds(0, 0, rounded_size.width(), rounded_size.height()); 64 gfx::Transform transform; 65 transform.Scale(ui_scale, ui_scale); 66 child->SetTransform(transform); 67 } 68 69 private: 70 DISALLOW_COPY_AND_ASSIGN(LayerControlView); 71 }; 72 73 } // namespace 74 75 // This event handler receives events in the pre-target phase and takes care of 76 // the following: 77 // - Disabling overview mode on touch release. 78 // - Disabling overview mode on mouse release. 79 class PreEventDispatchHandler : public ui::EventHandler { 80 public: 81 PreEventDispatchHandler() {} 82 virtual ~PreEventDispatchHandler() {} 83 84 private: 85 // ui::EventHandler: 86 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 87 CHECK_EQ(ui::EP_PRETARGET, event->phase()); 88 WindowSelectorController* controller = 89 Shell::GetInstance()->window_selector_controller(); 90 if (event->type() == ui::ET_MOUSE_RELEASED && controller->IsSelecting()) { 91 controller->ToggleOverview(); 92 event->StopPropagation(); 93 } 94 } 95 96 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 97 CHECK_EQ(ui::EP_PRETARGET, event->phase()); 98 WindowSelectorController* controller = 99 Shell::GetInstance()->window_selector_controller(); 100 if (event->type() == ui::ET_GESTURE_TAP && controller->IsSelecting()) { 101 controller->ToggleOverview(); 102 event->StopPropagation(); 103 } 104 } 105 106 DISALLOW_COPY_AND_ASSIGN(PreEventDispatchHandler); 107 }; 108 109 //////////////////////////////////////////////////////////////////////////////// 110 // DesktopBackgroundView, public: 111 112 DesktopBackgroundView::DesktopBackgroundView() 113 : pre_dispatch_handler_(new PreEventDispatchHandler()) { 114 set_context_menu_controller(this); 115 AddPreTargetHandler(pre_dispatch_handler_.get()); 116 } 117 118 DesktopBackgroundView::~DesktopBackgroundView() { 119 RemovePreTargetHandler(pre_dispatch_handler_.get()); 120 } 121 122 //////////////////////////////////////////////////////////////////////////////// 123 // DesktopBackgroundView, views::View overrides: 124 125 void DesktopBackgroundView::OnPaint(gfx::Canvas* canvas) { 126 // Scale the image while maintaining the aspect ratio, cropping as 127 // necessary to fill the background. Ideally the image should be larger 128 // than the largest display supported, if not we will scale and center it if 129 // the layout is WALLPAPER_LAYOUT_CENTER_CROPPED. 130 DesktopBackgroundController* controller = 131 Shell::GetInstance()->desktop_background_controller(); 132 gfx::ImageSkia wallpaper = controller->GetWallpaper(); 133 WallpaperLayout wallpaper_layout = controller->GetWallpaperLayout(); 134 135 if (wallpaper.isNull()) { 136 canvas->FillRect(GetLocalBounds(), SK_ColorBLACK); 137 return; 138 } 139 140 gfx::NativeView native_view = GetWidget()->GetNativeView(); 141 gfx::Display display = gfx::Screen::GetScreenFor(native_view)-> 142 GetDisplayNearestWindow(native_view); 143 144 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); 145 DisplayInfo display_info = display_manager->GetDisplayInfo(display.id()); 146 float scaling = display_info.GetEffectiveUIScale(); 147 if (scaling <= 1.0f) 148 scaling = 1.0f; 149 // Allow scaling up to the UI scaling. 150 // TODO(oshima): Create separate layer that fits to the image and then 151 // scale to avoid artifacts and be more efficient when clipped. 152 gfx::Rect wallpaper_rect( 153 0, 0, wallpaper.width() * scaling, wallpaper.height() * scaling); 154 155 if (wallpaper_layout == WALLPAPER_LAYOUT_CENTER_CROPPED) { 156 // The dimension with the smallest ratio must be cropped, the other one 157 // is preserved. Both are set in gfx::Size cropped_size. 158 double horizontal_ratio = static_cast<double>(width()) / 159 static_cast<double>(wallpaper.width()); 160 double vertical_ratio = static_cast<double>(height()) / 161 static_cast<double>(wallpaper.height()); 162 163 gfx::Size cropped_size; 164 if (vertical_ratio > horizontal_ratio) { 165 cropped_size = gfx::Size( 166 RoundPositive(static_cast<double>(width()) / vertical_ratio), 167 wallpaper.height()); 168 } else { 169 cropped_size = gfx::Size(wallpaper.width(), 170 RoundPositive(static_cast<double>(height()) / horizontal_ratio)); 171 } 172 173 gfx::Rect wallpaper_cropped_rect( 174 0, 0, wallpaper.width(), wallpaper.height()); 175 wallpaper_cropped_rect.ClampToCenteredSize(cropped_size); 176 canvas->DrawImageInt(wallpaper, 177 wallpaper_cropped_rect.x(), wallpaper_cropped_rect.y(), 178 wallpaper_cropped_rect.width(), wallpaper_cropped_rect.height(), 179 0, 0, width(), height(), 180 true); 181 } else if (wallpaper_layout == WALLPAPER_LAYOUT_TILE) { 182 canvas->TileImageInt(wallpaper, 0, 0, width(), height()); 183 } else if (wallpaper_layout == WALLPAPER_LAYOUT_STRETCH) { 184 // This is generally not recommended as it may show artifacts. 185 canvas->DrawImageInt(wallpaper, 0, 0, wallpaper.width(), 186 wallpaper.height(), 0, 0, width(), height(), true); 187 } else { 188 // Fill with black to make sure that the entire area is opaque. 189 canvas->FillRect(GetLocalBounds(), SK_ColorBLACK); 190 // All other are simply centered, and not scaled (but may be clipped). 191 canvas->DrawImageInt( 192 wallpaper, 193 0, 0, wallpaper.width(), wallpaper.height(), 194 (width() - wallpaper_rect.width()) / 2, 195 (height() - wallpaper_rect.height()) / 2, 196 wallpaper_rect.width(), 197 wallpaper_rect.height(), 198 true); 199 } 200 } 201 202 bool DesktopBackgroundView::OnMousePressed(const ui::MouseEvent& event) { 203 return true; 204 } 205 206 void DesktopBackgroundView::ShowContextMenuForView( 207 views::View* source, 208 const gfx::Point& point, 209 ui::MenuSourceType source_type) { 210 Shell::GetInstance()->ShowContextMenu(point, source_type); 211 } 212 213 views::Widget* CreateDesktopBackground(aura::Window* root_window, 214 int container_id) { 215 DesktopBackgroundController* controller = 216 Shell::GetInstance()->desktop_background_controller(); 217 UserWallpaperDelegate* wallpaper_delegate = 218 Shell::GetInstance()->user_wallpaper_delegate(); 219 220 views::Widget* desktop_widget = new views::Widget; 221 views::Widget::InitParams params( 222 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); 223 if (controller->GetWallpaper().isNull()) 224 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 225 params.parent = root_window->GetChildById(container_id); 226 desktop_widget->Init(params); 227 desktop_widget->GetNativeWindow()->layer()->SetMasksToBounds(true); 228 desktop_widget->SetContentsView( 229 new LayerControlView(new DesktopBackgroundView())); 230 int animation_type = wallpaper_delegate->GetAnimationType(); 231 wm::SetWindowVisibilityAnimationType( 232 desktop_widget->GetNativeView(), animation_type); 233 234 RootWindowController* root_window_controller = 235 GetRootWindowController(root_window); 236 237 // Enable wallpaper transition for the following cases: 238 // 1. Initial(OOBE) wallpaper animation. 239 // 2. Wallpaper fades in from a non empty background. 240 // 3. From an empty background, chrome transit to a logged in user session. 241 // 4. From an empty background, guest user logged in. 242 if (wallpaper_delegate->ShouldShowInitialAnimation() || 243 root_window_controller->animating_wallpaper_controller() || 244 Shell::GetInstance()->session_state_delegate()->NumberOfLoggedInUsers()) { 245 wm::SetWindowVisibilityAnimationTransition( 246 desktop_widget->GetNativeView(), wm::ANIMATE_SHOW); 247 int duration_override = wallpaper_delegate->GetAnimationDurationOverride(); 248 if (duration_override) { 249 wm::SetWindowVisibilityAnimationDuration( 250 desktop_widget->GetNativeView(), 251 base::TimeDelta::FromMilliseconds(duration_override)); 252 } 253 } else { 254 // Disable animation if transition to login screen from an empty background. 255 wm::SetWindowVisibilityAnimationTransition( 256 desktop_widget->GetNativeView(), wm::ANIMATE_NONE); 257 } 258 259 desktop_widget->SetBounds(params.parent->bounds()); 260 return desktop_widget; 261 } 262 263 } // namespace ash 264