Commit c50b0d29 authored by Sammie Quon's avatar Sammie Quon Committed by Commit Bot

overview: Windows with extreme bounds have limits in overview mode.

Sometimes windows can be very long or very tall outside of overview
mode. These windows will either become super wide or super skinny after
entering overvie wmode. This is not ideal as too wide -> takes up too
much space and too skinny -> users cannot see the title.

Clamps the width to be within 0.5 and 2 times the height. Windows will
still keep their aspect ratio, so the resulting window is centered in
the window selector item.

https://screenshot.googleplex.com/dSfEciJ5CPd
The above screenshot shows the effect of this CL (minus the blue and
red borders which help visualize). The extreme windows will look they
are floating since the backdrop is not implemented yet. But this cl is all
behind a switch.

Test: ash_unittests WindowSelectorTest.ExtremeWindowBounds
Bug: 782320
Change-Id: Id337eb091fcc4f1297ae8f864175c7f06b10b71c
Reviewed-on: https://chromium-review.googlesource.com/869051
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532029}
parent 70e7884a
......@@ -50,6 +50,23 @@ aura::Window* GetTransientRoot(aura::Window* window) {
return window;
}
ScopedTransformOverviewWindow::GridWindowFillMode GetWindowDimensionsType(
aura::Window* window) {
if (window->bounds().width() >
window->bounds().height() *
ScopedTransformOverviewWindow::kExtremeWindowRatioThreshold) {
return ScopedTransformOverviewWindow::GridWindowFillMode::kLetterBoxed;
}
if (window->bounds().height() >
window->bounds().width() *
ScopedTransformOverviewWindow::kExtremeWindowRatioThreshold) {
return ScopedTransformOverviewWindow::GridWindowFillMode::kPillarBoxed;
}
return ScopedTransformOverviewWindow::GridWindowFillMode::kNormal;
}
// An iterator class that traverses an aura::Window and all of its transient
// descendants.
class TransientDescendantIterator {
......@@ -261,7 +278,10 @@ ScopedTransformOverviewWindow::ScopedTransformOverviewWindow(
overview_started_(false),
original_transform_(window->layer()->GetTargetTransform()),
original_opacity_(window->layer()->GetTargetOpacity()),
weak_ptr_factory_(this) {}
weak_ptr_factory_(this) {
if (IsNewOverviewUi())
type_ = GetWindowDimensionsType(window);
}
ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() = default;
......@@ -423,26 +443,6 @@ float ScopedTransformOverviewWindow::GetItemScale(const gfx::Size& source,
(source.height() - top_view_inset));
}
gfx::Rect ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
const gfx::Rect& rect,
const gfx::Rect& bounds,
int top_view_inset,
int title_height) {
DCHECK(!rect.IsEmpty());
DCHECK_LE(top_view_inset, rect.height());
const float scale =
GetItemScale(rect.size(), bounds.size(), top_view_inset, title_height);
const int horizontal_offset = gfx::ToFlooredInt(
0.5 * (bounds.width() - gfx::ToFlooredInt(scale * rect.width())));
const int width = bounds.width() - 2 * horizontal_offset;
const int vertical_offset =
title_height - gfx::ToCeiledInt(scale * top_view_inset);
const int height = std::min(gfx::ToCeiledInt(scale * rect.height()),
bounds.height() - vertical_offset);
return gfx::Rect(bounds.x() + horizontal_offset, bounds.y() + vertical_offset,
width, height);
}
gfx::Transform ScopedTransformOverviewWindow::GetTransformForRect(
const gfx::Rect& src_rect,
const gfx::Rect& dst_rect) {
......@@ -492,6 +492,68 @@ void ScopedTransformOverviewWindow::UpdateMirrorWindowForMinimizedState() {
}
}
gfx::Rect ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
const gfx::Rect& rect,
const gfx::Rect& bounds,
int top_view_inset,
int title_height) {
DCHECK(!rect.IsEmpty());
DCHECK_LE(top_view_inset, rect.height());
const float scale =
GetItemScale(rect.size(), bounds.size(), top_view_inset, title_height);
const int horizontal_offset = gfx::ToFlooredInt(
0.5 * (bounds.width() - gfx::ToFlooredInt(scale * rect.width())));
const int width = bounds.width() - 2 * horizontal_offset;
const int vertical_offset =
title_height - gfx::ToCeiledInt(scale * top_view_inset);
const int height = std::min(gfx::ToCeiledInt(scale * rect.height()),
bounds.height() - vertical_offset);
gfx::Rect new_bounds(bounds.x() + horizontal_offset,
bounds.y() + vertical_offset, width, height);
if (!IsNewOverviewUi()) {
DCHECK_EQ(ScopedTransformOverviewWindow::GridWindowFillMode::kNormal,
type());
}
switch (type()) {
case ScopedTransformOverviewWindow::GridWindowFillMode::kLetterBoxed:
case ScopedTransformOverviewWindow::GridWindowFillMode::kPillarBoxed: {
// Attempt to scale |rect| to fit |bounds|. Maintain the aspect ratio of
// |rect|. Letter boxed windows' width will match |bounds|'s height and
// pillar boxed windows' height will match |bounds|'s height.
const bool is_pillar =
type() ==
ScopedTransformOverviewWindow::GridWindowFillMode::kPillarBoxed;
gfx::Rect src = rect;
new_bounds = bounds;
src.Inset(0, top_view_inset, 0, 0);
new_bounds.Inset(0, title_height, 0, 0);
float scale = is_pillar ? static_cast<float>(new_bounds.height()) /
static_cast<float>(src.height())
: static_cast<float>(new_bounds.width()) /
static_cast<float>(src.width());
gfx::Size size(is_pillar ? src.width() * scale : new_bounds.width(),
is_pillar ? new_bounds.height() : src.height() * scale);
new_bounds.ClampToCenteredSize(size);
// Extend |new_bounds| in the vertical direction to account for the header
// that will be hidden.
if (top_view_inset > 0)
new_bounds.Inset(0, -(scale * top_view_inset), 0, 0);
// Save the original bounds minus the title into |window_selector_bounds_|
// so a larger backdrop can be drawn behind the window after.
window_selector_bounds_ = bounds;
window_selector_bounds_->Inset(0, title_height, 0, 0);
break;
}
default:
break;
}
return new_bounds;
}
void ScopedTransformOverviewWindow::Close() {
if (immediate_close_for_tests) {
CloseWidget();
......@@ -622,6 +684,14 @@ ScopedTransformOverviewWindow::GetOverviewWindowForMinimizedState() const {
return minimized_widget_ ? minimized_widget_->GetNativeWindow() : nullptr;
}
void ScopedTransformOverviewWindow::UpdateWindowDimensionsType() {
if (!IsNewOverviewUi())
return;
type_ = GetWindowDimensionsType(window_);
window_selector_bounds_.reset();
}
void ScopedTransformOverviewWindow::CreateMirrorWindowForMinimizedState() {
DCHECK(!minimized_widget_.get());
views::Widget::InitParams params;
......
......@@ -12,9 +12,11 @@
#include "ash/wm/overview/overview_animation_type.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/transform.h"
......@@ -51,8 +53,20 @@ class ASH_EXPORT ScopedTransformOverviewWindow
: public ui::EventHandler,
public ui::ImplicitAnimationObserver {
public:
class OverviewContentMask;
using ShapeRects = std::vector<gfx::Rect>;
// Overview windows have certain properties if their aspect ratio exceedes a
// threshold. This enum keeps track of which category the window falls into,
// based on its aspect ratio.
enum class GridWindowFillMode {
kNormal = 0,
kLetterBoxed,
kPillarBoxed,
};
// Windows whose aspect ratio surpass this (width twice as large as height or
// vice versa) will be classified as too wide or too tall and will be handled
// slightly differently in overview mode.
static constexpr float kExtremeWindowRatioThreshold = 2.f;
using ScopedAnimationSettings =
std::vector<std::unique_ptr<ScopedOverviewAnimationSettings>>;
......@@ -63,15 +77,6 @@ class ASH_EXPORT ScopedTransformOverviewWindow
int top_view_inset,
int title_height);
// Returns |rect| having been shrunk to fit within |bounds| (preserving the
// aspect ratio). Takes into account a window header that is |top_view_inset|
// tall in the original window getting replaced by a window caption that is
// |title_height| tall in the transformed window.
static gfx::Rect ShrinkRectToFitPreservingAspectRatio(const gfx::Rect& rect,
const gfx::Rect& bounds,
int top_view_inset,
int title_height);
// Returns the transform turning |src_rect| into |dst_rect|.
static gfx::Transform GetTransformForRect(const gfx::Rect& src_rect,
const gfx::Rect& dst_rect);
......@@ -139,8 +144,24 @@ class ASH_EXPORT ScopedTransformOverviewWindow
// Creates/Deletes a mirror window for minimized windows.
void UpdateMirrorWindowForMinimizedState();
// Returns |rect| having been shrunk to fit within |bounds| (preserving the
// aspect ratio). Takes into account a window header that is |top_view_inset|
// tall in the original window getting replaced by a window caption that is
// |title_height| tall in the transformed window. If |type_| is not normal,
// write |window_selector_bounds_|, which would differ than the return bounds.
gfx::Rect ShrinkRectToFitPreservingAspectRatio(const gfx::Rect& rect,
const gfx::Rect& bounds,
int top_view_inset,
int title_height);
aura::Window* window() const { return window_; }
GridWindowFillMode type() const { return type_; }
base::Optional<gfx::Rect> window_selector_bounds() const {
return window_selector_bounds_;
}
// Closes the transient root of the window managed by |this|.
void Close();
......@@ -155,6 +176,10 @@ class ASH_EXPORT ScopedTransformOverviewWindow
// does not exist.
aura::Window* GetOverviewWindowForMinimizedState() const;
// Called via WindowSelectorItem from WindowGrid when |window_|'s bounds
// change. Must be called before PositionWindows in WindowGrid.
void UpdateWindowDimensionsType();
// ui::EventHandler:
void OnGestureEvent(ui::GestureEvent* event) override;
void OnMouseEvent(ui::MouseEvent* event) override;
......@@ -193,6 +218,13 @@ class ASH_EXPORT ScopedTransformOverviewWindow
// The original opacity of the window before entering overview mode.
float original_opacity_;
// Specifies how the window is laid out in the grid.
GridWindowFillMode type_ = GridWindowFillMode::kNormal;
// Empty if window is of type normal. Contains the bounds the window selector
// item should be if the window is too wide or too tall.
base::Optional<gfx::Rect> window_selector_bounds_;
// A widget that holds the content for the minimized window.
std::unique_ptr<views::Widget> minimized_widget_;
......
......@@ -735,6 +735,7 @@ void WindowGrid::OnWindowBoundsChanged(aura::Window* window,
// Immediately finish any active bounds animation.
window->layer()->GetAnimator()->StopAnimatingProperty(
ui::LayerAnimationElement::BOUNDS);
(*iter)->UpdateWindowDimensionsType();
PositionWindows(false);
}
......@@ -955,10 +956,22 @@ bool WindowGrid::FitWindowRectsInBounds(const gfx::Rect& bounds,
size_t i = 0;
for (const auto& window : window_list_) {
const gfx::Rect target_bounds = window->GetTargetBoundsInScreen();
const int width =
std::max(1, gfx::ToFlooredInt(target_bounds.width() *
window->GetItemScale(item_size)) +
2 * kWindowMargin);
int width = std::max(1, gfx::ToFlooredInt(target_bounds.width() *
window->GetItemScale(item_size)) +
2 * kWindowMargin);
switch (window->GetWindowDimensionsType()) {
case ScopedTransformOverviewWindow::GridWindowFillMode::kLetterBoxed:
width = ScopedTransformOverviewWindow::kExtremeWindowRatioThreshold *
height;
break;
case ScopedTransformOverviewWindow::GridWindowFillMode::kPillarBoxed:
width = height /
ScopedTransformOverviewWindow::kExtremeWindowRatioThreshold;
break;
default:
break;
}
if (left + width > bounds.right()) {
// Move to the next row if possible.
if (*min_right > left)
......
......@@ -711,6 +711,15 @@ void WindowSelectorItem::UpdateCannotSnapWarningVisibility() {
caption_container_view_->SetCannotSnapLabelVisibility(visible);
}
ScopedTransformOverviewWindow::GridWindowFillMode
WindowSelectorItem::GetWindowDimensionsType() const {
return transform_window_.type();
}
void WindowSelectorItem::UpdateWindowDimensionsType() {
transform_window_.UpdateWindowDimensionsType();
}
void WindowSelectorItem::SetDimmed(bool dimmed) {
dimmed_ = dimmed;
SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
......@@ -800,7 +809,7 @@ void WindowSelectorItem::SetItemBounds(const gfx::Rect& target_bounds,
const int top_view_inset = transform_window_.GetTopInset();
const int title_height = close_button_->GetPreferredSize().height();
gfx::Rect selector_item_bounds =
ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
transform_window_.ShrinkRectToFitPreservingAspectRatio(
screen_rect, target_bounds, top_view_inset, title_height);
gfx::Transform transform = ScopedTransformOverviewWindow::GetTransformForRect(
screen_rect, selector_item_bounds);
......@@ -902,7 +911,9 @@ void WindowSelectorItem::CreateWindowLabel(const base::string16& title) {
void WindowSelectorItem::UpdateHeaderLayout(
HeaderFadeInMode mode,
OverviewAnimationType animation_type) {
gfx::Rect transformed_window_bounds(transform_window_.GetTransformedBounds());
gfx::Rect transformed_window_bounds =
transform_window_.window_selector_bounds().value_or(
transform_window_.GetTransformedBounds());
::wm::ConvertRectFromScreen(root_window_, &transformed_window_bounds);
gfx::Rect label_rect(close_button_->GetPreferredSize());
......
......@@ -122,6 +122,13 @@ class ASH_EXPORT WindowSelectorItem : public views::ButtonListener,
// window cannot be snapped.
void UpdateCannotSnapWarningVisibility();
ScopedTransformOverviewWindow::GridWindowFillMode GetWindowDimensionsType()
const;
// Recalculates the window dimensions type of |transform_window_|. Called when
// |window_|'s bounds change.
void UpdateWindowDimensionsType();
// Sets if the item is dimmed in the overview. Changing the value will also
// change the visibility of the transform windows.
void SetDimmed(bool dimmed);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment