| OLD | NEW |
| (Empty) |
| 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 #include "ash/wm/overview/scoped_transform_overview_window.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "ash/common/material_design/material_design_controller.h" | |
| 11 #include "ash/common/wm/window_state.h" | |
| 12 #include "ash/common/wm_window.h" | |
| 13 #include "ash/common/wm_window_property.h" | |
| 14 #include "ash/wm/overview/scoped_overview_animation_settings.h" | |
| 15 #include "ash/wm/overview/scoped_overview_animation_settings_factory.h" | |
| 16 #include "ash/wm/overview/window_selector_item.h" | |
| 17 #include "base/macros.h" | |
| 18 #include "third_party/skia/include/core/SkPaint.h" | |
| 19 #include "third_party/skia/include/core/SkPath.h" | |
| 20 #include "third_party/skia/include/core/SkRect.h" | |
| 21 #include "ui/compositor/layer.h" | |
| 22 #include "ui/compositor/layer_delegate.h" | |
| 23 #include "ui/compositor/paint_recorder.h" | |
| 24 #include "ui/gfx/geometry/rect.h" | |
| 25 #include "ui/gfx/geometry/safe_integer_conversions.h" | |
| 26 #include "ui/gfx/transform_util.h" | |
| 27 | |
| 28 using WmWindows = std::vector<ash::WmWindow*>; | |
| 29 | |
| 30 namespace ash { | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 // The opacity level that windows will be set to when they are restored. | |
| 35 const float kRestoreWindowOpacity = 1.0f; | |
| 36 | |
| 37 // Alpha value used to paint mask layer that masks the original window header. | |
| 38 const int kOverviewContentMaskAlpha = 255; | |
| 39 | |
| 40 WmWindow* GetTransientRoot(WmWindow* window) { | |
| 41 while (window->GetTransientParent()) | |
| 42 window = window->GetTransientParent(); | |
| 43 return window; | |
| 44 } | |
| 45 | |
| 46 std::unique_ptr<ScopedOverviewAnimationSettings> | |
| 47 CreateScopedOverviewAnimationSettings(OverviewAnimationType animation_type, | |
| 48 WmWindow* window) { | |
| 49 return ScopedOverviewAnimationSettingsFactory::Get() | |
| 50 ->CreateOverviewAnimationSettings(animation_type, window); | |
| 51 } | |
| 52 | |
| 53 // An iterator class that traverses a WmWindow and all of its transient | |
| 54 // descendants. | |
| 55 class TransientDescendantIterator { | |
| 56 public: | |
| 57 // Creates an empty iterator. | |
| 58 TransientDescendantIterator(); | |
| 59 | |
| 60 // Copy constructor required for iterator purposes. | |
| 61 TransientDescendantIterator( | |
| 62 const TransientDescendantIterator& other) = default; | |
| 63 | |
| 64 // Iterates over |root_window| and all of its transient descendants. | |
| 65 // Note |root_window| must not have a transient parent. | |
| 66 explicit TransientDescendantIterator(WmWindow* root_window); | |
| 67 | |
| 68 // Prefix increment operator. This assumes there are more items (i.e. | |
| 69 // *this != TransientDescendantIterator()). | |
| 70 const TransientDescendantIterator& operator++(); | |
| 71 | |
| 72 // Comparison for STL-based loops. | |
| 73 bool operator!=(const TransientDescendantIterator& other) const; | |
| 74 | |
| 75 // Dereference operator for STL-compatible iterators. | |
| 76 WmWindow* operator*() const; | |
| 77 | |
| 78 private: | |
| 79 // Explicit assignment operator defined because an explicit copy constructor | |
| 80 // is needed and therefore the DISALLOW_COPY_AND_ASSIGN macro cannot be used. | |
| 81 TransientDescendantIterator& operator=( | |
| 82 const TransientDescendantIterator& other) = default; | |
| 83 | |
| 84 // The current window that |this| refers to. A null |current_window_| denotes | |
| 85 // an empty iterator and is used as the last possible value in the traversal. | |
| 86 WmWindow* current_window_; | |
| 87 }; | |
| 88 | |
| 89 // Provides a virtual container implementing begin() and end() for a sequence of | |
| 90 // TransientDescendantIterators. This can be used in range-based for loops. | |
| 91 class TransientDescendantIteratorRange { | |
| 92 public: | |
| 93 explicit TransientDescendantIteratorRange( | |
| 94 const TransientDescendantIterator& begin); | |
| 95 | |
| 96 // Copy constructor required for iterator purposes. | |
| 97 TransientDescendantIteratorRange( | |
| 98 const TransientDescendantIteratorRange& other) = default; | |
| 99 | |
| 100 const TransientDescendantIterator& begin() const { return begin_; } | |
| 101 const TransientDescendantIterator& end() const { return end_; } | |
| 102 | |
| 103 private: | |
| 104 // Explicit assignment operator defined because an explicit copy constructor | |
| 105 // is needed and therefore the DISALLOW_COPY_AND_ASSIGN macro cannot be used. | |
| 106 TransientDescendantIteratorRange& operator=( | |
| 107 const TransientDescendantIteratorRange& other) = default; | |
| 108 | |
| 109 TransientDescendantIterator begin_; | |
| 110 TransientDescendantIterator end_; | |
| 111 }; | |
| 112 | |
| 113 TransientDescendantIterator::TransientDescendantIterator() | |
| 114 : current_window_(nullptr) { | |
| 115 } | |
| 116 | |
| 117 TransientDescendantIterator::TransientDescendantIterator(WmWindow* root_window) | |
| 118 : current_window_(root_window) { | |
| 119 DCHECK(!root_window->GetTransientParent()); | |
| 120 } | |
| 121 | |
| 122 // Performs a pre-order traversal of the transient descendants. | |
| 123 const TransientDescendantIterator& | |
| 124 TransientDescendantIterator::operator++() { | |
| 125 DCHECK(current_window_); | |
| 126 | |
| 127 const WmWindows transient_children = current_window_->GetTransientChildren(); | |
| 128 | |
| 129 if (!transient_children.empty()) { | |
| 130 current_window_ = transient_children.front(); | |
| 131 } else { | |
| 132 while (current_window_) { | |
| 133 WmWindow* parent = current_window_->GetTransientParent(); | |
| 134 if (!parent) { | |
| 135 current_window_ = nullptr; | |
| 136 break; | |
| 137 } | |
| 138 const WmWindows transient_siblings = parent->GetTransientChildren(); | |
| 139 auto iter = std::find(transient_siblings.begin(), | |
| 140 transient_siblings.end(), current_window_); | |
| 141 ++iter; | |
| 142 if (iter != transient_siblings.end()) { | |
| 143 current_window_ = *iter; | |
| 144 break; | |
| 145 } | |
| 146 current_window_ = current_window_->GetTransientParent(); | |
| 147 } | |
| 148 } | |
| 149 return *this; | |
| 150 } | |
| 151 | |
| 152 bool TransientDescendantIterator::operator!=( | |
| 153 const TransientDescendantIterator& other) const { | |
| 154 return current_window_ != other.current_window_; | |
| 155 } | |
| 156 | |
| 157 WmWindow* TransientDescendantIterator::operator*() const { | |
| 158 return current_window_; | |
| 159 } | |
| 160 | |
| 161 TransientDescendantIteratorRange::TransientDescendantIteratorRange( | |
| 162 const TransientDescendantIterator& begin) | |
| 163 : begin_(begin) { | |
| 164 } | |
| 165 | |
| 166 TransientDescendantIteratorRange GetTransientTreeIterator(WmWindow* window) { | |
| 167 return TransientDescendantIteratorRange( | |
| 168 TransientDescendantIterator(GetTransientRoot(window))); | |
| 169 } | |
| 170 | |
| 171 } // namespace | |
| 172 | |
| 173 // Mask layer that clips the window's original header in overview mode. | |
| 174 // Only used with Material Design. | |
| 175 class ScopedTransformOverviewWindow::OverviewContentMask | |
| 176 : public ui::LayerDelegate { | |
| 177 public: | |
| 178 OverviewContentMask(int inset, int radius); | |
| 179 ~OverviewContentMask() override; | |
| 180 | |
| 181 ui::Layer* layer() { return &layer_; } | |
| 182 | |
| 183 // Overridden from LayerDelegate. | |
| 184 void OnPaintLayer(const ui::PaintContext& context) override; | |
| 185 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {} | |
| 186 void OnDeviceScaleFactorChanged(float device_scale_factor) override; | |
| 187 base::Closure PrepareForLayerBoundsChange() override; | |
| 188 | |
| 189 private: | |
| 190 ui::Layer layer_; | |
| 191 int inset_; | |
| 192 int radius_; | |
| 193 | |
| 194 DISALLOW_COPY_AND_ASSIGN(OverviewContentMask); | |
| 195 }; | |
| 196 | |
| 197 ScopedTransformOverviewWindow::OverviewContentMask::OverviewContentMask( | |
| 198 int inset, | |
| 199 int radius) | |
| 200 : layer_(ui::LAYER_TEXTURED), inset_(inset), radius_(radius) { | |
| 201 layer_.set_delegate(this); | |
| 202 } | |
| 203 | |
| 204 ScopedTransformOverviewWindow::OverviewContentMask::~OverviewContentMask() { | |
| 205 layer_.set_delegate(nullptr); | |
| 206 } | |
| 207 | |
| 208 void ScopedTransformOverviewWindow::OverviewContentMask::OnPaintLayer( | |
| 209 const ui::PaintContext& context) { | |
| 210 ui::PaintRecorder recorder(context, layer()->size()); | |
| 211 gfx::Rect bounds(layer()->bounds().size()); | |
| 212 bounds.Inset(0, inset_, 0, 0); | |
| 213 | |
| 214 // Tile a window into an area, rounding the bottom corners. | |
| 215 const SkRect rect = gfx::RectToSkRect(bounds); | |
| 216 const SkScalar corner_radius_scalar = SkIntToScalar(radius_); | |
| 217 SkScalar radii[8] = {0, | |
| 218 0, // top-left | |
| 219 0, | |
| 220 0, // top-right | |
| 221 corner_radius_scalar, | |
| 222 corner_radius_scalar, // bottom-right | |
| 223 corner_radius_scalar, | |
| 224 corner_radius_scalar}; // bottom-left | |
| 225 SkPath path; | |
| 226 path.addRoundRect(rect, radii, SkPath::kCW_Direction); | |
| 227 | |
| 228 // Set a mask. | |
| 229 SkPaint paint; | |
| 230 paint.setAlpha(kOverviewContentMaskAlpha); | |
| 231 paint.setStyle(SkPaint::kFill_Style); | |
| 232 paint.setAntiAlias(true); | |
| 233 recorder.canvas()->DrawPath(path, paint); | |
| 234 } | |
| 235 | |
| 236 void ScopedTransformOverviewWindow::OverviewContentMask:: | |
| 237 OnDeviceScaleFactorChanged(float device_scale_factor) { | |
| 238 // Redrawing will take care of scale factor change. | |
| 239 } | |
| 240 | |
| 241 base::Closure ScopedTransformOverviewWindow::OverviewContentMask:: | |
| 242 PrepareForLayerBoundsChange() { | |
| 243 return base::Closure(); | |
| 244 } | |
| 245 | |
| 246 ScopedTransformOverviewWindow::ScopedTransformOverviewWindow(WmWindow* window) | |
| 247 : window_(window), | |
| 248 minimized_(window->GetShowState() == ui::SHOW_STATE_MINIMIZED), | |
| 249 ignored_by_shelf_(window->GetWindowState()->ignored_by_shelf()), | |
| 250 overview_started_(false), | |
| 251 original_transform_(window->GetTargetTransform()), | |
| 252 original_opacity_(window->GetTargetOpacity()) {} | |
| 253 | |
| 254 ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() { | |
| 255 } | |
| 256 | |
| 257 void ScopedTransformOverviewWindow::RestoreWindow() { | |
| 258 if (ash::MaterialDesignController::IsOverviewMaterial()) { | |
| 259 window()->GetLayer()->SetMaskLayer(nullptr); | |
| 260 mask_.reset(); | |
| 261 } | |
| 262 | |
| 263 ScopedAnimationSettings animation_settings_list; | |
| 264 BeginScopedAnimation( | |
| 265 OverviewAnimationType::OVERVIEW_ANIMATION_RESTORE_WINDOW, | |
| 266 &animation_settings_list); | |
| 267 SetTransform(window()->GetRootWindow(), original_transform_, 0); | |
| 268 | |
| 269 std::unique_ptr<ScopedOverviewAnimationSettings> animation_settings = | |
| 270 CreateScopedOverviewAnimationSettings( | |
| 271 OverviewAnimationType::OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS, | |
| 272 window_); | |
| 273 gfx::Transform transform; | |
| 274 if (minimized_ && window_->GetShowState() != ui::SHOW_STATE_MINIMIZED) { | |
| 275 // Setting opacity 0 and visible false ensures that the property change | |
| 276 // to SHOW_STATE_MINIMIZED will not animate the window from its original | |
| 277 // bounds to the minimized position. | |
| 278 // Hiding the window needs to be done before the target opacity is 0, | |
| 279 // otherwise the layer's visibility will not be updated | |
| 280 // (See VisibilityController::UpdateLayerVisibility). | |
| 281 window_->Hide(); | |
| 282 window_->SetOpacity(0); | |
| 283 window_->SetShowState(ui::SHOW_STATE_MINIMIZED); | |
| 284 } | |
| 285 window_->GetWindowState()->set_ignored_by_shelf(ignored_by_shelf_); | |
| 286 SetOpacity(original_opacity_); | |
| 287 } | |
| 288 | |
| 289 void ScopedTransformOverviewWindow::BeginScopedAnimation( | |
| 290 OverviewAnimationType animation_type, | |
| 291 ScopedAnimationSettings* animation_settings) { | |
| 292 for (const auto& window : GetTransientTreeIterator(window_)) { | |
| 293 animation_settings->push_back( | |
| 294 CreateScopedOverviewAnimationSettings(animation_type, window)); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 bool ScopedTransformOverviewWindow::Contains(const WmWindow* target) const { | |
| 299 for (const auto& window : GetTransientTreeIterator(window_)) { | |
| 300 if (window->Contains(target)) | |
| 301 return true; | |
| 302 } | |
| 303 return false; | |
| 304 } | |
| 305 | |
| 306 gfx::Rect ScopedTransformOverviewWindow::GetTargetBoundsInScreen() const { | |
| 307 gfx::Rect bounds; | |
| 308 for (const auto& window : GetTransientTreeIterator(window_)) { | |
| 309 // Ignore other window types when computing bounding box of window | |
| 310 // selector target item. | |
| 311 if (window != window_ && window->GetType() != ui::wm::WINDOW_TYPE_NORMAL && | |
| 312 window->GetType() != ui::wm::WINDOW_TYPE_PANEL) { | |
| 313 continue; | |
| 314 } | |
| 315 bounds.Union( | |
| 316 window->GetParent()->ConvertRectToScreen(window->GetTargetBounds())); | |
| 317 } | |
| 318 return bounds; | |
| 319 } | |
| 320 | |
| 321 void ScopedTransformOverviewWindow::ShowWindowIfMinimized() { | |
| 322 if (minimized_ && window_->GetShowState() == ui::SHOW_STATE_MINIMIZED) | |
| 323 window_->Show(); | |
| 324 } | |
| 325 | |
| 326 void ScopedTransformOverviewWindow::ShowWindowOnExit() { | |
| 327 if (minimized_) { | |
| 328 minimized_ = false; | |
| 329 original_transform_ = gfx::Transform(); | |
| 330 original_opacity_ = kRestoreWindowOpacity; | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 void ScopedTransformOverviewWindow::OnWindowDestroyed() { | |
| 335 window_ = nullptr; | |
| 336 } | |
| 337 | |
| 338 float ScopedTransformOverviewWindow::GetItemScale(const gfx::Size& source, | |
| 339 const gfx::Size& target, | |
| 340 int top_view_inset, | |
| 341 int title_height) { | |
| 342 if (ash::MaterialDesignController::IsOverviewMaterial()) { | |
| 343 return std::min(2.0f, static_cast<float>((target.height() - title_height)) / | |
| 344 (source.height() - top_view_inset)); | |
| 345 } | |
| 346 return std::min( | |
| 347 1.0f, std::min(static_cast<float>(target.width()) / source.width(), | |
| 348 static_cast<float>(target.height()) / source.height())); | |
| 349 } | |
| 350 | |
| 351 gfx::Rect ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio( | |
| 352 const gfx::Rect& rect, | |
| 353 const gfx::Rect& bounds, | |
| 354 int top_view_inset, | |
| 355 int title_height) { | |
| 356 DCHECK(!rect.IsEmpty()); | |
| 357 DCHECK_LE(top_view_inset, rect.height()); | |
| 358 const float scale = | |
| 359 GetItemScale(rect.size(), bounds.size(), top_view_inset, title_height); | |
| 360 if (!ash::MaterialDesignController::IsOverviewMaterial()) { | |
| 361 return gfx::Rect( | |
| 362 bounds.x() + 0.5 * (bounds.width() - scale * rect.width()), | |
| 363 bounds.y() + title_height - scale * top_view_inset + | |
| 364 0.5 * (bounds.height() - | |
| 365 (title_height + scale * (rect.height() - top_view_inset))), | |
| 366 rect.width() * scale, rect.height() * scale); | |
| 367 } | |
| 368 const int horizontal_offset = gfx::ToFlooredInt( | |
| 369 0.5 * (bounds.width() - gfx::ToFlooredInt(scale * rect.width()))); | |
| 370 const int width = bounds.width() - 2 * horizontal_offset; | |
| 371 const int vertical_offset = | |
| 372 title_height - gfx::ToCeiledInt(scale * top_view_inset); | |
| 373 const int height = std::min(gfx::ToCeiledInt(scale * rect.height()), | |
| 374 bounds.height() - vertical_offset); | |
| 375 return gfx::Rect(bounds.x() + horizontal_offset, bounds.y() + vertical_offset, | |
| 376 width, height); | |
| 377 } | |
| 378 | |
| 379 gfx::Transform ScopedTransformOverviewWindow::GetTransformForRect( | |
| 380 const gfx::Rect& src_rect, | |
| 381 const gfx::Rect& dst_rect) { | |
| 382 DCHECK(!src_rect.IsEmpty()); | |
| 383 gfx::Transform transform; | |
| 384 transform.Translate(dst_rect.x() - src_rect.x(), | |
| 385 dst_rect.y() - src_rect.y()); | |
| 386 transform.Scale(static_cast<float>(dst_rect.width()) / src_rect.width(), | |
| 387 static_cast<float>(dst_rect.height()) / src_rect.height()); | |
| 388 return transform; | |
| 389 } | |
| 390 | |
| 391 void ScopedTransformOverviewWindow::SetTransform( | |
| 392 WmWindow* root_window, | |
| 393 const gfx::Transform& transform, | |
| 394 int radius) { | |
| 395 DCHECK(overview_started_); | |
| 396 | |
| 397 if (ash::MaterialDesignController::IsOverviewMaterial()) { | |
| 398 mask_.reset(new OverviewContentMask( | |
| 399 window()->GetIntProperty(WmWindowProperty::TOP_VIEW_INSET), radius)); | |
| 400 mask_->layer()->SetBounds(GetTargetBoundsInScreen()); | |
| 401 window()->GetLayer()->SetMaskLayer(mask_->layer()); | |
| 402 } | |
| 403 | |
| 404 gfx::Point target_origin(GetTargetBoundsInScreen().origin()); | |
| 405 | |
| 406 for (const auto& window : GetTransientTreeIterator(window_)) { | |
| 407 WmWindow* parent_window = window->GetParent(); | |
| 408 gfx::Point original_origin = | |
| 409 parent_window->ConvertRectToScreen(window->GetTargetBounds()).origin(); | |
| 410 gfx::Transform new_transform = TransformAboutPivot( | |
| 411 gfx::Point(target_origin.x() - original_origin.x(), | |
| 412 target_origin.y() - original_origin.y()), | |
| 413 transform); | |
| 414 window->SetTransform(new_transform); | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 void ScopedTransformOverviewWindow::SetOpacity(float opacity) { | |
| 419 for (const auto& window : GetTransientTreeIterator(window_)) { | |
| 420 window->SetOpacity(opacity); | |
| 421 } | |
| 422 } | |
| 423 | |
| 424 void ScopedTransformOverviewWindow::Close() { | |
| 425 GetTransientRoot(window_)->CloseWidget(); | |
| 426 } | |
| 427 | |
| 428 void ScopedTransformOverviewWindow::PrepareForOverview() { | |
| 429 DCHECK(!overview_started_); | |
| 430 overview_started_ = true; | |
| 431 window_->GetWindowState()->set_ignored_by_shelf(true); | |
| 432 ShowWindowIfMinimized(); | |
| 433 } | |
| 434 | |
| 435 } // namespace ash | |
| OLD | NEW |