Skip to content

Commit

Permalink
Refactor the home card structure and introduce animation.
Browse files Browse the repository at this point in the history
This CL includes the following refactorings:
- minimized home is simply a layer instead of views
- moves the mouse click handler of minimized home to home card impl
- home card now has the same size of the screen. Changing state is
  now simply changing its position.

Then schedule the animation between BOTTOM state and CENTERED state.

BUG=398062
R=sadrul@chromium.org, oshima@chromium.org
TEST=manually on remote desktop

Review URL: https://codereview.chromium.org/478293004

Cr-Commit-Position: refs/heads/master@{#292092}
  • Loading branch information
jmuk authored and Commit bot committed Aug 27, 2014
1 parent bf20fc6 commit 30e0231
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 195 deletions.
177 changes: 115 additions & 62 deletions athena/home/athena_start_page_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,13 @@ class SearchBoxContainer : public views::View {
new views::RoundRectPainter(SK_ColorGRAY, kSearchBoxCornerRadius),
gfx::Insets(kSearchBoxBorderWidth, kSearchBoxBorderWidth,
kSearchBoxBorderWidth, kSearchBoxBorderWidth)));
SetLayoutManager(new views::FillLayout());
AddChildView(search_box_);
}
virtual ~SearchBoxContainer() {}

private:
// views::View:
virtual void Layout() OVERRIDE {
gfx::Rect search_box_bounds = GetContentsBounds();
search_box_bounds.ClampToCenteredSize(GetPreferredSize());
search_box_->SetBoundsRect(search_box_bounds);
}
virtual gfx::Size GetPreferredSize() const OVERRIDE {
return gfx::Size(kSearchBoxWidth, kSearchBoxHeight);
}
Expand All @@ -152,13 +148,30 @@ class SearchBoxContainer : public views::View {

namespace athena {

// static
const char AthenaStartPageView::kViewClassName[] = "AthenaStartPageView";

AthenaStartPageView::LayoutData::LayoutData()
: logo_opacity(1.0f),
background_opacity(1.0f) {
}

AthenaStartPageView::AthenaStartPageView(
app_list::AppListViewDelegate* view_delegate)
: delegate_(view_delegate),
layout_state_(0.0f),
weak_factory_(this) {
background_ = new views::View();
background_->set_background(
views::Background::CreateSolidBackground(SK_ColorWHITE));
background_->SetPaintToLayer(true);
background_->SetFillsBoundsOpaquely(false);
AddChildView(background_);

logo_ = view_delegate->CreateStartPageWebView(
gfx::Size(kWebViewWidth, kWebViewHeight));
logo_->SetPaintToLayer(true);
logo_->SetSize(logo_->GetPreferredSize());
AddChildView(logo_);

search_results_view_ = new app_list::SearchResultListView(
Expand All @@ -183,13 +196,7 @@ AthenaStartPageView::AthenaStartPageView(
view_delegate->GetModel()->top_level_item_list();
for (size_t i = 0; i < std::min(top_level->item_count(), kMaxIconNum); ++i)
app_icon_container_->AddChildView(new AppIconButton(top_level->item_at(i)));

search_box_view_ = new app_list::SearchBoxView(this, view_delegate);
search_box_view_->set_contents_view(this);
search_box_container_ = new SearchBoxContainer(search_box_view_);
search_box_container_->SetPaintToLayer(true);
search_box_container_->SetFillsBoundsOpaquely(false);
AddChildView(search_box_container_);
app_icon_container_->SetSize(app_icon_container_->GetPreferredSize());

control_icon_container_ = new views::View();
control_icon_container_->SetPaintToLayer(true);
Expand All @@ -199,6 +206,15 @@ AthenaStartPageView::AthenaStartPageView(
views::BoxLayout::kHorizontal, 0, 0, kIconMargin));
for (size_t i = 0; i < kMaxIconNum; ++i)
control_icon_container_->AddChildView(new PlaceHolderButton());
control_icon_container_->SetSize(control_icon_container_->GetPreferredSize());

search_box_view_ = new app_list::SearchBoxView(this, view_delegate);
search_box_view_->set_contents_view(this);
search_box_container_ = new SearchBoxContainer(search_box_view_);
search_box_container_->SetPaintToLayer(true);
search_box_container_->SetFillsBoundsOpaquely(false);
search_box_container_->SetSize(search_box_container_->GetPreferredSize());
AddChildView(search_box_container_);
}

AthenaStartPageView::~AthenaStartPageView() {}
Expand All @@ -207,6 +223,69 @@ void AthenaStartPageView::RequestFocusOnSearchBox() {
search_box_view_->search_box()->RequestFocus();
}

void AthenaStartPageView::SetLayoutState(float layout_state) {
layout_state_ = layout_state;
Layout();
}

void AthenaStartPageView::SetLayoutStateWithAnimation(float layout_state) {
ui::ScopedLayerAnimationSettings logo(logo_->layer()->GetAnimator());
ui::ScopedLayerAnimationSettings search_box(
search_box_container_->layer()->GetAnimator());
ui::ScopedLayerAnimationSettings icons(
app_icon_container_->layer()->GetAnimator());
ui::ScopedLayerAnimationSettings controls(
control_icon_container_->layer()->GetAnimator());

logo.SetTweenType(gfx::Tween::EASE_IN_OUT);
search_box.SetTweenType(gfx::Tween::EASE_IN_OUT);
icons.SetTweenType(gfx::Tween::EASE_IN_OUT);
controls.SetTweenType(gfx::Tween::EASE_IN_OUT);

SetLayoutState(layout_state);
}

AthenaStartPageView::LayoutData AthenaStartPageView::CreateBottomBounds(
int width) {
LayoutData state;
state.icons.set_size(app_icon_container_->size());
state.icons.set_x(kIconMargin);
state.icons.set_y(kIconMargin);

state.controls.set_size(control_icon_container_->size());
state.controls.set_x(width - kIconMargin - state.controls.width());
state.controls.set_y(kIconMargin);

state.search_box.set_size(search_box_container_->size());
state.search_box.set_x((width - state.search_box.width()) / 2);
state.search_box.set_y((kHomeCardHeight - state.search_box.height()) / 2);

state.logo_opacity = 0.0f;
state.background_opacity = 0.9f;
return state;
}

AthenaStartPageView::LayoutData AthenaStartPageView::CreateCenteredBounds(
int width) {
LayoutData state;

state.search_box.set_size(search_box_container_->size());
state.search_box.set_x((width - state.search_box.width()) / 2);
state.search_box.set_y(logo_->bounds().bottom() + kInstantContainerSpacing);

state.icons.set_size(app_icon_container_->size());
state.icons.set_x(width / 2 - state.icons.width() - kIconMargin / 2);
state.icons.set_y(state.search_box.bottom() + kInstantContainerSpacing);

state.controls.set_size(control_icon_container_->size());
state.controls.set_x(width / 2 + kIconMargin / 2 + kIconMargin % 2);
state.controls.set_y(state.icons.y());

state.logo_opacity = 1.0f;
state.background_opacity = 1.0f;
return state;
}

void AthenaStartPageView::LayoutSearchResults(bool should_show_search_results) {
if (should_show_search_results ==
search_results_view_->layer()->GetTargetVisibility()) {
Expand Down Expand Up @@ -276,57 +355,31 @@ void AthenaStartPageView::OnSearchResultLayoutAnimationCompleted(
}

void AthenaStartPageView::Layout() {
gfx::Rect bounds = GetContentsBounds();
search_results_view_->SetVisible(false);

if (bounds.height() <= kHomeCardHeight) {
logo_->SetVisible(false);
gfx::Rect icon_bounds(app_icon_container_->GetPreferredSize());
icon_bounds.set_x(bounds.x() + kIconMargin);
icon_bounds.set_y(bounds.x() + kIconMargin);
app_icon_container_->SetBoundsRect(icon_bounds);

gfx::Rect control_bounds(control_icon_container_->GetPreferredSize());
control_bounds.set_x(
bounds.right() - kIconMargin - control_bounds.width());
control_bounds.set_y(bounds.y() + kIconMargin);
control_icon_container_->SetBoundsRect(control_bounds);

search_box_container_->SetBounds(
icon_bounds.right(), bounds.y(),
control_bounds.x() - icon_bounds.right(), kHomeCardHeight);

set_background(views::Background::CreateSolidBackground(
255, 255, 255, 255 * 0.9));
} else {
// TODO(mukai): set the intermediate state.
logo_->SetVisible(true);
logo_->layer()->SetOpacity(1.0f);
set_background(views::Background::CreateSolidBackground(SK_ColorWHITE));
gfx::Rect logo_bounds(bounds.x() + bounds.width() / 2 - kWebViewWidth / 2,
bounds.y() + kTopMargin,
kWebViewWidth,
kWebViewHeight);
logo_->SetBoundsRect(logo_bounds);

gfx::Rect search_box_bounds(search_box_container_->GetPreferredSize());
search_box_bounds.set_x(
bounds.x() + bounds.width() / 2 - search_box_bounds.width() / 2);
search_box_bounds.set_y(logo_bounds.bottom() + kInstantContainerSpacing);
search_box_container_->SetBoundsRect(search_box_bounds);

gfx::Rect icon_bounds(app_icon_container_->GetPreferredSize());
icon_bounds.set_x(bounds.x() + bounds.width() / 2 -
icon_bounds.width() - kIconMargin / 2);
icon_bounds.set_y(search_box_bounds.bottom() + kInstantContainerSpacing);
app_icon_container_->SetBoundsRect(icon_bounds);

gfx::Rect control_bounds(control_icon_container_->GetPreferredSize());
control_bounds.set_x(bounds.x() + bounds.width() / 2 +
kIconMargin / 2 + kIconMargin % 2);
control_bounds.set_y(icon_bounds.y());
control_icon_container_->SetBoundsRect(control_bounds);
}
gfx::Rect logo_bounds(x() + width() / 2 - kWebViewWidth / 2, y() + kTopMargin,
kWebViewWidth, kWebViewHeight);
logo_->SetBoundsRect(logo_bounds);

LayoutData bottom_bounds = CreateBottomBounds(width());
LayoutData centered_bounds = CreateCenteredBounds(width());

logo_->layer()->SetOpacity(gfx::Tween::FloatValueBetween(
gfx::Tween::CalculateValue(gfx::Tween::EASE_IN_2, layout_state_),
bottom_bounds.logo_opacity, centered_bounds.logo_opacity));
logo_->SetVisible(logo_->layer()->GetTargetOpacity() != 0.0f);

app_icon_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
layout_state_, bottom_bounds.icons, centered_bounds.icons));
control_icon_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
layout_state_, bottom_bounds.controls, centered_bounds.controls));
search_box_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
layout_state_, bottom_bounds.search_box, centered_bounds.search_box));

background_->SetBoundsRect(bounds());
background_->layer()->SetOpacity(gfx::Tween::FloatValueBetween(
layout_state_,
bottom_bounds.background_opacity,
centered_bounds.background_opacity));
}

bool AthenaStartPageView::OnKeyPressed(const ui::KeyEvent& key_event) {
Expand Down
36 changes: 34 additions & 2 deletions athena/home/athena_start_page_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ class SearchResultListView;

namespace athena {

// It will replace app_list::StartPageView in Athena UI in the future.
// Right now it's simply used for VISIBLE_BOTTOM state.
class AthenaStartPageView : public views::View,
public app_list::SearchBoxViewDelegate {
public:
Expand All @@ -28,7 +26,33 @@ class AthenaStartPageView : public views::View,
// Requests the focus on the search box in the start page view.
void RequestFocusOnSearchBox();

// Updates the layout state. See the comment of |layout_state_| field.
void SetLayoutState(float layout_state);

// Updates the layout state and move the subviews to the target location with
// animation.
void SetLayoutStateWithAnimation(float layout_state);

private:
static const char kViewClassName[];

// A struct which bundles the layout data of subviews.
struct LayoutData {
gfx::Rect search_box;
gfx::Rect icons;
gfx::Rect controls;
float logo_opacity;
float background_opacity;

LayoutData();
};

// Returns the bounds for |VISIBLE_BOTTOM|.
LayoutData CreateBottomBounds(int width);

// Returns the bounds for |VISIBLE_CENTERED|.
LayoutData CreateCenteredBounds(int width);

// Schedules the animation for the layout the search box and the search
// results.
void LayoutSearchResults(bool should_show_search_results);
Expand All @@ -55,9 +79,17 @@ class AthenaStartPageView : public views::View,
app_list::SearchBoxView* search_box_view_;
app_list::SearchResultListView* search_results_view_;

// Do not use views::Background but a views::View with ui::Layer for gradient
// background opacity update and animation.
views::View* background_;

// The expected height of |search_results_view_|
int search_results_height_;

// The state to specify how each of the subviews should be laid out, in the
// range of [0, 1]. 0 means fully BOTTOM state, and 1 is fully CENTERED state.
float layout_state_;

base::WeakPtrFactory<AthenaStartPageView> weak_factory_;

DISALLOW_COPY_AND_ASSIGN(AthenaStartPageView);
Expand Down
1 change: 1 addition & 0 deletions athena/home/home_card_gesture_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ void HomeCardGestureManager::UpdateScrollState(const ui::GestureEvent& event) {
// The finger is between two states.
float progress = (last_estimated_height_ - smaller_height) /
(bigger_height - smaller_height);
progress = std::min(1.0f, std::max(0.0f, progress));

if (last_state_ == state) {
if (event.details().scroll_y() > 0) {
Expand Down
Loading

0 comments on commit 30e0231

Please sign in to comment.