overview_item_view.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // Copyright 2019 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/overview/overview_item_view.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include "ash/strings/grit/ash_strings.h"
  8. #include "ash/style/close_button.h"
  9. #include "ash/wm/overview/overview_constants.h"
  10. #include "ash/wm/overview/overview_grid.h"
  11. #include "ash/wm/overview/overview_item.h"
  12. #include "ash/wm/window_preview_view.h"
  13. #include "ash/wm/wm_highlight_item_border.h"
  14. #include "base/containers/contains.h"
  15. #include "ui/accessibility/ax_enums.mojom.h"
  16. #include "ui/accessibility/ax_node_data.h"
  17. #include "ui/aura/window.h"
  18. #include "ui/base/l10n/l10n_util.h"
  19. #include "ui/base/metadata/metadata_impl_macros.h"
  20. #include "ui/compositor/layer.h"
  21. #include "ui/compositor/scoped_layer_animation_settings.h"
  22. #include "ui/gfx/geometry/size_conversions.h"
  23. #include "ui/strings/grit/ui_strings.h"
  24. #include "ui/views/animation/ink_drop.h"
  25. #include "ui/views/controls/label.h"
  26. #include "ui/views/widget/widget.h"
  27. namespace ash {
  28. namespace {
  29. // Duration of the show/hide animation of the header.
  30. constexpr base::TimeDelta kHeaderFadeDuration = base::Milliseconds(167);
  31. // Delay before the show animation of the header.
  32. constexpr base::TimeDelta kHeaderFadeInDelay = base::Milliseconds(83);
  33. // Duration of the slow show animation of the close button.
  34. constexpr base::TimeDelta kCloseButtonSlowFadeInDuration =
  35. base::Milliseconds(300);
  36. // Delay before the slow show animation of the close button.
  37. constexpr base::TimeDelta kCloseButtonSlowFadeInDelay = base::Milliseconds(750);
  38. // Value should match the one in
  39. // ash/resources/vector_icons/overview_window_close.icon.
  40. constexpr int kCloseButtonIconMarginDp = 5;
  41. // Animates |layer| from 0 -> 1 opacity if |visible| and 1 -> 0 opacity
  42. // otherwise. The tween type differs for |visible| and if |visible| is true
  43. // there is a slight delay before the animation begins. Does not animate if
  44. // opacity matches |visible|.
  45. void AnimateLayerOpacity(ui::Layer* layer, bool visible) {
  46. float target_opacity = visible ? 1.f : 0.f;
  47. if (layer->GetTargetOpacity() == target_opacity)
  48. return;
  49. layer->SetOpacity(1.f - target_opacity);
  50. gfx::Tween::Type tween =
  51. visible ? gfx::Tween::LINEAR_OUT_SLOW_IN : gfx::Tween::FAST_OUT_LINEAR_IN;
  52. ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
  53. settings.SetTransitionDuration(kHeaderFadeDuration);
  54. settings.SetTweenType(tween);
  55. settings.SetPreemptionStrategy(ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
  56. if (visible) {
  57. layer->GetAnimator()->SchedulePauseForProperties(
  58. kHeaderFadeInDelay, ui::LayerAnimationElement::OPACITY);
  59. }
  60. layer->SetOpacity(target_opacity);
  61. }
  62. } // namespace
  63. OverviewItemView::OverviewItemView(
  64. OverviewItem* overview_item,
  65. views::Button::PressedCallback close_callback,
  66. aura::Window* window,
  67. bool show_preview)
  68. : WindowMiniView(window),
  69. overview_item_(overview_item),
  70. close_button_(header_view()->AddChildView(
  71. std::make_unique<CloseButton>(std::move(close_callback),
  72. CloseButton::Type::kLargeFloating))) {
  73. DCHECK(overview_item_);
  74. // This should not be focusable. It's also to avoid accessibility error when
  75. // |window->GetTitle()| is empty.
  76. SetFocusBehavior(FocusBehavior::NEVER);
  77. views::InkDrop::Get(close_button_)
  78. ->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
  79. close_button_->SetAccessibleName(
  80. l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
  81. close_button_->SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
  82. // Call this last as it calls |Layout()| which relies on the some of the other
  83. // elements existing.
  84. SetShowPreview(show_preview);
  85. // Do not show header if the current overview item is the drop target widget.
  86. if (show_preview || overview_item_->overview_grid()->IsDropTargetWindow(
  87. overview_item_->GetWindow())) {
  88. header_view()->layer()->SetOpacity(0.f);
  89. current_header_visibility_ = HeaderVisibility::kInvisible;
  90. }
  91. UpdateIconView();
  92. }
  93. OverviewItemView::~OverviewItemView() = default;
  94. void OverviewItemView::SetHeaderVisibility(HeaderVisibility visibility) {
  95. DCHECK(header_view()->layer());
  96. if (visibility == current_header_visibility_)
  97. return;
  98. const HeaderVisibility previous_visibility = current_header_visibility_;
  99. current_header_visibility_ = visibility;
  100. const bool all_invisible = visibility == HeaderVisibility::kInvisible;
  101. AnimateLayerOpacity(header_view()->layer(), !all_invisible);
  102. // If there is not a `close_button_`, then we are done.
  103. if (!close_button_)
  104. return;
  105. // If the whole header is fading out and there is a `close_button_`, then
  106. // we need to disable the close button without also fading the close button.
  107. if (all_invisible) {
  108. close_button_->SetEnabled(false);
  109. return;
  110. }
  111. const bool close_button_visible = visibility == HeaderVisibility::kVisible;
  112. // If `header_view()` was hidden and is fading in, set the opacity and enabled
  113. // state of `close_button_` depending on whether the close button should fade
  114. // in with `header_view()` or stay hidden.
  115. if (previous_visibility == HeaderVisibility::kInvisible) {
  116. close_button_->layer()->SetOpacity(close_button_visible ? 1.f : 0.f);
  117. close_button_->SetEnabled(close_button_visible);
  118. return;
  119. }
  120. AnimateLayerOpacity(close_button_->layer(), close_button_visible);
  121. close_button_->SetEnabled(close_button_visible);
  122. }
  123. void OverviewItemView::HideCloseInstantlyAndThenShowItSlowly() {
  124. DCHECK(close_button_);
  125. DCHECK_NE(HeaderVisibility::kInvisible, current_header_visibility_);
  126. ui::Layer* layer = close_button_->layer();
  127. DCHECK(layer);
  128. current_header_visibility_ = HeaderVisibility::kVisible;
  129. layer->SetOpacity(0.f);
  130. ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
  131. settings.SetTransitionDuration(kCloseButtonSlowFadeInDuration);
  132. settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
  133. settings.SetPreemptionStrategy(ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
  134. layer->GetAnimator()->SchedulePauseForProperties(
  135. kCloseButtonSlowFadeInDelay, ui::LayerAnimationElement::OPACITY);
  136. layer->SetOpacity(1.f);
  137. close_button_->SetEnabled(true);
  138. }
  139. void OverviewItemView::OnOverviewItemWindowRestoring() {
  140. overview_item_ = nullptr;
  141. close_button_->ResetListener();
  142. }
  143. void OverviewItemView::RefreshPreviewView() {
  144. if (!preview_view())
  145. return;
  146. preview_view()->RecreatePreviews();
  147. Layout();
  148. }
  149. gfx::Rect OverviewItemView::GetHeaderBounds() const {
  150. // We want to align the edges of the image as shown below in the diagram. The
  151. // resource itself contains some padding, which is the distance from the edges
  152. // of the image to the edges of the vector icon. The icon keeps its size in
  153. // dips and is centered in the middle of the preferred width, so the
  154. // additional padding would be equal to half the difference in width between
  155. // the preferred width and the image size. The resulting padding would be that
  156. // number plus the padding in the resource, in dips.
  157. const int image_width = kIconSize.width();
  158. const int close_button_width = close_button_->GetPreferredSize().width();
  159. const int right_padding =
  160. (close_button_width - image_width) / 2 + kCloseButtonIconMarginDp;
  161. // Positions the header in a way so that the right aligned close button is
  162. // aligned so that the edge of its icon, not the button lines up with the
  163. // margins. In the diagram below, a represents the the right edge of the
  164. // provided icon (which contains some padding), b represents the right edge of
  165. // |close_button_| and c represents the right edge of the local bounds.
  166. // ---------------------------+---------+
  167. // | +---+ |
  168. // |title_label_| | | a b
  169. // | +---+ |
  170. // ---------------------------+---------+
  171. // c
  172. // |
  173. // The size of this view is larger than that of the visible elements. Position
  174. // the header so that the margin is accounted for, then shift the right bounds
  175. // by a bit so that the close button resource lines up with the right edge of
  176. // the visible region.
  177. const gfx::Rect contents_bounds(GetContentsBounds());
  178. return gfx::Rect(contents_bounds.x(), contents_bounds.y(),
  179. contents_bounds.width() + right_padding, kHeaderHeightDp);
  180. }
  181. gfx::Size OverviewItemView::GetPreviewViewSize() const {
  182. // The preview should expand to fit the bounds allocated for the content,
  183. // except if it is letterboxed or pillarboxed.
  184. const gfx::SizeF preview_pref_size(preview_view()->GetPreferredSize());
  185. const float aspect_ratio =
  186. preview_pref_size.width() / preview_pref_size.height();
  187. gfx::SizeF target_size(GetContentAreaBounds().size());
  188. OverviewGridWindowFillMode fill_mode =
  189. overview_item_ ? overview_item_->GetWindowDimensionsType()
  190. : OverviewGridWindowFillMode::kNormal;
  191. switch (fill_mode) {
  192. case OverviewGridWindowFillMode::kNormal:
  193. break;
  194. case OverviewGridWindowFillMode::kLetterBoxed:
  195. target_size.set_height(target_size.width() / aspect_ratio);
  196. break;
  197. case OverviewGridWindowFillMode::kPillarBoxed:
  198. target_size.set_width(target_size.height() * aspect_ratio);
  199. break;
  200. }
  201. return gfx::ToRoundedSize(target_size);
  202. }
  203. views::View* OverviewItemView::GetView() {
  204. return this;
  205. }
  206. void OverviewItemView::MaybeActivateHighlightedView() {
  207. if (overview_item_)
  208. overview_item_->OnHighlightedViewActivated();
  209. }
  210. void OverviewItemView::MaybeCloseHighlightedView(bool primary_action) {
  211. if (overview_item_ && primary_action)
  212. overview_item_->OnHighlightedViewClosed();
  213. }
  214. void OverviewItemView::MaybeSwapHighlightedView(bool right) {}
  215. bool OverviewItemView::MaybeActivateHighlightedViewOnOverviewExit(
  216. OverviewSession* overview_session) {
  217. DCHECK(overview_session);
  218. overview_session->SelectWindow(overview_item_);
  219. return true;
  220. }
  221. void OverviewItemView::OnViewHighlighted() {
  222. UpdateBorderState(/*show=*/true);
  223. }
  224. void OverviewItemView::OnViewUnhighlighted() {
  225. UpdateBorderState(/*show=*/false);
  226. }
  227. gfx::Point OverviewItemView::GetMagnifierFocusPointInScreen() {
  228. // When this item is tabbed into, put the magnifier focus on the front of the
  229. // title, so that users can read the title first thing.
  230. const gfx::Rect title_bounds = title_label()->GetBoundsInScreen();
  231. return gfx::Point(GetMirroredXInView(title_bounds.x()),
  232. title_bounds.CenterPoint().y());
  233. }
  234. bool OverviewItemView::OnMousePressed(const ui::MouseEvent& event) {
  235. if (!overview_item_)
  236. return views::View::OnMousePressed(event);
  237. overview_item_->HandleMouseEvent(event);
  238. return true;
  239. }
  240. bool OverviewItemView::OnMouseDragged(const ui::MouseEvent& event) {
  241. if (!overview_item_)
  242. return views::View::OnMouseDragged(event);
  243. overview_item_->HandleMouseEvent(event);
  244. return true;
  245. }
  246. void OverviewItemView::OnMouseReleased(const ui::MouseEvent& event) {
  247. if (!overview_item_) {
  248. views::View::OnMouseReleased(event);
  249. return;
  250. }
  251. overview_item_->HandleMouseEvent(event);
  252. }
  253. void OverviewItemView::OnGestureEvent(ui::GestureEvent* event) {
  254. if (!overview_item_)
  255. return;
  256. overview_item_->HandleGestureEvent(event);
  257. event->SetHandled();
  258. }
  259. bool OverviewItemView::CanAcceptEvent(const ui::Event& event) {
  260. bool accept_events = true;
  261. // Do not process or accept press down events that are on the border.
  262. static ui::EventType press_types[] = {ui::ET_GESTURE_TAP_DOWN,
  263. ui::ET_MOUSE_PRESSED};
  264. if (event.IsLocatedEvent() && base::Contains(press_types, event.type())) {
  265. const gfx::Rect content_bounds = GetContentsBounds();
  266. if (!content_bounds.Contains(event.AsLocatedEvent()->location()))
  267. accept_events = false;
  268. }
  269. return accept_events && views::View::CanAcceptEvent(event);
  270. }
  271. void OverviewItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  272. WindowMiniView::GetAccessibleNodeData(node_data);
  273. // TODO: This doesn't allow |this| to be navigated by ChromeVox, find a way
  274. // to allow |this| as well as the title and close button.
  275. node_data->role = ax::mojom::Role::kGenericContainer;
  276. node_data->AddStringAttribute(
  277. ax::mojom::StringAttribute::kDescription,
  278. l10n_util::GetStringUTF8(
  279. IDS_ASH_OVERVIEW_CLOSABLE_HIGHLIGHT_ITEM_A11Y_EXTRA_TIP));
  280. }
  281. void OverviewItemView::OnThemeChanged() {
  282. WindowMiniView::OnThemeChanged();
  283. UpdateBorderState(IsViewHighlighted());
  284. }
  285. BEGIN_METADATA(OverviewItemView, WindowMiniView)
  286. END_METADATA
  287. } // namespace ash