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 |