Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ash/common/system/tray/tray_details_view.h" | 5 #include "ash/common/system/tray/tray_details_view.h" |
| 6 | 6 |
| 7 #include "ash/common/material_design/material_design_controller.h" | 7 #include "ash/common/material_design/material_design_controller.h" |
| 8 #include "ash/common/system/tray/fixed_sized_scroll_view.h" | 8 #include "ash/common/system/tray/fixed_sized_scroll_view.h" |
| 9 #include "ash/common/system/tray/system_tray.h" | 9 #include "ash/common/system/tray/system_tray.h" |
| 10 #include "ash/common/system/tray/system_tray_item.h" | 10 #include "ash/common/system/tray/system_tray_item.h" |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 25 const int kTitleRowSeparatorBorderHeight = 1; | 25 const int kTitleRowSeparatorBorderHeight = 1; |
| 26 const int kTitleRowProgressBarHeight = 2; | 26 const int kTitleRowProgressBarHeight = 2; |
| 27 // The separator's height should be same as kTitleRowProgressBarHeight, and | 27 // The separator's height should be same as kTitleRowProgressBarHeight, and |
| 28 // should not be larger than kTitleRowSeparatorBorderHeight. | 28 // should not be larger than kTitleRowSeparatorBorderHeight. |
| 29 const int kTitleRowSeparatorHeight = kTitleRowProgressBarHeight; | 29 const int kTitleRowSeparatorHeight = kTitleRowProgressBarHeight; |
| 30 const int kTitleRowPaddingTop = kTitleRowVerticalPadding; | 30 const int kTitleRowPaddingTop = kTitleRowVerticalPadding; |
| 31 const int kTitleRowPaddingBottom = | 31 const int kTitleRowPaddingBottom = |
| 32 kTitleRowVerticalPadding - kTitleRowSeparatorHeight; | 32 kTitleRowVerticalPadding - kTitleRowSeparatorHeight; |
| 33 const SkColor kTitleRowSeparatorBorderColor = SkColorSetRGB(0xe0, 0xe0, 0xe0); | 33 const SkColor kTitleRowSeparatorBorderColor = SkColorSetRGB(0xe0, 0xe0, 0xe0); |
| 34 | 34 |
| 35 const int kHeaderRowId = 1000; | |
|
tdanderson
2016/10/28 19:42:41
Would it be safer to use -1 for |kHeaderRowId| ?
varkha
2016/11/02 02:03:20
Done.
| |
| 36 const int kHeaderRowSeparatorThickness = 1; | |
| 37 const SkColor kHeaderRowSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F); | |
| 38 | |
| 35 // Special layout to overlap the separator for title row and the progress bar | 39 // Special layout to overlap the separator for title row and the progress bar |
| 36 // which is displayed on it. | 40 // which is displayed on it. |
| 37 // Expected children are a views::Separator and, optionally, a | 41 // Expected children are a views::Separator and, optionally, a |
| 38 // views::ProgressBar. If the progress bar is present and visible, it is drawn | 42 // views::ProgressBar. If the progress bar is present and visible, it is drawn |
| 39 // over top of the separator so that only the progress bar is visible. | 43 // over top of the separator so that only the progress bar is visible. |
| 40 class TitleRowSeparatorLayout : public views::LayoutManager { | 44 class TitleRowSeparatorLayout : public views::LayoutManager { |
| 41 public: | 45 public: |
| 42 TitleRowSeparatorLayout() {} | 46 TitleRowSeparatorLayout() {} |
| 43 ~TitleRowSeparatorLayout() override {} | 47 ~TitleRowSeparatorLayout() override {} |
| 44 | 48 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 58 | 62 |
| 59 private: | 63 private: |
| 60 int GetMaxHeight(const views::View* host) const { | 64 int GetMaxHeight(const views::View* host) const { |
| 61 int max_height = 0; | 65 int max_height = 0; |
| 62 for (int i = 0; i < host->child_count(); ++i) | 66 for (int i = 0; i < host->child_count(); ++i) |
| 63 max_height = std::max(max_height, host->child_at(i)->height()); | 67 max_height = std::max(max_height, host->child_at(i)->height()); |
| 64 return max_height; | 68 return max_height; |
| 65 } | 69 } |
| 66 }; | 70 }; |
| 67 | 71 |
| 68 } // namespace | 72 // Variation of a BoxLayout that keeps header rows at the end of children_ in |
| 73 // order to stack them above the other rows while maintaining the order (group | |
| 74 // headers first, the rest of the group rows in their original order). | |
| 75 // To use this layout tag header rows with set_id(kHeaderRowId) and tag groups | |
| 76 // including the header row with SetGroup(group_id). | |
| 77 class HeaderListLayout : public views::BoxLayout { | |
| 78 public: | |
| 79 HeaderListLayout() : views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1) {} | |
| 80 ~HeaderListLayout() override {} | |
| 81 | |
| 82 protected: | |
| 83 // views::BoxLayout: | |
| 84 void LayoutChildren(views::View* host, | |
| 85 const gfx::Rect& child_area, | |
| 86 int main_free_space, | |
| 87 int flex_sum, | |
| 88 int main_position, | |
| 89 int current_flex, | |
| 90 int* total_padding) override { | |
| 91 for (int i = 0, count = host->child_count(); i < count; ++i) { | |
|
tdanderson
2016/10/28 19:42:41
I would find LayoutChildren() to be much more read
varkha
2016/11/02 02:03:20
Acknowledged.
| |
| 92 views::View* child = host->child_at(i); | |
| 93 if (child->id() == kHeaderRowId) { | |
|
Evan Stade
2016/10/28 17:15:40
I think we discussed an alternative where you woul
varkha
2016/11/01 00:51:06
I've tried to add setting header rows explicitly.
| |
| 94 auto header = std::find(headers_.begin(), headers_.end(), child); | |
| 95 if (header == headers_.end()) | |
| 96 headers_.push_back(Header(child)); | |
| 97 } | |
| 98 } | |
| 99 // Ensure that the header rows are stacked at the end of |host|'s children. | |
| 100 for (const auto& header : headers_) | |
| 101 host->ReorderChildView(header.view, -1); | |
| 102 | |
| 103 auto headers(headers_); | |
| 104 int group_id = -1; | |
| 105 for (int i = 0, count = host->child_count(); i < count; ++i) { | |
| 106 views::View* child = host->child_at(i); | |
| 107 if (!child->visible() || child->id() == kHeaderRowId) | |
| 108 continue; | |
| 109 if (child->GetGroup() != group_id) { | |
| 110 // Find a header for the new group and place it above the group. | |
| 111 group_id = child->GetGroup(); | |
| 112 auto header = std::find(headers.begin(), headers.end(), group_id); | |
| 113 if (header != headers.end()) { | |
| 114 LayoutChild(header->view, child_area, main_free_space, flex_sum, | |
| 115 &main_position, ¤t_flex, total_padding); | |
| 116 headers.erase(header); | |
| 117 } | |
| 118 } | |
| 119 LayoutChild(child, child_area, main_free_space, flex_sum, &main_position, | |
| 120 ¤t_flex, total_padding); | |
| 121 } | |
| 122 // In the end layout unclaimed headers. | |
|
tdanderson
2016/10/28 19:42:41
nit on wording: just "Layout unclaimed headers" or
varkha
2016/11/02 02:03:20
Acknowledged.
| |
| 123 for (const auto& header : headers) { | |
| 124 LayoutChild(header.view, child_area, main_free_space, flex_sum, | |
| 125 &main_position, ¤t_flex, total_padding); | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 void ViewRemoved(views::View* host, views::View* view) override { | |
| 130 views::BoxLayout::ViewRemoved(host, view); | |
| 131 auto header = std::find(headers_.begin(), headers_.end(), view); | |
| 132 if (header != headers_.end()) | |
| 133 headers_.erase(header); | |
| 134 } | |
| 135 | |
| 136 private: | |
| 137 class Header { | |
|
Evan Stade
2016/10/28 17:15:40
somewhat confusing that you have to classes both n
varkha
2016/11/02 02:03:20
Acknowledged.
| |
| 138 public: | |
| 139 explicit Header(views::View* header) : view(header) {} | |
| 140 bool operator==(views::View* other) { return view == other; } | |
| 141 bool operator==(int group_id) { return view->GetGroup() == group_id; } | |
| 142 | |
| 143 views::View* view; | |
| 144 }; | |
| 145 | |
| 146 // Header child views that stick to the top of visible viewport when scrolled. | |
| 147 std::vector<Header> headers_; | |
| 148 | |
| 149 DISALLOW_COPY_AND_ASSIGN(HeaderListLayout); | |
| 150 }; | |
| 151 | |
| 152 // A view that is used as ScrollView contents. It supports designating some of | |
| 153 // the children as sticky header rows. The sticky header rows are not scrolled | |
| 154 // above the top of the visible viewport and are painted above other children. | |
| 155 // To indicate that a child is a sticky header row use set_id(kHeaderRowId). | |
| 156 class ScrollContentsView : public views::View { | |
| 157 public: | |
| 158 ScrollContentsView() {} | |
| 159 ~ScrollContentsView() override {} | |
| 160 | |
| 161 protected: | |
| 162 // views::View. | |
|
tdanderson
2016/10/28 19:42:41
nit: views::View:
varkha
2016/11/02 02:03:21
Done.
| |
| 163 void Layout() override { | |
| 164 views::View::Layout(); | |
| 165 headers_.clear(); | |
| 166 for (int i = 0, count = child_count(); i < count; ++i) { | |
|
tdanderson
2016/10/28 19:42:41
just (int i = 0; i < child_count(); ++i) ? here an
varkha
2016/11/02 02:03:20
Done.
| |
| 167 views::View* header = child_at(i); | |
|
tdanderson
2016/10/28 19:42:41
nit: consider naming as |view| instead since you h
varkha
2016/11/02 02:03:21
Done.
| |
| 168 if (header->id() == kHeaderRowId) | |
| 169 headers_.push_back(Header(header)); | |
| 170 } | |
| 171 ScrollChildren(); | |
| 172 } | |
| 173 | |
| 174 void OnBoundsChanged(const gfx::Rect& previous_bounds) override { | |
| 175 ScrollChildren(); | |
| 176 } | |
| 177 | |
| 178 void ViewHierarchyChanged( | |
| 179 const ViewHierarchyChangedDetails& details) override { | |
| 180 if (!details.is_add && details.parent == this) { | |
| 181 auto header = std::find(headers_.begin(), headers_.end(), details.child); | |
| 182 if (header != headers_.end()) | |
| 183 headers_.erase(header); | |
|
tdanderson
2016/10/28 19:42:41
Shouldn't you call ScrollChildren() after erasing
varkha
2016/11/02 02:03:20
I thought you would get a Layout() call when this
| |
| 184 } | |
| 185 } | |
| 186 | |
| 187 const char* GetClassName() const override { return "ScrollContentsView"; } | |
| 188 | |
| 189 private: | |
| 190 class Header { | |
| 191 public: | |
| 192 explicit Header(views::View* header) | |
| 193 : view(header), offset(header->bounds().y()) {} | |
| 194 bool operator==(views::View* other) { return view == other; } | |
| 195 | |
| 196 views::View* view; | |
| 197 int offset; | |
| 198 }; | |
| 199 | |
| 200 // Sets decorations on a header row to indicate whether it is sticky. | |
| 201 static void ShowHeaderSticky(views::View* header, bool show_sticky) { | |
| 202 if (show_sticky) { | |
| 203 header->SetBorder(views::Border::CreateSolidSidedBorder( | |
| 204 0, 0, kHeaderRowSeparatorThickness, 0, kHeaderRowSeparatorColor)); | |
| 205 } else { | |
| 206 header->SetBorder(views::Border::CreateSolidSidedBorder( | |
| 207 kHeaderRowSeparatorThickness, 0, 0, 0, kHeaderRowSeparatorColor)); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 // Adjusts y-position of header rows allowing one or two rows to stick to the | |
| 212 // top of the visible viewport. | |
|
Evan Stade
2016/10/28 17:15:40
do you have a use case for multiple sticky headers
varkha
2016/11/02 02:03:20
Yes, Cellular networks will have same UI with a st
| |
| 213 void ScrollChildren() { | |
|
tdanderson
2016/10/28 19:42:41
Consider a more descriptive name for this, such as
varkha
2016/11/02 02:03:20
Done.
| |
| 214 const int scroll_offset = -bounds().y(); | |
| 215 Header* previous_header = nullptr; | |
| 216 for (auto& header : headers_) { | |
| 217 gfx::Rect header_bounds = header.view->bounds(); | |
| 218 if (scroll_offset > header.offset) { | |
| 219 header_bounds.set_y(scroll_offset); | |
| 220 header.view->SetBoundsRect(header_bounds); | |
| 221 ShowHeaderSticky(header.view, true); | |
| 222 header.view->Layout(); | |
| 223 header.view->SchedulePaint(); | |
| 224 if (previous_header) { | |
| 225 header_bounds = previous_header->view->bounds(); | |
| 226 header_bounds.set_y(previous_header->offset); | |
| 227 previous_header->view->SetBoundsRect(header_bounds); | |
| 228 ShowHeaderSticky(previous_header->view, false); | |
| 229 } | |
| 230 previous_header = &header; | |
| 231 } else if (previous_header && | |
| 232 header_bounds.y() < previous_header->view->bounds().bottom()) { | |
| 233 gfx::Rect previous_header_bounds = previous_header->view->bounds(); | |
| 234 previous_header_bounds.set_y(header_bounds.y() - | |
| 235 previous_header->view->bounds().height()); | |
| 236 previous_header->view->SetBoundsRect(previous_header_bounds); | |
| 237 ShowHeaderSticky(previous_header->view, false); | |
| 238 ShowHeaderSticky(header.view, false); | |
| 239 } else { | |
| 240 ShowHeaderSticky(header.view, false); | |
| 241 } | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 // Header child views that stick to the top of visible viewport when scrolled. | |
| 246 std::vector<Header> headers_; | |
| 247 | |
| 248 DISALLOW_COPY_AND_ASSIGN(ScrollContentsView); | |
| 249 }; | |
| 69 | 250 |
| 70 class ScrollSeparator : public views::View { | 251 class ScrollSeparator : public views::View { |
| 71 public: | 252 public: |
| 72 ScrollSeparator() {} | 253 ScrollSeparator() {} |
| 73 | 254 |
| 74 ~ScrollSeparator() override {} | 255 ~ScrollSeparator() override {} |
| 75 | 256 |
| 76 private: | 257 private: |
| 77 // Overriden from views::View. | 258 // views::View. |
| 78 void OnPaint(gfx::Canvas* canvas) override { | 259 void OnPaint(gfx::Canvas* canvas) override { |
| 79 canvas->FillRect(gfx::Rect(0, height() / 2, width(), 1), kBorderLightColor); | 260 canvas->FillRect(gfx::Rect(0, height() / 2, width(), 1), |
| 261 ash::kBorderLightColor); | |
| 80 } | 262 } |
| 81 gfx::Size GetPreferredSize() const override { | 263 gfx::Size GetPreferredSize() const override { |
| 82 return gfx::Size(1, kTrayPopupScrollSeparatorHeight); | 264 return gfx::Size(1, ash::kTrayPopupScrollSeparatorHeight); |
| 83 } | 265 } |
| 84 | 266 |
| 85 DISALLOW_COPY_AND_ASSIGN(ScrollSeparator); | 267 DISALLOW_COPY_AND_ASSIGN(ScrollSeparator); |
| 86 }; | 268 }; |
| 87 | 269 |
| 270 } // namespace | |
| 271 | |
| 88 class ScrollBorder : public views::Border { | 272 class ScrollBorder : public views::Border { |
| 89 public: | 273 public: |
| 90 ScrollBorder() {} | 274 ScrollBorder() {} |
| 91 ~ScrollBorder() override {} | 275 ~ScrollBorder() override {} |
| 92 | 276 |
| 93 void set_visible(bool visible) { visible_ = visible; } | 277 void set_visible(bool visible) { visible_ = visible; } |
| 94 | 278 |
| 95 private: | 279 private: |
| 96 // Overridden from views::Border. | 280 // views::Border. |
| 97 void Paint(const views::View& view, gfx::Canvas* canvas) override { | 281 void Paint(const views::View& view, gfx::Canvas* canvas) override { |
| 98 if (!visible_) | 282 if (!visible_) |
| 99 return; | 283 return; |
| 100 canvas->FillRect(gfx::Rect(0, view.height() - 1, view.width(), 1), | 284 canvas->FillRect(gfx::Rect(0, view.height() - 1, view.width(), 1), |
| 101 kBorderLightColor); | 285 kBorderLightColor); |
| 102 } | 286 } |
| 103 | 287 |
| 104 gfx::Insets GetInsets() const override { return gfx::Insets(0, 0, 1, 0); } | 288 gfx::Insets GetInsets() const override { return gfx::Insets(0, 0, 1, 0); } |
| 105 | 289 |
| 106 gfx::Size GetMinimumSize() const override { return gfx::Size(0, 1); } | 290 gfx::Size GetMinimumSize() const override { return gfx::Size(0, 1); } |
| 107 | 291 |
| 108 bool visible_; | 292 bool visible_ = false; |
| 109 | 293 |
| 110 DISALLOW_COPY_AND_ASSIGN(ScrollBorder); | 294 DISALLOW_COPY_AND_ASSIGN(ScrollBorder); |
| 111 }; | 295 }; |
| 112 | 296 |
| 113 TrayDetailsView::TrayDetailsView(SystemTrayItem* owner) | 297 TrayDetailsView::TrayDetailsView(SystemTrayItem* owner) |
| 114 : owner_(owner), | 298 : owner_(owner), |
| 115 title_row_(nullptr), | 299 title_row_(nullptr), |
| 116 scroller_(nullptr), | 300 scroller_(nullptr), |
| 117 scroll_content_(nullptr), | 301 scroll_content_(nullptr), |
| 118 progress_bar_(nullptr), | 302 progress_bar_(nullptr), |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 174 CreateExtraTitleRowButtons(); | 358 CreateExtraTitleRowButtons(); |
| 175 | 359 |
| 176 if (MaterialDesignController::IsSystemTrayMenuMaterial()) | 360 if (MaterialDesignController::IsSystemTrayMenuMaterial()) |
| 177 back_button_ = title_row_->AddBackButton(this); | 361 back_button_ = title_row_->AddBackButton(this); |
| 178 | 362 |
| 179 Layout(); | 363 Layout(); |
| 180 } | 364 } |
| 181 | 365 |
| 182 void TrayDetailsView::CreateScrollableList() { | 366 void TrayDetailsView::CreateScrollableList() { |
| 183 DCHECK(!scroller_); | 367 DCHECK(!scroller_); |
| 184 scroll_content_ = new views::View; | 368 scroll_content_ = new ScrollContentsView(); |
| 185 scroll_content_->SetLayoutManager( | 369 scroll_content_->SetLayoutManager(new HeaderListLayout()); |
| 186 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); | |
| 187 scroller_ = new FixedSizedScrollView; | 370 scroller_ = new FixedSizedScrollView; |
| 188 scroller_->SetContentsView(scroll_content_); | 371 scroller_->SetContentsView(scroll_content_); |
| 189 | 372 |
| 190 // Note: |scroller_| takes ownership of |scroll_border_|. | 373 // Note: |scroller_| takes ownership of |scroll_border_|. |
| 191 scroll_border_ = new ScrollBorder; | 374 scroll_border_ = new ScrollBorder; |
| 192 scroller_->SetBorder(std::unique_ptr<views::Border>(scroll_border_)); | 375 scroller_->SetBorder(std::unique_ptr<views::Border>(scroll_border_)); |
| 193 | 376 |
| 194 AddChildView(scroller_); | 377 AddChildView(scroller_); |
| 195 } | 378 } |
| 196 | 379 |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 279 if (index < child_count() - 1 && child_at(index + 1) != title_row_) | 462 if (index < child_count() - 1 && child_at(index + 1) != title_row_) |
| 280 scroll_border_->set_visible(true); | 463 scroll_border_->set_visible(true); |
| 281 else | 464 else |
| 282 scroll_border_->set_visible(false); | 465 scroll_border_->set_visible(false); |
| 283 } | 466 } |
| 284 | 467 |
| 285 views::View::OnPaintBorder(canvas); | 468 views::View::OnPaintBorder(canvas); |
| 286 } | 469 } |
| 287 | 470 |
| 288 } // namespace ash | 471 } // namespace ash |
| OLD | NEW |