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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 } | 42 } |
43 | 43 |
44 // The index of the horizontal rule below the title row. | 44 // The index of the horizontal rule below the title row. |
45 const int kTitleRowSeparatorIndex = 1; | 45 const int kTitleRowSeparatorIndex = 1; |
46 | 46 |
47 // A view that is used as ScrollView contents. It supports designating some of | 47 // A view that is used as ScrollView contents. It supports designating some of |
48 // the children as sticky header rows. The sticky header rows are not scrolled | 48 // the children as sticky header rows. The sticky header rows are not scrolled |
49 // above the top of the visible viewport until the next one "pushes" it up and | 49 // above the top of the visible viewport until the next one "pushes" it up and |
50 // are painted above other children. To indicate that a child is a sticky header | 50 // are painted above other children. To indicate that a child is a sticky header |
51 // row use set_id(VIEW_ID_STICKY_HEADER). | 51 // row use set_id(VIEW_ID_STICKY_HEADER). |
52 class ScrollContentsView : public views::View, | 52 class ScrollContentsView : public views::View { |
53 public views::ViewTargeterDelegate { | |
54 public: | 53 public: |
55 ScrollContentsView() | 54 ScrollContentsView() |
56 : box_layout_(new views::BoxLayout( | 55 : box_layout_(new views::BoxLayout( |
57 views::BoxLayout::kVertical, | 56 views::BoxLayout::kVertical, |
58 0, | 57 0, |
59 0, | 58 0, |
60 UseMd() ? 0 : kContentsBetweenChildSpacingNonMd)) { | 59 UseMd() ? 0 : kContentsBetweenChildSpacingNonMd)) { |
61 SetEventTargeter(base::MakeUnique<views::ViewTargeter>(this)); | |
62 SetLayoutManager(box_layout_); | 60 SetLayoutManager(box_layout_); |
63 } | 61 } |
64 ~ScrollContentsView() override {} | 62 ~ScrollContentsView() override {} |
65 | 63 |
66 protected: | 64 protected: |
67 // views::View: | 65 // views::View: |
68 void OnBoundsChanged(const gfx::Rect& previous_bounds) override { | 66 void OnBoundsChanged(const gfx::Rect& previous_bounds) override { |
69 PositionHeaderRows(); | 67 PositionHeaderRows(); |
70 } | 68 } |
71 | 69 |
72 void PaintChildren(const ui::PaintContext& context) override { | 70 void PaintChildren(const ui::PaintContext& context) override { |
73 for (int i = 0; i < child_count(); ++i) { | 71 views::View::PaintChildren(context); |
74 if (child_at(i)->id() != VIEW_ID_STICKY_HEADER && !child_at(i)->layer()) | |
75 child_at(i)->Paint(context); | |
76 } | |
77 bool did_draw_shadow = false; | 72 bool did_draw_shadow = false; |
78 // Paint header rows above other children in Z-order. | 73 // Paint header row separators. |
79 for (auto& header : headers_) { | 74 for (auto& header : headers_) |
80 if (!header.view->layer()) | |
81 header.view->Paint(context); | |
82 did_draw_shadow = PaintDelineation(header, context) || did_draw_shadow; | 75 did_draw_shadow = PaintDelineation(header, context) || did_draw_shadow; |
83 } | |
84 | 76 |
85 // Draw a shadow at the top of the viewport when scrolled, but only if a | 77 // Draw a shadow at the top of the viewport when scrolled, but only if a |
86 // header didn't already draw one. Overlap the shadow with the separator | 78 // header didn't already draw one. Overlap the shadow with the separator |
87 // that's below the header view so we don't get both a separator and a full | 79 // that's below the header view so we don't get both a separator and a full |
88 // shadow. | 80 // shadow. |
89 if (y() != 0 && !did_draw_shadow) | 81 if (y() != 0 && !did_draw_shadow) |
90 DrawShadow(context, gfx::Rect(0, 0, width(), -y() - kSeparatorWidth)); | 82 DrawShadow(context, gfx::Rect(0, 0, width(), -y() - kSeparatorWidth)); |
91 } | 83 } |
92 | 84 |
93 void Layout() override { | 85 void Layout() override { |
94 views::View::Layout(); | 86 views::View::Layout(); |
95 headers_.clear(); | 87 headers_.clear(); |
96 for (int i = 0; i < child_count(); ++i) { | 88 for (int i = 0; i < child_count(); ++i) { |
97 views::View* view = child_at(i); | 89 views::View* view = child_at(i); |
98 if (view->id() == VIEW_ID_STICKY_HEADER) | 90 if (view->id() == VIEW_ID_STICKY_HEADER) |
99 headers_.emplace_back(view); | 91 headers_.emplace_back(view); |
100 } | 92 } |
101 PositionHeaderRows(); | 93 PositionHeaderRows(); |
102 } | 94 } |
103 | 95 |
104 void ReorderChildLayers(ui::Layer* parent_layer) override { | 96 View::Views GetChildrenInZOrder() override { |
105 views::View::ReorderChildLayers(parent_layer); | 97 View::Views children; |
106 ui::Layer* topmost_layer = TopmostLayer(this, parent_layer); | 98 // Iterate over regular children and later over the sticky headers to keep |
107 if (!topmost_layer) | 99 // the sticky headers above in Z-order. |
108 return; | 100 for (int i = 0; i < child_count(); ++i) { |
109 | 101 if (child_at(i)->id() != VIEW_ID_STICKY_HEADER) |
110 // Keep the sticky headers with layers above the rest of the children's | 102 children.push_back(child_at(i)); |
111 // layers. Make sure to avoid changing the stacking order of the layers that | |
112 // are children of |parent_layer| but do not belong to the same parent view. | |
113 // Note: this assumes that all views that have id=VIEW_ID_STICKY_HEADER have | |
114 // a layer. | |
115 for (int i = child_count() - 1; i >= 0; --i) { | |
116 View* child = child_at(i); | |
117 if (child->id() == VIEW_ID_STICKY_HEADER && | |
118 child->layer() != topmost_layer) { | |
119 parent_layer->StackAbove(child->layer(), topmost_layer); | |
120 } | |
121 } | 103 } |
| 104 for (int i = 0; i < child_count(); ++i) { |
| 105 if (child_at(i)->id() == VIEW_ID_STICKY_HEADER) |
| 106 children.push_back(child_at(i)); |
| 107 } |
| 108 DCHECK_EQ(child_count(), static_cast<int>(children.size())); |
| 109 return children; |
122 } | 110 } |
123 | 111 |
124 void ViewHierarchyChanged( | 112 void ViewHierarchyChanged( |
125 const ViewHierarchyChangedDetails& details) override { | 113 const ViewHierarchyChangedDetails& details) override { |
126 if (!details.is_add && details.parent == this) { | 114 if (!details.is_add && details.parent == this) { |
127 headers_.erase(std::remove_if(headers_.begin(), headers_.end(), | 115 headers_.erase(std::remove_if(headers_.begin(), headers_.end(), |
128 [details](const Header& header) { | 116 [details](const Header& header) { |
129 return header.view == details.child; | 117 return header.view == details.child; |
130 }), | 118 }), |
131 headers_.end()); | 119 headers_.end()); |
132 } else if (details.is_add && details.parent == this && | 120 } else if (details.is_add && details.parent == this && |
133 details.child == child_at(0)) { | 121 details.child == child_at(0)) { |
134 // We always want padding on the bottom of the scroll contents. | 122 // We always want padding on the bottom of the scroll contents. |
135 // We only want padding on the top of the scroll contents if the first | 123 // We only want padding on the top of the scroll contents if the first |
136 // child is not a header (in that case, the padding is built into the | 124 // child is not a header (in that case, the padding is built into the |
137 // header). | 125 // header). |
138 DCHECK_EQ(box_layout_, GetLayoutManager()); | 126 DCHECK_EQ(box_layout_, GetLayoutManager()); |
139 box_layout_->set_inside_border_insets( | 127 box_layout_->set_inside_border_insets( |
140 gfx::Insets(details.child->id() == VIEW_ID_STICKY_HEADER | 128 gfx::Insets(details.child->id() == VIEW_ID_STICKY_HEADER |
141 ? 0 | 129 ? 0 |
142 : kMenuSeparatorVerticalPadding, | 130 : kMenuSeparatorVerticalPadding, |
143 0, kMenuSeparatorVerticalPadding, 0)); | 131 0, kMenuSeparatorVerticalPadding, 0)); |
144 } | 132 } |
145 } | 133 } |
146 | 134 |
147 // views::ViewTargeterDelegate: | |
148 View* TargetForRect(View* root, const gfx::Rect& rect) override { | |
149 // Give header rows first dibs on events. | |
150 for (auto& header : headers_) { | |
151 views::View* view = header.view; | |
152 gfx::Rect local_to_header = rect; | |
153 local_to_header.Offset(-view->x(), -view->y()); | |
154 if (ViewTargeterDelegate::DoesIntersectRect(view, local_to_header)) | |
155 return ViewTargeterDelegate::TargetForRect(view, local_to_header); | |
156 } | |
157 return ViewTargeterDelegate::TargetForRect(root, rect); | |
158 } | |
159 | |
160 private: | 135 private: |
161 const SkColor kSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F); | 136 const SkColor kSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F); |
162 const int kShadowOffsetY = 2; | 137 const int kShadowOffsetY = 2; |
163 const int kShadowBlur = 2; | 138 const int kShadowBlur = 2; |
164 // TODO(fukino): Remove this constant once we stop maintaining pre-MD design. | 139 // TODO(fukino): Remove this constant once we stop maintaining pre-MD design. |
165 // crbug.com/614453. | 140 // crbug.com/614453. |
166 const int kContentsBetweenChildSpacingNonMd = 1; | 141 const int kContentsBetweenChildSpacingNonMd = 1; |
167 | 142 |
168 // A structure that keeps the original offset of each header between the | 143 // A structure that keeps the original offset of each header between the |
169 // calls to Layout() to allow keeping track of which view should be sticky. | 144 // calls to Layout() to allow keeping track of which view should be sticky. |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 SkPaint paint; | 219 SkPaint paint; |
245 gfx::ShadowValues shadow; | 220 gfx::ShadowValues shadow; |
246 shadow.emplace_back(gfx::Vector2d(0, kShadowOffsetY), kShadowBlur, | 221 shadow.emplace_back(gfx::Vector2d(0, kShadowOffsetY), kShadowBlur, |
247 kSeparatorColor); | 222 kSeparatorColor); |
248 paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadow)); | 223 paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadow)); |
249 paint.setAntiAlias(true); | 224 paint.setAntiAlias(true); |
250 canvas->ClipRect(shadowed_area, SkClipOp::kDifference); | 225 canvas->ClipRect(shadowed_area, SkClipOp::kDifference); |
251 canvas->DrawRect(shadowed_area, paint); | 226 canvas->DrawRect(shadowed_area, paint); |
252 } | 227 } |
253 | 228 |
254 // Recursively iterates through children to return the child layer that is | |
255 // stacked at the top. Only considers layers that are direct |parent_layer|'s | |
256 // children. | |
257 ui::Layer* TopmostLayer(views::View* view, ui::Layer* parent_layer) { | |
258 DCHECK(parent_layer); | |
259 // Iterate backwards through the children to find the topmost layer. | |
260 for (int i = view->child_count() - 1; i >= 0; --i) { | |
261 views::View* child = view->child_at(i); | |
262 ui::Layer* layer = TopmostLayer(child, parent_layer); | |
263 if (layer) | |
264 return layer; | |
265 } | |
266 if (view->layer() && view->layer() != parent_layer && | |
267 view->layer()->parent() == parent_layer) { | |
268 return view->layer(); | |
269 } | |
270 return nullptr; | |
271 } | |
272 | |
273 views::BoxLayout* box_layout_; | 229 views::BoxLayout* box_layout_; |
274 | 230 |
275 // Header child views that stick to the top of visible viewport when scrolled. | 231 // Header child views that stick to the top of visible viewport when scrolled. |
276 std::vector<Header> headers_; | 232 std::vector<Header> headers_; |
277 | 233 |
278 DISALLOW_COPY_AND_ASSIGN(ScrollContentsView); | 234 DISALLOW_COPY_AND_ASSIGN(ScrollContentsView); |
279 }; | 235 }; |
280 | 236 |
281 // Constants for the title row in material design. | 237 // Constants for the title row in material design. |
282 const int kTitleRowVerticalPadding = 4; | 238 const int kTitleRowVerticalPadding = 4; |
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
602 if (index < child_count() - 1 && child_at(index + 1) != title_row_) | 558 if (index < child_count() - 1 && child_at(index + 1) != title_row_) |
603 scroll_border_->set_visible(true); | 559 scroll_border_->set_visible(true); |
604 else | 560 else |
605 scroll_border_->set_visible(false); | 561 scroll_border_->set_visible(false); |
606 } | 562 } |
607 | 563 |
608 views::View::OnPaintBorder(canvas); | 564 views::View::OnPaintBorder(canvas); |
609 } | 565 } |
610 | 566 |
611 } // namespace ash | 567 } // namespace ash |
OLD | NEW |