panel_scroller.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this 2// source code is governed by a BSD-style license that can be found in the 3// 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 "gfx/canvas.h" 15#include "views/widget/widget_gtk.h" 16 17struct PanelScroller::Panel { 18 PanelScrollerHeader* header; 19 PanelScrollerContainer* container; 20}; 21 22PanelScroller::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(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 77PanelScroller::~PanelScroller() { 78 STLDeleteContainerPointers(panels_.begin(), panels_.end()); 79} 80 81// static 82PanelScroller* PanelScroller::CreateWindow() { 83 views::WidgetGtk* widget = 84 new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW); 85 widget->set_delete_on_destroy(true); 86 widget->Init(NULL, gfx::Rect(0, 0, 100, 800)); 87 88 PanelScroller* scroller = new PanelScroller(); 89 widget->SetContentsView(scroller); 90 91 widget->Show(); 92 93 return scroller; 94} 95 96void PanelScroller::ViewHierarchyChanged(bool is_add, 97 views::View* parent, 98 views::View* child) { 99 // Our child views changed without us knowing it. Stop the animation and mark 100 // us as dirty (needs_layout_ = true). 101 animation_.Stop(); 102 needs_layout_ = true; 103} 104 105gfx::Size PanelScroller::GetPreferredSize() { 106 return gfx::Size(75, 200); 107} 108 109void PanelScroller::Layout() { 110/* TODO(brettw) this doesn't work for some reason. 111 if (!needs_layout_ || !animation_.IsShowing()) 112 return; 113 needs_layout_ = false;*/ 114 115 // The current location in the content that we're laying out. This is before 116 // scrolling is accounted for. 117 int cur_content_pos = 0; 118 119 // Number of pixels used by headers stuck to the top of the scroll area. 120 int top_header_pixel_count = 0; 121 122 int panel_count = static_cast<int>(panels_.size()); 123 for (int i = 0; i < panel_count; i++) { 124 if (cur_content_pos < scroll_pos_ + top_header_pixel_count) { 125 // This panel is at least partially off the top. Put the header below the 126 // others already there. 127 panels_[i]->header->SetBounds(gfx::Rect(0, top_header_pixel_count, 128 width(), divider_height_)); 129 top_header_pixel_count += divider_height_; 130 131 } else if (cur_content_pos > height() + scroll_pos_ - 132 (panel_count - i) * divider_height_) { 133 // When we've hit the bottom of the visible content, all the remaining 134 // headers will stack up at the bottom. Counting this header, there are 135 // (size() - i) left, which is used in the expression above. 136 int top = height() - (panel_count - i) * divider_height_; 137 panels_[i]->header->SetBounds(gfx::Rect(0, top, 138 width(), divider_height_)); 139 } else { 140 // Normal header positioning in-flow. 141 panels_[i]->header->SetBounds(gfx::Rect(0, cur_content_pos - scroll_pos_, 142 width(), 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->SetBounds( 151 gfx::Rect(0, cur_content_pos - scroll_pos_, 152 width(), container_height)); 153 cur_content_pos += container_height; 154 } 155} 156 157bool PanelScroller::OnMousePressed(const views::MouseEvent& event) { 158 return true; 159} 160 161bool PanelScroller::OnMouseDragged(const views::MouseEvent& event) { 162 return true; 163} 164 165void PanelScroller::OnMouseReleased(const views::MouseEvent& event, 166 bool canceled) { 167} 168 169void PanelScroller::HeaderClicked(PanelScrollerHeader* source) { 170 for (size_t i = 0; i < panels_.size(); i++) { 171 if (panels_[i]->header == source) { 172 ScrollToPanel(static_cast<int>(i)); 173 return; 174 } 175 } 176 NOTREACHED() << "Invalid panel passed to HeaderClicked."; 177} 178 179void PanelScroller::ScrollToPanel(int index) { 180 int affected_panel_height = 181 panels_[index]->container->GetPreferredSize().height(); 182 183 // The pixel size we need to reserve for the stuck headers. 184 int top_stuck_header_pixel_size = index * divider_height_; 185 int bottom_stuck_header_pixel_size = 186 (static_cast<int>(panels_.size()) - index - 1) * divider_height_; 187 188 // Compute the offset of the top of the panel to scroll to. 189 int space_above = 0; 190 for (int i = 0; i < index; i++) { 191 space_above += divider_height_ + 192 panels_[i]->container->GetPreferredSize().height(); 193 } 194 195 // Compute the space below the top of the panel. 196 int space_below = 0; 197 for (int i = index; i < static_cast<int>(panels_.size()); i++) { 198 space_below += divider_height_ + 199 panels_[i]->container->GetPreferredSize().height(); 200 } 201 202 // The scroll position of the top of the stuck headers is the space above 203 // minus the size of the headers stuck there. 204 int top_stuck_headers_scroll_pos = space_above - top_stuck_header_pixel_size; 205 206 // If the panel is already fully visible, do nothing. 207 if (scroll_pos_ <= top_stuck_headers_scroll_pos && 208 space_above + divider_height_ + affected_panel_height - 209 bottom_stuck_header_pixel_size <= scroll_pos_ + height()) 210 return; 211 212 // Compute the scroll position. 213 if (height() > space_below) { 214 // There's enough room for this panel and everything below it to fit on the 215 // screen. 216 animated_scroll_end_ = (space_above + space_below) - height(); 217 if (animated_scroll_end_ > top_stuck_headers_scroll_pos) 218 animated_scroll_end_ = top_stuck_headers_scroll_pos; 219 } else if (space_above > scroll_pos_) { 220 // If we're going to be scrolling the content up, scroll just until the 221 // panel in question is fully visible. 222 animated_scroll_end_ = space_above + 223 divider_height_ + affected_panel_height + // Size of this panel. 224 bottom_stuck_header_pixel_size - // Leave room for these. 225 height(); // Available size in the window. 226 if (animated_scroll_end_ > top_stuck_headers_scroll_pos) 227 animated_scroll_end_ = top_stuck_headers_scroll_pos; 228 } else { 229 animated_scroll_end_ = top_stuck_headers_scroll_pos; 230 } 231 232 animated_scroll_begin_ = scroll_pos_; 233 if (animated_scroll_begin_ == animated_scroll_end_) 234 return; // Nothing to animate. 235 236 // Start animating to the destination. 237 animation_.Reset(); 238 animation_.Show(); 239} 240 241void PanelScroller::AnimationProgressed(const Animation* animation) { 242 scroll_pos_ = static_cast<int>( 243 static_cast<double>(animated_scroll_end_ - animated_scroll_begin_) * 244 animation_.GetCurrentValue()) + animated_scroll_begin_; 245 246 Layout(); 247 SchedulePaint(); 248} 249