Home | History | Annotate | Download | only in panels
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/chromeos/panels/panel_scroller.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/logging.h"
      9 #include "base/stl_util-inl.h"
     10 #include "base/string_util.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/chromeos/panels/panel_scroller_container.h"
     13 #include "chrome/browser/chromeos/panels/panel_scroller_header.h"
     14 #include "ui/gfx/canvas.h"
     15 #include "views/widget/widget.h"
     16 
     17 struct PanelScroller::Panel {
     18   PanelScrollerHeader* header;
     19   PanelScrollerContainer* container;
     20 };
     21 
     22 PanelScroller::PanelScroller()
     23     : views::View(),
     24       divider_height_(18),
     25       needs_layout_(true),
     26       scroll_pos_(0),
     27       ALLOW_THIS_IN_INITIALIZER_LIST(animation_(this)),
     28       animated_scroll_begin_(0),
     29       animated_scroll_end_(0) {
     30   animation_.SetTweenType(ui::Tween::EASE_IN_OUT);
     31   animation_.SetSlideDuration(300);
     32 
     33   Panel* panel = new Panel;
     34   panel->header = new PanelScrollerHeader(this);
     35   panel->header->set_title(ASCIIToUTF16("Email"));
     36   panel->container = new PanelScrollerContainer(this, new views::View());
     37   panels_.push_back(panel);
     38 
     39   panel = new Panel;
     40   panel->header = new PanelScrollerHeader(this);
     41   panel->header->set_title(ASCIIToUTF16("Chat"));
     42   panel->container = new PanelScrollerContainer(this, new views::View());
     43   panels_.push_back(panel);
     44 
     45   panel = new Panel;
     46   panel->header = new PanelScrollerHeader(this);
     47   panel->header->set_title(ASCIIToUTF16("Calendar"));
     48   panel->container = new PanelScrollerContainer(this, new views::View());
     49   panels_.push_back(panel);
     50 
     51   panel = new Panel;
     52   panel->header = new PanelScrollerHeader(this);
     53   panel->header->set_title(ASCIIToUTF16("Recent searches"));
     54   panel->container = new PanelScrollerContainer(this, new views::View());
     55   panels_.push_back(panel);
     56 
     57   panel = new Panel;
     58   panel->header = new PanelScrollerHeader(this);
     59   panel->header->set_title(ASCIIToUTF16("Pony news"));
     60   panel->container = new PanelScrollerContainer(this, new views::View());
     61   panels_.push_back(panel);
     62 
     63   // Add the containers first since they're on the bottom.
     64   AddChildView(panels_[0]->container);
     65   AddChildView(panels_[1]->container);
     66   AddChildView(panels_[2]->container);
     67   AddChildView(panels_[3]->container);
     68   AddChildView(panels_[4]->container);
     69 
     70   AddChildView(panels_[0]->header);
     71   AddChildView(panels_[1]->header);
     72   AddChildView(panels_[2]->header);
     73   AddChildView(panels_[3]->header);
     74   AddChildView(panels_[4]->header);
     75 }
     76 
     77 PanelScroller::~PanelScroller() {
     78   STLDeleteContainerPointers(panels_.begin(), panels_.end());
     79 }
     80 
     81 // static
     82 PanelScroller* PanelScroller::CreateWindow() {
     83   views::Widget* widget = views::Widget::CreateWidget(
     84       views::Widget::CreateParams(views::Widget::CreateParams::TYPE_WINDOW));
     85   widget->Init(NULL, gfx::Rect(0, 0, 100, 800));
     86 
     87   PanelScroller* scroller = new PanelScroller();
     88   widget->SetContentsView(scroller);
     89 
     90   widget->Show();
     91 
     92   return scroller;
     93 }
     94 
     95 void PanelScroller::ViewHierarchyChanged(bool is_add,
     96                                          views::View* parent,
     97                                          views::View* child) {
     98   // Our child views changed without us knowing it. Stop the animation and mark
     99   // us as dirty (needs_layout_ = true).
    100   animation_.Stop();
    101   needs_layout_ = true;
    102 }
    103 
    104 gfx::Size PanelScroller::GetPreferredSize() {
    105   return gfx::Size(75, 200);
    106 }
    107 
    108 void PanelScroller::Layout() {
    109 /* TODO(brettw) this doesn't work for some reason.
    110   if (!needs_layout_ || !animation_.IsShowing())
    111     return;
    112   needs_layout_ = false;*/
    113 
    114   // The current location in the content that we're laying out. This is before
    115   // scrolling is accounted for.
    116   int cur_content_pos = 0;
    117 
    118   // Number of pixels used by headers stuck to the top of the scroll area.
    119   int top_header_pixel_count = 0;
    120 
    121   int panel_count = static_cast<int>(panels_.size());
    122   for (int i = 0; i < panel_count; i++) {
    123     if (cur_content_pos < scroll_pos_ + top_header_pixel_count) {
    124       // This panel is at least partially off the top. Put the header below the
    125       // others already there.
    126       panels_[i]->header->SetBoundsRect(gfx::Rect(0, top_header_pixel_count,
    127                                                   width(), divider_height_));
    128       top_header_pixel_count += divider_height_;
    129 
    130     } else if (cur_content_pos > height() + scroll_pos_ -
    131                (panel_count - i) * divider_height_) {
    132       // When we've hit the bottom of the visible content, all the remaining
    133       // headers will stack up at the bottom. Counting this header, there are
    134       // (size() - i) left, which is used in the expression above.
    135       int top = height() - (panel_count - i) * divider_height_;
    136       panels_[i]->header->SetBoundsRect(gfx::Rect(0, top,
    137                                                   width(), divider_height_));
    138     } else {
    139       // Normal header positioning in-flow.
    140       panels_[i]->header->SetBoundsRect(
    141           gfx::Rect(0, cur_content_pos - scroll_pos_, width(),
    142                     divider_height_));
    143     }
    144 
    145     cur_content_pos += divider_height_;
    146 
    147     // Now position the content. It always goes in-flow ignoring any stacked
    148     // up headers at the top or bottom.
    149     int container_height = panels_[i]->container->GetPreferredSize().height();
    150     panels_[i]->container->SetBoundsRect(
    151         gfx::Rect(0, cur_content_pos - scroll_pos_,
    152                   width(), container_height));
    153     cur_content_pos += container_height;
    154   }
    155 }
    156 
    157 bool PanelScroller::OnMousePressed(const views::MouseEvent& event) {
    158   return true;
    159 }
    160 
    161 bool PanelScroller::OnMouseDragged(const views::MouseEvent& event) {
    162   return true;
    163 }
    164 
    165 void PanelScroller::HeaderClicked(PanelScrollerHeader* source) {
    166   for (size_t i = 0; i < panels_.size(); i++) {
    167     if (panels_[i]->header == source) {
    168       ScrollToPanel(static_cast<int>(i));
    169       return;
    170     }
    171   }
    172   NOTREACHED() << "Invalid panel passed to HeaderClicked.";
    173 }
    174 
    175 void PanelScroller::ScrollToPanel(int index) {
    176   int affected_panel_height =
    177       panels_[index]->container->GetPreferredSize().height();
    178 
    179   // The pixel size we need to reserve for the stuck headers.
    180   int top_stuck_header_pixel_size = index * divider_height_;
    181   int bottom_stuck_header_pixel_size =
    182       (static_cast<int>(panels_.size()) - index - 1) * divider_height_;
    183 
    184   // Compute the offset of the top of the panel to scroll to.
    185   int space_above = 0;
    186   for (int i = 0; i < index; i++) {
    187     space_above += divider_height_ +
    188         panels_[i]->container->GetPreferredSize().height();
    189   }
    190 
    191   // Compute the space below the top of the panel.
    192   int space_below = 0;
    193   for (int i = index; i < static_cast<int>(panels_.size()); i++) {
    194     space_below += divider_height_ +
    195         panels_[i]->container->GetPreferredSize().height();
    196   }
    197 
    198   // The scroll position of the top of the stuck headers is the space above
    199   // minus the size of the headers stuck there.
    200   int top_stuck_headers_scroll_pos = space_above - top_stuck_header_pixel_size;
    201 
    202   // If the panel is already fully visible, do nothing.
    203   if (scroll_pos_ <= top_stuck_headers_scroll_pos &&
    204       space_above + divider_height_ + affected_panel_height -
    205       bottom_stuck_header_pixel_size <= scroll_pos_ + height())
    206     return;
    207 
    208   // Compute the scroll position.
    209   if (height() > space_below) {
    210     // There's enough room for this panel and everything below it to fit on the
    211     // screen.
    212     animated_scroll_end_ = (space_above + space_below) - height();
    213     if (animated_scroll_end_ > top_stuck_headers_scroll_pos)
    214       animated_scroll_end_ = top_stuck_headers_scroll_pos;
    215   } else if (space_above > scroll_pos_) {
    216     // If we're going to be scrolling the content up, scroll just until the
    217     // panel in question is fully visible.
    218     animated_scroll_end_ = space_above +
    219         divider_height_ + affected_panel_height +  // Size of this panel.
    220         bottom_stuck_header_pixel_size -  // Leave room for these.
    221         height();  // Available size in the window.
    222     if (animated_scroll_end_ > top_stuck_headers_scroll_pos)
    223       animated_scroll_end_ = top_stuck_headers_scroll_pos;
    224   } else {
    225     animated_scroll_end_ = top_stuck_headers_scroll_pos;
    226   }
    227 
    228   animated_scroll_begin_ = scroll_pos_;
    229   if (animated_scroll_begin_ == animated_scroll_end_)
    230     return;  // Nothing to animate.
    231 
    232   // Start animating to the destination.
    233   animation_.Reset();
    234   animation_.Show();
    235 }
    236 
    237 void PanelScroller::AnimationProgressed(const ui::Animation* animation) {
    238   scroll_pos_ = static_cast<int>(
    239       static_cast<double>(animated_scroll_end_ - animated_scroll_begin_) *
    240       animation_.GetCurrentValue()) + animated_scroll_begin_;
    241 
    242   Layout();
    243   SchedulePaint();
    244 }
    245