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/ash_view_ids.h" | 7 #include "ash/common/ash_view_ids.h" |
8 #include "ash/common/material_design/material_design_controller.h" | 8 #include "ash/common/material_design/material_design_controller.h" |
9 #include "ash/common/system/tray/fixed_sized_scroll_view.h" | 9 #include "ash/common/system/tray/fixed_sized_scroll_view.h" |
10 #include "ash/common/system/tray/system_menu_button.h" | 10 #include "ash/common/system/tray/system_menu_button.h" |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
62 } | 62 } |
63 ~ScrollContentsView() override {} | 63 ~ScrollContentsView() override {} |
64 | 64 |
65 protected: | 65 protected: |
66 // views::View: | 66 // views::View: |
67 void OnBoundsChanged(const gfx::Rect& previous_bounds) override { | 67 void OnBoundsChanged(const gfx::Rect& previous_bounds) override { |
68 PositionHeaderRows(); | 68 PositionHeaderRows(); |
69 } | 69 } |
70 | 70 |
71 void PaintChildren(const ui::PaintContext& context) override { | 71 void PaintChildren(const ui::PaintContext& context) override { |
72 for (int i = 0; i < child_count(); ++i) { | 72 views::View::PaintChildren(context); |
73 if (child_at(i)->id() != VIEW_ID_STICKY_HEADER && !child_at(i)->layer()) | |
74 child_at(i)->Paint(context); | |
75 } | |
76 bool did_draw_shadow = false; | 73 bool did_draw_shadow = false; |
77 // Paint header rows above other children in Z-order. | 74 // Paint header row separators. |
78 for (auto& header : headers_) { | 75 for (auto& header : headers_) |
79 if (!header.view->layer()) | |
80 header.view->Paint(context); | |
81 did_draw_shadow = PaintDelineation(header, context) || did_draw_shadow; | 76 did_draw_shadow = PaintDelineation(header, context) || did_draw_shadow; |
82 } | |
83 | 77 |
84 // Draw a shadow at the top of the viewport when scrolled, but only if a | 78 // Draw a shadow at the top of the viewport when scrolled, but only if a |
85 // header didn't already draw one. Overlap the shadow with the separator | 79 // header didn't already draw one. Overlap the shadow with the separator |
86 // that's below the header view so we don't get both a separator and a full | 80 // that's below the header view so we don't get both a separator and a full |
87 // shadow. | 81 // shadow. |
88 if (y() != 0 && !did_draw_shadow) | 82 if (y() != 0 && !did_draw_shadow) |
89 DrawShadow(context, gfx::Rect(0, 0, width(), -y() - kSeparatorWidth)); | 83 DrawShadow(context, gfx::Rect(0, 0, width(), -y() - kSeparatorWidth)); |
90 } | 84 } |
91 | 85 |
92 void Layout() override { | 86 void Layout() override { |
93 views::View::Layout(); | 87 views::View::Layout(); |
94 headers_.clear(); | 88 headers_.clear(); |
95 for (int i = 0; i < child_count(); ++i) { | 89 for (int i = 0; i < child_count(); ++i) { |
96 views::View* view = child_at(i); | 90 views::View* view = child_at(i); |
97 if (view->id() == VIEW_ID_STICKY_HEADER) | 91 if (view->id() == VIEW_ID_STICKY_HEADER) |
98 headers_.emplace_back(view); | 92 headers_.emplace_back(view); |
99 } | 93 } |
100 PositionHeaderRows(); | 94 PositionHeaderRows(); |
101 } | 95 } |
102 | 96 |
| 97 View::Views GetChildrenOrderedByVisualOrder() override { |
| 98 // TODO(varkha): Not sure if this is worth it but the array could be a class |
| 99 // member |children_in_visual_order_| and only updated in an override of |
| 100 // ReorderChildLayers() before calling base View::ReorderChildLayers(). Then |
| 101 // this method could simply return |children_in_visual_order_|. |
| 102 View::Views children; |
| 103 // Iterate over regular children and later over the sticky headers to keep |
| 104 // the sticky headers above in Z-order. |
| 105 for (int i = 0; i < child_count(); ++i) { |
| 106 if (child_at(i)->id() != VIEW_ID_STICKY_HEADER) |
| 107 children.push_back(child_at(i)); |
| 108 } |
| 109 for (int i = 0; i < child_count(); ++i) { |
| 110 if (child_at(i)->id() == VIEW_ID_STICKY_HEADER) |
| 111 children.push_back(child_at(i)); |
| 112 } |
| 113 DCHECK_EQ(child_count(), static_cast<int>(children.size())); |
| 114 return children; |
| 115 } |
| 116 |
103 void ViewHierarchyChanged( | 117 void ViewHierarchyChanged( |
104 const ViewHierarchyChangedDetails& details) override { | 118 const ViewHierarchyChangedDetails& details) override { |
105 if (!details.is_add && details.parent == this) { | 119 if (!details.is_add && details.parent == this) { |
106 headers_.erase(std::remove_if(headers_.begin(), headers_.end(), | 120 headers_.erase(std::remove_if(headers_.begin(), headers_.end(), |
107 [details](const Header& header) { | 121 [details](const Header& header) { |
108 return header.view == details.child; | 122 return header.view == details.child; |
109 }), | 123 }), |
110 headers_.end()); | 124 headers_.end()); |
111 } else if (details.is_add && details.parent == this && | 125 } else if (details.is_add && details.parent == this && |
112 details.child == child_at(0)) { | 126 details.child == child_at(0)) { |
113 // We always want padding on the bottom of the scroll contents. | 127 // We always want padding on the bottom of the scroll contents. |
114 // We only want padding on the top of the scroll contents if the first | 128 // We only want padding on the top of the scroll contents if the first |
115 // child is not a header (in that case, the padding is built into the | 129 // child is not a header (in that case, the padding is built into the |
116 // header). | 130 // header). |
117 DCHECK_EQ(box_layout_, GetLayoutManager()); | 131 DCHECK_EQ(box_layout_, GetLayoutManager()); |
118 box_layout_->set_inside_border_insets( | 132 box_layout_->set_inside_border_insets( |
119 gfx::Insets(details.child->id() == VIEW_ID_STICKY_HEADER | 133 gfx::Insets(details.child->id() == VIEW_ID_STICKY_HEADER |
120 ? 0 | 134 ? 0 |
121 : kMenuSeparatorVerticalPadding, | 135 : kMenuSeparatorVerticalPadding, |
122 0, kMenuSeparatorVerticalPadding, 0)); | 136 0, kMenuSeparatorVerticalPadding, 0)); |
123 } | 137 } |
124 } | 138 } |
125 | 139 |
126 // views::ViewTargeterDelegate: | |
127 View* TargetForRect(View* root, const gfx::Rect& rect) override { | |
128 // Give header rows first dibs on events. | |
129 for (auto& header : headers_) { | |
130 views::View* view = header.view; | |
131 gfx::Rect local_to_header = rect; | |
132 local_to_header.Offset(-view->x(), -view->y()); | |
133 if (ViewTargeterDelegate::DoesIntersectRect(view, local_to_header)) | |
134 return ViewTargeterDelegate::TargetForRect(view, local_to_header); | |
135 } | |
136 return ViewTargeterDelegate::TargetForRect(root, rect); | |
137 } | |
138 | |
139 private: | 140 private: |
140 const SkColor kSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F); | 141 const SkColor kSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F); |
141 const int kShadowOffsetY = 2; | 142 const int kShadowOffsetY = 2; |
142 const int kShadowBlur = 2; | 143 const int kShadowBlur = 2; |
143 // TODO(fukino): Remove this constant once we stop maintaining pre-MD design. | 144 // TODO(fukino): Remove this constant once we stop maintaining pre-MD design. |
144 // crbug.com/614453. | 145 // crbug.com/614453. |
145 const int kContentsBetweenChildSpacingNonMd = 1; | 146 const int kContentsBetweenChildSpacingNonMd = 1; |
146 | 147 |
147 // A structure that keeps the original offset of each header between the | 148 // A structure that keeps the original offset of each header between the |
148 // calls to Layout() to allow keeping track of which view should be sticky. | 149 // calls to Layout() to allow keeping track of which view should be sticky. |
(...skipping 13 matching lines...) Expand all Loading... |
162 bool draw_separator_below; | 163 bool draw_separator_below; |
163 }; | 164 }; |
164 | 165 |
165 // Adjusts y-position of header rows allowing one or two rows to stick to the | 166 // Adjusts y-position of header rows allowing one or two rows to stick to the |
166 // top of the visible viewport. | 167 // top of the visible viewport. |
167 void PositionHeaderRows() { | 168 void PositionHeaderRows() { |
168 const int scroll_offset = -y(); | 169 const int scroll_offset = -y(); |
169 Header* previous_header = nullptr; | 170 Header* previous_header = nullptr; |
170 for (auto& header : base::Reversed(headers_)) { | 171 for (auto& header : base::Reversed(headers_)) { |
171 views::View* header_view = header.view; | 172 views::View* header_view = header.view; |
172 header.draw_separator_below = false; | 173 bool draw_separator_below = false; |
173 if (header.natural_offset >= scroll_offset) { | 174 if (header.natural_offset >= scroll_offset) { |
174 previous_header = &header; | 175 previous_header = &header; |
175 header_view->SetY(header.natural_offset); | 176 header_view->SetY(header.natural_offset); |
176 continue; | 177 } else { |
| 178 if (previous_header && |
| 179 previous_header->view->y() <= |
| 180 scroll_offset + header_view->height()) { |
| 181 // Lower header displacing the header above. |
| 182 draw_separator_below = true; |
| 183 header_view->SetY(previous_header->view->y() - header_view->height()); |
| 184 } else { |
| 185 // A header becomes sticky. |
| 186 header_view->SetY(scroll_offset); |
| 187 header_view->Layout(); |
| 188 header_view->SchedulePaint(); |
| 189 } |
177 } | 190 } |
178 if (previous_header && | 191 if (header.draw_separator_below != draw_separator_below) { |
179 previous_header->view->y() <= scroll_offset + header_view->height()) { | 192 header.draw_separator_below = draw_separator_below; |
180 // Lower header displacing the header above. | 193 TrayPopupUtils::ShowStickyHeaderSeparator(header_view, |
181 header.draw_separator_below = true; | 194 draw_separator_below); |
182 header_view->SetY(previous_header->view->y() - header_view->height()); | |
183 } else { | |
184 // A header becomes sticky. | |
185 header_view->SetY(scroll_offset); | |
186 header_view->Layout(); | |
187 header_view->SchedulePaint(); | |
188 } | 195 } |
189 break; | 196 if (header.natural_offset < scroll_offset) |
| 197 break; |
190 } | 198 } |
191 } | 199 } |
192 | 200 |
193 // Paints a separator for a header view. The separator can be a horizontal | 201 // Paints a separator for a header view. The separator can be a horizontal |
194 // rule or a horizontal shadow, depending on whether the header is sticking to | 202 // rule or a horizontal shadow, depending on whether the header is sticking to |
195 // the top of the scroll viewport. The return value indicates whether a shadow | 203 // the top of the scroll viewport. The return value indicates whether a shadow |
196 // was drawn. | 204 // was drawn. |
197 bool PaintDelineation(const Header& header, const ui::PaintContext& context) { | 205 bool PaintDelineation(const Header& header, const ui::PaintContext& context) { |
198 const View* view = header.view; | 206 const View* view = header.view; |
199 | 207 |
200 // If the header is where it normally belongs, draw nothing. | 208 // If the header is where it normally belongs or If the header is pushed by |
201 if (view->y() == header.natural_offset) | 209 // a header directly below it, draw nothing. |
| 210 if (view->y() == header.natural_offset || header.draw_separator_below) |
202 return false; | 211 return false; |
203 | 212 |
204 // If the header is pushed by a header directly below it, draw a separator. | |
205 if (header.draw_separator_below) { | |
206 // TODO(estade): look better at 1.5x scale. | |
207 ui::PaintRecorder recorder(context, size()); | |
208 gfx::Canvas* canvas = recorder.canvas(); | |
209 gfx::Rect separator = view->bounds(); | |
210 separator.set_y(separator.bottom() - kSeparatorWidth); | |
211 separator.set_height(kSeparatorWidth); | |
212 canvas->FillRect(separator, kSeparatorColor); | |
213 return false; | |
214 } | |
215 | |
216 // Otherwise, draw a shadow below. | 213 // Otherwise, draw a shadow below. |
217 DrawShadow(context, | 214 DrawShadow(context, |
218 gfx::Rect(0, 0, view->width(), view->bounds().bottom())); | 215 gfx::Rect(0, 0, view->width(), view->bounds().bottom())); |
219 return true; | 216 return true; |
220 } | 217 } |
221 | 218 |
222 // Draws a drop shadow below |shadowed_area|. | 219 // Draws a drop shadow below |shadowed_area|. |
223 void DrawShadow(const ui::PaintContext& context, | 220 void DrawShadow(const ui::PaintContext& context, |
224 const gfx::Rect& shadowed_area) { | 221 const gfx::Rect& shadowed_area) { |
225 ui::PaintRecorder recorder(context, size()); | 222 ui::PaintRecorder recorder(context, size()); |
(...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
566 if (index < child_count() - 1 && child_at(index + 1) != title_row_) | 563 if (index < child_count() - 1 && child_at(index + 1) != title_row_) |
567 scroll_border_->set_visible(true); | 564 scroll_border_->set_visible(true); |
568 else | 565 else |
569 scroll_border_->set_visible(false); | 566 scroll_border_->set_visible(false); |
570 } | 567 } |
571 | 568 |
572 views::View::OnPaintBorder(canvas); | 569 views::View::OnPaintBorder(canvas); |
573 } | 570 } |
574 | 571 |
575 } // namespace ash | 572 } // namespace ash |
OLD | NEW |