| 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 |