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