Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2637)

Unified Diff: ash/wm/splitview/split_view_controller.cc

Issue 2960843004: CrOS Tablet Window management - Split Screen part I (Closed)
Patch Set: nit. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ash/wm/splitview/split_view_controller.cc
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..db08fcef8ec4c9a53eb0ad0804ea9886a749c392
--- /dev/null
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -0,0 +1,352 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/splitview/split_view_controller.h"
+
+#include "ash/ash_switches.h"
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/screen_util.h"
+#include "ash/shell.h"
+#include "ash/wm/maximize_mode/maximize_mode_controller.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "ash/wm/wm_event.h"
+#include "base/command_line.h"
+#include "ui/aura/window.h"
+#include "ui/wm/core/coordinate_conversion.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace ash {
+
+namespace {
+
+// Returns true if |window| can be activated and snapped.
+bool CanSnap(aura::Window* window) {
+ return wm::CanActivateWindow(window) ? wm::GetWindowState(window)->CanSnap()
+ : false;
+}
+
+} // namespace
+
+SplitViewController::SplitViewController() {
+ Shell::Get()->AddShellObserver(this);
+ Shell::Get()->activation_client()->AddObserver(this);
+}
+
+SplitViewController::~SplitViewController() {
+ Shell::Get()->RemoveShellObserver(this);
+ Shell::Get()->activation_client()->RemoveObserver(this);
+ EndSplitView();
+}
+
+// static
+bool SplitViewController::ShouldAllowSplitView() {
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshEnableTabletSplitView)) {
+ return false;
+ }
+ if (!Shell::Get()
+ ->maximize_mode_controller()
+ ->IsMaximizeModeWindowManagerEnabled()) {
+ return false;
+ }
+ return true;
+}
+
+bool SplitViewController::IsSplitViewModeActive() const {
+ return state_ != NO_SNAP;
+}
+
+void SplitViewController::SetLeftWindow(aura::Window* left_window) {
+ DCHECK(left_window);
+
+ if (state_ == NO_SNAP)
+ Shell::Get()->NotifySplitViewModeStarting();
+
+ if (right_window_ == left_window) {
+ StopObserving(right_window_);
+ right_window_ = nullptr;
+ }
+ if (left_window_ != left_window) {
+ StopObserving(left_window_);
+ left_window_ = left_window;
+ left_window_->AddObserver(this);
+ wm::GetWindowState(left_window_)->AddObserver(this);
+ }
+
+ State previous_state = state_;
+ switch (state_) {
+ case NO_SNAP:
+ default_snap_position_ = LEFT;
+ state_ = LEFT_SNAPPED;
+ break;
+ case LEFT_SNAPPED:
+ state_ = LEFT_SNAPPED;
+ break;
+ case RIGHT_SNAPPED:
+ case BOTH_SNAPPED:
+ state_ = BOTH_SNAPPED;
+ break;
+ }
+
+ const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
+ wm::GetWindowState(left_window_)->OnWMEvent(&event);
+ wm::ActivateWindow(left_window_);
+
+ NotifySplitViewStateChanged(previous_state, state_);
+}
+
+void SplitViewController::SetRightWindow(aura::Window* right_window) {
varkha 2017/06/30 14:29:35 nit: Those methods look quite similar, maybe avoid
xdai1 2017/07/05 20:18:22 I merged these two methods into one.
+ DCHECK(right_window);
+
+ if (state_ == NO_SNAP)
+ Shell::Get()->NotifySplitViewModeStarting();
+
+ if (left_window_ == right_window) {
+ StopObserving(left_window_);
+ left_window_ = nullptr;
+ }
+ if (right_window_ != right_window) {
+ StopObserving(right_window_);
+ right_window_ = right_window;
+ right_window_->AddObserver(this);
+ wm::GetWindowState(right_window_)->AddObserver(this);
+ }
+
+ State previous_state = state_;
+ switch (state_) {
+ case NO_SNAP:
+ default_snap_position_ = RIGHT;
+ state_ = RIGHT_SNAPPED;
+ break;
+ case RIGHT_SNAPPED:
+ state_ = RIGHT_SNAPPED;
+ break;
+ case LEFT_SNAPPED:
+ case BOTH_SNAPPED:
+ state_ = BOTH_SNAPPED;
+ break;
+ }
+
+ const wm::WMEvent event(wm::WM_EVENT_SNAP_RIGHT);
+ wm::GetWindowState(right_window_)->OnWMEvent(&event);
+ wm::ActivateWindow(right_window_);
+
+ NotifySplitViewStateChanged(previous_state, state_);
+}
+
+aura::Window* SplitViewController::GetDefaultSnappedWindow() {
+ if (default_snap_position_ == LEFT)
+ return left_window_;
+ if (default_snap_position_ == RIGHT)
+ return right_window_;
+ return nullptr;
+}
+
+gfx::Rect SplitViewController::GetSnappedWindowBoundsInParent(
+ aura::Window* window,
+ State snap_state) {
+ if (snap_state == LEFT_SNAPPED)
+ return GetLeftWindowBoundsInParent(window);
+ else if (snap_state == RIGHT_SNAPPED)
+ return GetRightWindowBoundsInParent(window);
+
+ NOTREACHED();
+ return gfx::Rect();
+}
+
+gfx::Rect SplitViewController::GetSnappedWindowBoundsInScreen(
+ aura::Window* window,
+ State snap_state) {
+ if (snap_state == LEFT_SNAPPED)
+ return GetLeftWindowBoundsInScreen(window);
+ else if (snap_state == RIGHT_SNAPPED)
+ return GetRightWindowBoundsInScreen(window);
+
+ NOTREACHED();
+ return gfx::Rect();
+}
+
+gfx::Rect SplitViewController::GetDisplayWorkAreaBoundsInParent(
+ aura::Window* window) {
+ aura::Window* root_window = window->GetRootWindow();
+ return ScreenUtil::GetDisplayWorkAreaBoundsInParent(
+ root_window->GetChildById(kShellWindowId_DefaultContainer));
+}
+
+gfx::Rect SplitViewController::GetDisplayWorkAreaBoundsInScreen(
+ aura::Window* window) {
+ gfx::Rect bounds = GetDisplayWorkAreaBoundsInParent(window);
+ ::wm::ConvertRectToScreen(window->GetRootWindow(), &bounds);
+ return bounds;
+}
+
+void SplitViewController::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void SplitViewController::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void SplitViewController::OnWindowDestroying(aura::Window* window) {
+ // If one of the snapped window gets closed, end the split view mode. The
+ // behavior might change in the future.
+ DCHECK(window == left_window_ || window == right_window_);
+ EndSplitView();
+}
+
+void SplitViewController::OnPostWindowStateTypeChange(
+ ash::wm::WindowState* window_state,
+ ash::wm::WindowStateType old_type) {
+ if (window_state->IsFullscreen() || window_state->IsMinimized() ||
+ window_state->IsMaximized()) {
+ // TODO(xdai): Decide what to do if one of the snapped windows gets
+ // minimized /maximized / full-screened. For now we end the split view mode
+ // for simplicity.
+ EndSplitView();
+ }
+}
+
+void SplitViewController::OnWindowActivated(ActivationReason reason,
+ aura::Window* gained_active,
+ aura::Window* lost_active) {
+ if (!IsSplitViewModeActive())
+ return;
+
+ // If |gained_active| was activated as a side effect of a window disposition
+ // change, do nothing. For example, when a snapped window is closed, another
+ // window will be activated before OnWindowDestroying() is called. We should
+ // not try to snap another window in this case.
+ if (reason == ActivationReason::WINDOW_DISPOSITION_CHANGED)
+ return;
+
+ // Only snap window that can be snapped but hasn't been snapped.
+ if (!gained_active || gained_active == left_window_ ||
+ gained_active == right_window_ || !CanSnap(gained_active)) {
+ return;
+ }
+
+ // Only window in MRU list can be snapped.
+ aura::Window::Windows windows =
+ Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+ if (std::find(windows.begin(), windows.end(), gained_active) == windows.end())
+ return;
+
+ // Snap the window on the non-default side of the screen if split view mode
+ // is active.
+ if (default_snap_position_ == LEFT)
+ SetRightWindow(gained_active);
+ else
+ SetLeftWindow(gained_active);
+}
+
+void SplitViewController::OnOverviewModeStarting() {
+ // If split view mode is active, reset |state_| to make it be able to select
+ // another window from overview window grid.
+ if (IsSplitViewModeActive()) {
+ State previous_state = state_;
+ if (default_snap_position_ == LEFT) {
+ StopObserving(right_window_);
+ state_ = LEFT_SNAPPED;
+ } else {
+ StopObserving(left_window_);
+ state_ = RIGHT_SNAPPED;
+ }
+ NotifySplitViewStateChanged(previous_state, state_);
+ }
+}
+
+void SplitViewController::OnOverviewModeEnded() {
+ // If split view mode is active but only has one snapped window, use the MRU
+ // window list to auto select another window to snap.
+ if (IsSplitViewModeActive() && state_ != BOTH_SNAPPED) {
+ aura::Window::Windows windows =
+ Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+ for (auto* window : windows) {
+ if (CanSnap(window) && window != GetDefaultSnappedWindow()) {
+ if (default_snap_position_ == LEFT)
+ SetRightWindow(window);
+ else
+ SetLeftWindow(window);
+ break;
+ }
+ }
+ }
+}
+
+void SplitViewController::EndSplitView() {
+ StopObserving(left_window_);
+ StopObserving(right_window_);
+ left_window_ = nullptr;
+ right_window_ = nullptr;
+ default_snap_position_ = LEFT;
+ divider_position_ = -1;
+
+ State previous_state = state_;
+ state_ = NO_SNAP;
+ NotifySplitViewStateChanged(previous_state, state_);
+
+ Shell::Get()->NotifySplitViewModeEnded();
+}
+
+void SplitViewController::StopObserving(aura::Window* window) {
+ if (window) {
+ window->RemoveObserver(this);
+ if (wm::GetWindowState(window))
+ wm::GetWindowState(window)->RemoveObserver(this);
+ }
+}
+
+void SplitViewController::NotifySplitViewStateChanged(State previous_state,
+ State state) {
+ if (previous_state == state)
+ return;
+ for (Observer& observer : observers_)
+ observer.OnSplitViewStateChanged(previous_state, state);
+}
+
+gfx::Rect SplitViewController::GetLeftWindowBoundsInParent(
+ aura::Window* window) {
+ gfx::Rect bounds = GetLeftWindowBoundsInScreen(window);
+ ::wm::ConvertRectFromScreen(window->GetRootWindow(), &bounds);
+ return bounds;
+}
+
+gfx::Rect SplitViewController::GetRightWindowBoundsInParent(
+ aura::Window* window) {
+ gfx::Rect bounds = GetRightWindowBoundsInScreen(window);
+ ::wm::ConvertRectFromScreen(window->GetRootWindow(), &bounds);
+ return bounds;
+}
+
+gfx::Rect SplitViewController::GetLeftWindowBoundsInScreen(
+ aura::Window* window) {
+ const gfx::Rect display_bounds_in_screen =
+ GetDisplayWorkAreaBoundsInScreen(window);
+ if (divider_position_ < 0) {
+ divider_position_ =
+ display_bounds_in_screen.x() + display_bounds_in_screen.width() * 0.5f;
+ }
+ return gfx::Rect(display_bounds_in_screen.x(), display_bounds_in_screen.y(),
+ divider_position_ - display_bounds_in_screen.x(),
+ display_bounds_in_screen.height());
+}
+
+gfx::Rect SplitViewController::GetRightWindowBoundsInScreen(
+ aura::Window* window) {
+ const gfx::Rect display_bounds_in_screen =
+ GetDisplayWorkAreaBoundsInScreen(window);
+ if (divider_position_ < 0) {
+ divider_position_ =
+ display_bounds_in_screen.x() + display_bounds_in_screen.width() * 0.5f;
+ }
+ return gfx::Rect(divider_position_, display_bounds_in_screen.y(),
+ display_bounds_in_screen.x() +
+ display_bounds_in_screen.width() - divider_position_,
+ display_bounds_in_screen.height());
+}
+
+} // namespace ash

Powered by Google App Engine
This is Rietveld 408576698