resize_shadow.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. #include "ash/wm/resize_shadow.h"
  5. #include "base/no_destructor.h"
  6. #include "base/time/time.h"
  7. #include "ui/aura/window.h"
  8. #include "ui/compositor/layer.h"
  9. #include "ui/compositor/scoped_layer_animation_settings.h"
  10. #include "ui/gfx/canvas.h"
  11. #include "ui/gfx/geometry/insets.h"
  12. #include "ui/gfx/image/canvas_image_source.h"
  13. namespace {
  14. // This class simply draws a roundrect. The layout and tiling is handled by
  15. // ResizeShadow and NinePatchLayer.
  16. class ResizeShadowImageSource : public gfx::CanvasImageSource {
  17. public:
  18. ResizeShadowImageSource(int width, int corner_radius, SkColor color)
  19. : gfx::CanvasImageSource(gfx::Size(width, width)),
  20. shadow_corner_radius_(corner_radius),
  21. color_(color) {}
  22. ResizeShadowImageSource(const ResizeShadowImageSource&) = delete;
  23. ResizeShadowImageSource& operator=(const ResizeShadowImageSource&) = delete;
  24. ~ResizeShadowImageSource() override = default;
  25. // gfx::CanvasImageSource:
  26. void Draw(gfx::Canvas* canvas) override {
  27. cc::PaintFlags paint;
  28. paint.setAntiAlias(true);
  29. paint.setColor(color_);
  30. canvas->DrawRoundRect(gfx::RectF(gfx::SizeF(size())), shadow_corner_radius_,
  31. paint);
  32. }
  33. int shadow_corner_radius_;
  34. SkColor color_;
  35. };
  36. // Calculate outsets of the |window_| based on a |hit_test| code and |thickness|
  37. const gfx::Insets CalculateOutsets(int hit, int thickness) {
  38. bool show_top = hit == HTTOPLEFT || hit == HTTOP || hit == HTTOPRIGHT;
  39. bool show_left = hit == HTTOPLEFT || hit == HTLEFT || hit == HTBOTTOMLEFT;
  40. bool show_bottom =
  41. hit == HTBOTTOMLEFT || hit == HTBOTTOM || hit == HTBOTTOMRIGHT;
  42. bool show_right = hit == HTTOPRIGHT || hit == HTRIGHT || hit == HTBOTTOMRIGHT;
  43. const int outset = -thickness;
  44. return gfx::Insets::TLBR(show_top ? outset : 0, show_left ? outset : 0,
  45. show_bottom ? outset : 0, show_right ? outset : 0);
  46. }
  47. // static
  48. const gfx::ImageSkia& MakeShadowImageOnce(
  49. ash::ResizeShadowType type,
  50. const ash::ResizeShadow::InitParams& params) {
  51. // The image has to have enough space to depict the visual thickness
  52. // (left and right) plus an inset for extending beneath the window's
  53. // rounded corner plus one pixel for the center of the nine patch.
  54. int image_side = 2 * (params.thickness + params.window_corner_radius) + 1;
  55. switch (type) {
  56. case ash::ResizeShadowType::kLock: {
  57. static base::NoDestructor<gfx::ImageSkia> lock_shadow_image;
  58. if (lock_shadow_image->isNull()) {
  59. *lock_shadow_image =
  60. gfx::CanvasImageSource::MakeImageSkia<ResizeShadowImageSource>(
  61. image_side, params.shadow_corner_radius, params.color);
  62. }
  63. return *lock_shadow_image;
  64. }
  65. case ash::ResizeShadowType::kUnlock: {
  66. static base::NoDestructor<gfx::ImageSkia> unlock_shadow_image;
  67. if (unlock_shadow_image->isNull()) {
  68. *unlock_shadow_image =
  69. gfx::CanvasImageSource::MakeImageSkia<ResizeShadowImageSource>(
  70. image_side, params.shadow_corner_radius, params.color);
  71. }
  72. return *unlock_shadow_image;
  73. }
  74. }
  75. }
  76. } // namespace
  77. namespace ash {
  78. ResizeShadow::ResizeShadow(aura::Window* window,
  79. const InitParams& params,
  80. ResizeShadowType type)
  81. : window_(window), params_(params), type_(type) {
  82. // Use a NinePatchLayer to tile the shadow image (which is simply a
  83. // roundrect).
  84. gfx::Insets aperture_insets(params_.thickness + params_.window_corner_radius);
  85. layer_ = std::make_unique<ui::Layer>(ui::LAYER_NINE_PATCH);
  86. layer_->SetName("WindowResizeShadow");
  87. layer_->SetFillsBoundsOpaquely(false);
  88. layer_->SetOpacity(0.f);
  89. layer_->SetVisible(false);
  90. auto shadow_image = MakeShadowImageOnce(type, params);
  91. layer_->UpdateNinePatchLayerImage(shadow_image);
  92. gfx::Rect aperture(shadow_image.size());
  93. aperture.Inset(aperture_insets);
  94. layer_->UpdateNinePatchLayerAperture(aperture);
  95. layer_->UpdateNinePatchLayerBorder(
  96. gfx::Rect(aperture_insets.left(), aperture_insets.top(),
  97. aperture_insets.width(), aperture_insets.height()));
  98. ReparentLayer();
  99. }
  100. ResizeShadow::~ResizeShadow() = default;
  101. void ResizeShadow::ShowForHitTest(int hit) {
  102. UpdateHitTest(hit);
  103. visible_ = true;
  104. UpdateBoundsAndVisibility();
  105. }
  106. void ResizeShadow::Hide() {
  107. UpdateHitTest(HTNOWHERE);
  108. visible_ = false;
  109. UpdateBoundsAndVisibility();
  110. }
  111. void ResizeShadow::UpdateHitTest(int hit) {
  112. // Don't start animations unless something changed.
  113. if (hit == last_hit_test_)
  114. return;
  115. last_hit_test_ = hit;
  116. }
  117. void ResizeShadow::UpdateBoundsAndVisibility() {
  118. UpdateBounds(window_->bounds());
  119. }
  120. void ResizeShadow::UpdateBounds(const gfx::Rect& window_bounds) {
  121. // The shadow layer is positioned such that one or two edges will stick out
  122. // from underneath |window_|. Thus |window_| occludes the rest of the
  123. // roundrect.
  124. const gfx::Insets outsets =
  125. params_.hit_test_enabled
  126. ? CalculateOutsets(last_hit_test_, params_.thickness)
  127. : gfx::Insets(-params_.thickness);
  128. if (outsets.IsEmpty() && !layer_->GetTargetVisibility())
  129. return;
  130. visible_ &= !outsets.IsEmpty();
  131. if (visible_) {
  132. gfx::Rect bounds = window_bounds;
  133. bounds.Inset(outsets);
  134. layer_->SetBounds(bounds);
  135. }
  136. ui::ScopedLayerAnimationSettings settings(layer_->GetAnimator());
  137. if (!visible_)
  138. settings.SetTransitionDuration(
  139. base::Milliseconds(params_.hide_duration_ms));
  140. layer_->SetOpacity(visible_ ? params_.opacity : 0.f);
  141. layer_->SetVisible(visible_);
  142. }
  143. void ResizeShadow::ReparentLayer() {
  144. DCHECK(window_->layer()->parent());
  145. if (layer_->parent() != window_->layer()->parent())
  146. window_->layer()->parent()->Add(layer_.get());
  147. layer_->parent()->StackBelow(layer_.get(), window_->layer());
  148. }
  149. } // namespace ash