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

Unified Diff: ui/app_list/views/app_list_view.cc

Issue 2898743002: Draggable peeking/fullscreen launcher with transparent background. (Closed)
Patch Set: addressed msw@'s comments. 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: ui/app_list/views/app_list_view.cc
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 060e7d718b0806e9c333653e0f6d5274b17a4e10..a7ae6737f28b5753c4235c8ad4f8782c3bc2a2a6 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -53,10 +53,27 @@ namespace app_list {
namespace {
// The margin from the edge to the speech UI.
-const int kSpeechUIMargin = 12;
+constexpr int kSpeechUIMargin = 12;
+
+// The height/width of the shelf.
+constexpr int kShelfSize = 48;
+
+// The height of the peeking app list.
+constexpr int kPeekingAppListHeight = 320;
+
+// The fraction of app list height that the app list must be released at in
+// order to transition to the next state.
+constexpr int kAppListThresholdDenominator = 3;
+
+// The velocity the app list must be dragged in order to transition to the next
+// state, measured in DIPs/event.
+constexpr int kAppListDragVelocityThreshold = 25;
+
+// The opacity of the app list background.
+constexpr float kAppListOpacity = 0.8;
// The vertical position for the appearing animation of the speech UI.
-const float kSpeechUIAppearingPosition = 12;
+constexpr float kSpeechUIAppearingPosition = 12;
// This view forwards the focus to the search box widget by providing it as a
// FocusTraversable when a focus search is provided.
@@ -174,11 +191,17 @@ AppListView::AppListView(AppListViewDelegate* delegate)
search_box_focus_host_(nullptr),
search_box_widget_(nullptr),
search_box_view_(nullptr),
+ display_observer_(this),
overlay_view_(nullptr),
animation_observer_(new HideViewAnimationObserver()) {
CHECK(delegate);
delegate_->GetSpeechUI()->AddObserver(this);
+
+ if (features::IsFullscreenAppListEnabled()) {
+ display_observer_.Add(display::Screen::GetScreen());
+ is_fullscreen_app_list_enabled_ = true;
+ }
}
AppListView::~AppListView() {
@@ -195,15 +218,20 @@ void AppListView::Initialize(gfx::NativeView parent, int initial_apps_page) {
set_color(kContentsBackgroundColor);
set_parent_window(parent);
- if (features::IsFullscreenAppListEnabled())
+ if (is_fullscreen_app_list_enabled_)
InitializeFullscreen(parent, initial_apps_page);
else
InitializeBubble(parent, initial_apps_page);
InitChildWidgets();
AddChildView(overlay_view_);
+
+ if (is_fullscreen_app_list_enabled_)
+ ChangeState(PEEKING);
+
if (delegate_)
delegate_->ViewInitialized();
+
UMA_HISTOGRAM_TIMES("Apps.AppListCreationTime",
base::Time::Now() - start_time);
}
@@ -216,7 +244,7 @@ void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
void AppListView::MaybeSetAnchorPoint(const gfx::Point& anchor_point) {
// if the AppListView is a bubble
- if (!features::IsFullscreenAppListEnabled())
+ if (!is_fullscreen_app_list_enabled_)
SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
}
@@ -236,7 +264,7 @@ void AppListView::CloseAppList() {
void AppListView::UpdateBounds() {
// if the AppListView is a bubble
- if (!features::IsFullscreenAppListEnabled())
+ if (!is_fullscreen_app_list_enabled_)
SizeToContents();
}
@@ -329,14 +357,23 @@ void AppListView::InitContents(gfx::NativeView parent, int initial_apps_page) {
FROM_HERE_WITH_EXPLICIT_FUNCTION(
"440224, 441028 AppListView::InitContents"));
- app_list_main_view_ = new AppListMainView(delegate_);
+ if (is_fullscreen_app_list_enabled_) {
+ // The shield view that colors the background of the app list and makes it
+ // transparent.
+ app_list_background_shield_ = new views::View;
+ app_list_background_shield_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
+ app_list_background_shield_->layer()->SetColor(SK_ColorBLACK);
+ app_list_background_shield_->layer()->SetOpacity(kAppListOpacity);
+ AddChildView(app_list_background_shield_);
+ }
+ app_list_main_view_ = new AppListMainView(delegate_, this);
AddChildView(app_list_main_view_);
app_list_main_view_->SetPaintToLayer();
app_list_main_view_->layer()->SetFillsBoundsOpaquely(false);
app_list_main_view_->layer()->SetMasksToBounds(true);
// This will be added to the |search_box_widget_| after the app list widget is
// initialized.
- search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_);
+ search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_, this);
search_box_view_->SetPaintToLayer();
search_box_view_->layer()->SetFillsBoundsOpaquely(false);
search_box_view_->layer()->SetMasksToBounds(true);
@@ -403,20 +440,32 @@ void AppListView::InitChildWidgets() {
void AppListView::InitializeFullscreen(gfx::NativeView parent,
int initial_apps_page) {
- views::Widget* widget = new views::Widget;
+ app_list_state_ = INITIAL;
+ gfx::Rect display_work_area_bounds =
+ display::Screen::GetScreen()
+ ->GetDisplayNearestView(parent_window())
+ .work_area();
+
+ gfx::Rect app_list_overlay_view_bounds(
+ display_work_area_bounds.x(),
+ display_work_area_bounds.height() + kShelfSize - kPeekingAppListHeight,
+ display_work_area_bounds.width(),
+ display_work_area_bounds.height() + kShelfSize);
+
+ fullscreen_widget_ = new views::Widget;
views::Widget::InitParams app_list_overlay_view_params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ app_list_overlay_view_params.name = "AppList";
app_list_overlay_view_params.parent = parent;
app_list_overlay_view_params.delegate = this;
app_list_overlay_view_params.opacity =
views::Widget::InitParams::TRANSLUCENT_WINDOW;
- app_list_overlay_view_params.bounds =
- display::Screen::GetScreen()->
- GetDisplayNearestView(parent).work_area();
- widget->Init(app_list_overlay_view_params);
- widget->GetLayer()->SetBackgroundBlur(10);
+ app_list_overlay_view_params.bounds = app_list_overlay_view_bounds;
+ app_list_overlay_view_params.layer_type = ui::LAYER_SOLID_COLOR;
+ fullscreen_widget_->Init(app_list_overlay_view_params);
+ fullscreen_widget_bounds_ = fullscreen_widget_->GetWindowBoundsInScreen();
overlay_view_ = new AppListOverlayView(0 /* no corners */);
}
@@ -426,8 +475,8 @@ void AppListView::InitializeBubble(gfx::NativeView parent,
set_close_on_deactivate(false);
set_shadow(views::BubbleBorder::NO_ASSETS);
- // This creates the app list widget. (Before this, child widgets cannot be
- // created.)
+ // This creates the app list widget (Before this, child widgets cannot be
+ // created).
views::BubbleDialogDelegateView::CreateBubble(this);
SetBubbleArrow(views::BubbleBorder::FLOAT);
@@ -442,6 +491,88 @@ void AppListView::InitializeBubble(gfx::NativeView parent,
overlay_view_->SetBoundsRect(GetContentsBounds());
}
+void AppListView::StartDrag(const gfx::Point& location) {
+ initial_drag_point_ = location;
+ fullscreen_widget_bounds_ = fullscreen_widget_->GetWindowBoundsInScreen();
+}
+
+void AppListView::UpdateDrag(const gfx::Point& location) {
+ // Update the bounds of the widget while maintaining the
+ // relative position of the top of the widget and the mouse/gesture.
+ // Block drags north of 0 and recalculate the initial_drag_point_.
+ int const new_y_position = location.y() - initial_drag_point_.y() +
+ fullscreen_widget_->GetWindowBoundsInScreen().y();
+ gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
+ if (new_y_position < 0) {
+ new_widget_bounds.set_y(0);
+ initial_drag_point_ = location;
+ } else {
+ new_widget_bounds.set_y(new_y_position);
+ }
+ fullscreen_widget_->SetBounds(new_widget_bounds);
+}
+
+void AppListView::EndDrag(const gfx::Point& location) {
+ // Change the app list state based on where the drag ended. If fling velocity
+ // was over the threshold, snap to the next state in the direction of the
+ // fling.
+ int const new_y_position = location.y() - initial_drag_point_.y() +
+ fullscreen_widget_->GetWindowBoundsInScreen().y();
+ if (std::abs(last_fling_velocity_) > kAppListDragVelocityThreshold) {
+ // If the user releases drag with velocity over the threshold, snap to
+ // the next state, ignoring the drag release position.
+ if (app_list_state_ == FULLSCREEN) {
+ if (last_fling_velocity_ > 0) {
+ ChangeState(PEEKING);
+ }
+ } else {
+ if (last_fling_velocity_ > 0) {
+ CloseAppList();
+ } else if (last_fling_velocity_ < 0) {
+ ChangeState(FULLSCREEN);
+ }
+ }
+ last_fling_velocity_ = 0;
+ } else {
+ int display_height = display::Screen::GetScreen()
+ ->GetDisplayNearestView(parent_window())
+ .work_area()
+ .height();
+ int default_peeking_y = display_height + kShelfSize - kPeekingAppListHeight;
+ // The drag release velocity was too low, so use the release point.
+ int app_list_snap_y =
+ (app_list_state_ == FULLSCREEN) ? 0 : default_peeking_y;
+ // The DIP delta that must be exceeded for the app list to snap to the next
+ // state.
+ int app_list_threshold =
+ (fullscreen_widget_->GetWindowBoundsInScreen().height() + kShelfSize) /
+ kAppListThresholdDenominator;
+ app_list_threshold -=
+ ((app_list_state_ == FULLSCREEN) ? 0 : kPeekingAppListHeight) /
+ kAppListThresholdDenominator;
+
+ // If the user releases +/- 1/3 of |app_list_threshold|, snap to the
+ // next state.
+ if (std::abs(app_list_snap_y - new_y_position) < app_list_threshold) {
+ // The drag was not far enough so set the app list bounds to the target
+ // bounds for the current state.
+ ChangeState(app_list_state_);
+ } else if ((app_list_snap_y + app_list_threshold) < new_y_position) {
+ // The drag was far enough to change states and was a downward drag, so
+ // set the app list bounds to the next state.
+ if (app_list_state_ == FULLSCREEN) {
+ ChangeState(PEEKING);
+ } else {
+ CloseAppList();
+ }
+ } else {
+ // The drag was far enough to change states and was an upward drag, so
+ // set the app list bounds to the next state.
+ ChangeState(FULLSCREEN);
+ }
+ }
+}
+
void AppListView::OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
views::Widget* widget) const {
if (!params->native_widget) {
@@ -475,6 +606,51 @@ void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
mask->addRect(gfx::RectToSkRect(GetBubbleFrameView()->GetContentsBounds()));
}
+void AppListView::OnMouseEvent(ui::MouseEvent* event) {
msw 2017/06/07 02:31:38 Should OnMouseEvent and OnGestureEvent invoke the
newcomer 2017/06/07 17:21:37 Handled offline.
msw 2017/06/07 17:44:05 For potentially interested readers, the offline di
newcomer 2017/06/07 19:01:41 Acknowledged.
+ if (!is_fullscreen_app_list_enabled_)
+ return;
+
+ switch (event->type()) {
+ case ui::ET_MOUSE_PRESSED:
+ StartDrag(event->location());
+ event->SetHandled();
+ break;
+ case ui::ET_MOUSE_DRAGGED:
+ UpdateDrag(event->location());
+ event->SetHandled();
+ break;
+ case ui::ET_MOUSE_RELEASED:
+ EndDrag(event->location());
+ event->SetHandled();
+ break;
+ default:
+ break;
+ }
+}
+
+void AppListView::OnGestureEvent(ui::GestureEvent* event) {
+ if (!is_fullscreen_app_list_enabled_)
+ return;
+
+ switch (event->type()) {
+ case ui::ET_GESTURE_SCROLL_BEGIN:
+ StartDrag(event->location());
+ event->SetHandled();
+ break;
+ case ui::ET_GESTURE_SCROLL_UPDATE:
+ last_fling_velocity_ = event->details().velocity_y();
+ UpdateDrag(event->location());
+ event->SetHandled();
+ break;
+ case ui::ET_GESTURE_END:
+ EndDrag(event->location());
+ event->SetHandled();
+ break;
+ default:
+ break;
+ }
+}
+
bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
@@ -510,6 +686,11 @@ void AppListView::Layout() {
speech_bounds.Inset(-speech_view_->GetInsets());
speech_view_->SetBoundsRect(speech_bounds);
}
+
+ if (is_fullscreen_app_list_enabled_) {
+ app_list_main_view_->contents_view()->Layout();
+ app_list_background_shield_->SetBoundsRect(contents_bounds);
+ }
}
void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
@@ -518,6 +699,52 @@ void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
GetBubbleFrameView()->SchedulePaint();
}
+void AppListView::ToFullscreen() {
+ gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
+ new_widget_bounds.set_y(0);
+ fullscreen_widget_->SetBounds(new_widget_bounds);
+ app_list_state_ = FULLSCREEN;
+}
+
+void AppListView::ToPeeking() {
+ int display_height = display::Screen::GetScreen()
+ ->GetDisplayNearestView(parent_window())
+ .work_area()
+ .height();
+ int default_peeking_y = display_height + kShelfSize - kPeekingAppListHeight;
+ gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
+
+ new_widget_bounds.set_y(default_peeking_y);
+ fullscreen_widget_->SetBounds(new_widget_bounds);
+ app_list_state_ = PEEKING;
+}
+
+void AppListView::ChangeState(AppListState new_state) {
+ gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
+ int display_height = display::Screen::GetScreen()
msw 2017/06/07 02:31:38 nit: |display_height| and |default_peeking_y| are
newcomer 2017/06/07 17:21:37 Done.
+ ->GetDisplayNearestView(parent_window())
+ .work_area()
+ .height();
+ int default_peeking_y = display_height + kShelfSize - kPeekingAppListHeight;
+ switch (new_state) {
+ case PEEKING:
+ new_widget_bounds.set_y(default_peeking_y);
+ break;
+ case FULLSCREEN:
+ new_widget_bounds.set_y(0);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ fullscreen_widget_->SetBounds(new_widget_bounds);
+ app_list_state_ = new_state;
+}
+
+bool AppListView::IsFullscreen() const {
+ return app_list_state_ == FULLSCREEN;
+}
+
void AppListView::OnWidgetDestroying(views::Widget* widget) {
BubbleDialogDelegateView::OnWidgetDestroying(widget);
if (delegate_ && widget == GetWidget())
@@ -602,4 +829,32 @@ void AppListView::OnSpeechRecognitionStateChanged(
}
}
+void AppListView::OnDisplayMetricsChanged(const display::Display& display,
+ uint32_t changed_metrics) {
+ if (!is_fullscreen_app_list_enabled_)
+ return;
+
+ gfx::Rect display_work_area_bounds =
+ display::Screen::GetScreen()
+ ->GetDisplayNearestView(parent_window())
+ .work_area();
+
+ gfx::Rect recomputed_fullscreen_widget_bounds(
msw 2017/06/07 02:31:38 Remove this, it's no longer used.
newcomer 2017/06/07 17:21:37 Done.
+ display_work_area_bounds.x(),
+ display_work_area_bounds.height() + kShelfSize - kPeekingAppListHeight,
+ display_work_area_bounds.width(),
+ display_work_area_bounds.height() + kShelfSize);
+
+ fullscreen_widget_->SetBounds(gfx::Rect(
+ display_work_area_bounds.x(),
+ display_work_area_bounds.height() + kShelfSize - kPeekingAppListHeight,
msw 2017/06/07 02:31:38 nit: you should probably replace display_work_area
newcomer 2017/06/07 17:21:37 Actually this value is meaningless because y gets
+ display_work_area_bounds.width(),
+ display_work_area_bounds.height() + kShelfSize));
+
+ if (app_list_state_ == FULLSCREEN)
msw 2017/06/07 02:31:38 nit: add a comment like "Update the window's bound
newcomer 2017/06/07 17:21:37 Done.
+ ChangeState(FULLSCREEN);
+ else
+ ChangeState(PEEKING);
+}
+
} // namespace app_list

Powered by Google App Engine
This is Rietveld 408576698