Chromium Code Reviews| 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 |