• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PanelScroller()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 
~PanelScroller()77 PanelScroller::~PanelScroller() {
78   STLDeleteContainerPointers(panels_.begin(), panels_.end());
79 }
80 
81 // static
CreateWindow()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 
ViewHierarchyChanged(bool is_add,views::View * parent,views::View * child)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 
GetPreferredSize()104 gfx::Size PanelScroller::GetPreferredSize() {
105   return gfx::Size(75, 200);
106 }
107 
Layout()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 
OnMousePressed(const views::MouseEvent & event)157 bool PanelScroller::OnMousePressed(const views::MouseEvent& event) {
158   return true;
159 }
160 
OnMouseDragged(const views::MouseEvent & event)161 bool PanelScroller::OnMouseDragged(const views::MouseEvent& event) {
162   return true;
163 }
164 
HeaderClicked(PanelScrollerHeader * source)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 
ScrollToPanel(int index)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 
AnimationProgressed(const ui::Animation * animation)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