| Index: ash/common/system/tray/tray_details_view.cc
|
| diff --git a/ash/common/system/tray/tray_details_view.cc b/ash/common/system/tray/tray_details_view.cc
|
| index e691981f70b70fea0978614432c4fd17c3404e04..b714d61ef18969ad8afe213938c9e377b34dae1b 100644
|
| --- a/ash/common/system/tray/tray_details_view.cc
|
| +++ b/ash/common/system/tray/tray_details_view.cc
|
| @@ -4,13 +4,18 @@
|
|
|
| #include "ash/common/system/tray/tray_details_view.h"
|
|
|
| +#include "ash/common/ash_view_ids.h"
|
| #include "ash/common/material_design/material_design_controller.h"
|
| #include "ash/common/system/tray/fixed_sized_scroll_view.h"
|
| #include "ash/common/system/tray/system_tray.h"
|
| #include "ash/common/system/tray/system_tray_item.h"
|
| #include "ash/common/system/tray/tray_constants.h"
|
| #include "base/containers/adapters.h"
|
| +#include "third_party/skia/include/core/SkDrawLooper.h"
|
| +#include "ui/compositor/paint_context.h"
|
| +#include "ui/compositor/paint_recorder.h"
|
| #include "ui/gfx/canvas.h"
|
| +#include "ui/gfx/skia_util.h"
|
| #include "ui/views/background.h"
|
| #include "ui/views/border.h"
|
| #include "ui/views/controls/progress_bar.h"
|
| @@ -23,14 +28,11 @@
|
| namespace ash {
|
| namespace {
|
|
|
| -const int kHeaderRowSeparatorThickness = 1;
|
| -const SkColor kHeaderRowSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F);
|
| -
|
| // A view that is used as ScrollView contents. It supports designating some of
|
| // the children as sticky header rows. The sticky header rows are not scrolled
|
| // above the top of the visible viewport until the next one "pushes" it up and
|
| // are painted above other children. To indicate that a child is a sticky header
|
| -// row use set_id(kHeaderRowId).
|
| +// row use set_id(VIEW_ID_STICKY_HEADER).
|
| class ScrollContentsView : public views::View,
|
| public views::ViewTargeterDelegate {
|
| public:
|
| @@ -49,13 +51,14 @@ class ScrollContentsView : public views::View,
|
|
|
| void PaintChildren(const ui::PaintContext& context) override {
|
| for (int i = 0; i < child_count(); ++i) {
|
| - if (child_at(i)->id() != kHeaderRowId && !child_at(i)->layer())
|
| + if (child_at(i)->id() != VIEW_ID_STICKY_HEADER && !child_at(i)->layer())
|
| child_at(i)->Paint(context);
|
| }
|
| // Paint header rows above other children in Z-order.
|
| for (auto& header : headers_) {
|
| - if (!header.view()->layer())
|
| - header.view()->Paint(context);
|
| + if (!header.view->layer())
|
| + header.view->Paint(context);
|
| + PaintDelineation(header, context);
|
| }
|
| }
|
|
|
| @@ -64,7 +67,7 @@ class ScrollContentsView : public views::View,
|
| headers_.clear();
|
| for (int i = 0; i < child_count(); ++i) {
|
| views::View* view = child_at(i);
|
| - if (view->id() == kHeaderRowId)
|
| + if (view->id() == VIEW_ID_STICKY_HEADER)
|
| headers_.emplace_back(view);
|
| }
|
| PositionHeaderRows();
|
| @@ -75,7 +78,7 @@ class ScrollContentsView : public views::View,
|
| if (!details.is_add && details.parent == this) {
|
| headers_.erase(std::remove_if(headers_.begin(), headers_.end(),
|
| [details](const Header& header) {
|
| - return header.view() == details.child;
|
| + return header.view == details.child;
|
| }),
|
| headers_.end());
|
| }
|
| @@ -85,7 +88,7 @@ class ScrollContentsView : public views::View,
|
| View* TargetForRect(View* root, const gfx::Rect& rect) override {
|
| // Give header rows first dibs on events.
|
| for (auto& header : headers_) {
|
| - views::View* view = header.view();
|
| + views::View* view = header.view;
|
| gfx::Rect local_to_header = rect;
|
| local_to_header.Offset(-view->x(), -view->y());
|
| if (ViewTargeterDelegate::DoesIntersectRect(view, local_to_header))
|
| @@ -95,43 +98,23 @@ class ScrollContentsView : public views::View,
|
| }
|
|
|
| private:
|
| + const int kSeparatorThickness = 1;
|
| + const SkColor kSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F);
|
| + const int kShadowOffsetY = 2;
|
| + const int kShadowBlur = 2;
|
| +
|
| // A structure that keeps the original offset of each header between the
|
| // calls to Layout() to allow keeping track of which view should be sticky.
|
| - class Header {
|
| - public:
|
| - explicit Header(views::View* header)
|
| - : view_(header), offset_(header->y()), sticky_(true) {
|
| - DecorateAsSticky(false);
|
| - }
|
| -
|
| - // Sets decorations on a header row to indicate whether it is |sticky|.
|
| - void DecorateAsSticky(bool sticky) {
|
| - if (sticky_ == sticky)
|
| - return;
|
| - sticky_ = sticky;
|
| - if (sticky) {
|
| - view_->SetBorder(views::Border::CreateSolidSidedBorder(
|
| - 0, 0, kHeaderRowSeparatorThickness, 0, kHeaderRowSeparatorColor));
|
| - } else {
|
| - view_->SetBorder(views::Border::CreateSolidSidedBorder(
|
| - kHeaderRowSeparatorThickness, 0, 0, 0, kHeaderRowSeparatorColor));
|
| - }
|
| - }
|
| + struct Header {
|
| + explicit Header(views::View* view)
|
| + : view(view), natural_offset(view->y()) {}
|
|
|
| - const views::View* view() const { return view_; }
|
| - views::View* view() { return view_; }
|
| - int offset() const { return offset_; }
|
| -
|
| - private:
|
| // A header View that can be decorated as sticky.
|
| - views::View* view_;
|
| + views::View* view;
|
|
|
| // Offset from the top of ScrollContentsView to |view|'s original vertical
|
| // position.
|
| - int offset_;
|
| -
|
| - // True if the header is decorated as sticky with a shadow below.
|
| - bool sticky_;
|
| + int natural_offset;
|
| };
|
|
|
| // Adjusts y-position of header rows allowing one or two rows to stick to the
|
| @@ -140,23 +123,19 @@ class ScrollContentsView : public views::View,
|
| const int scroll_offset = -y();
|
| Header* previous_header = nullptr;
|
| for (auto& header : base::Reversed(headers_)) {
|
| - if (header.offset() >= scroll_offset) {
|
| - header.DecorateAsSticky(false);
|
| + views::View* header_view = header.view;
|
| + if (header.natural_offset >= scroll_offset) {
|
| previous_header = &header;
|
| + header_view->SetY(header.natural_offset);
|
| continue;
|
| }
|
| - views::View* header_view = header.view();
|
| if (previous_header &&
|
| - previous_header->view()->y() <
|
| - scroll_offset + header_view->height()) {
|
| + previous_header->view->y() < scroll_offset + header_view->height()) {
|
| // Lower header displacing the header above.
|
| - header_view->SetY(previous_header->view()->y() - header_view->height());
|
| - header.DecorateAsSticky(false);
|
| - previous_header->DecorateAsSticky(false);
|
| + header_view->SetY(previous_header->view->y() - header_view->height());
|
| } else {
|
| // A header becomes sticky.
|
| header_view->SetY(scroll_offset);
|
| - header.DecorateAsSticky(true);
|
| header_view->Layout();
|
| header_view->SchedulePaint();
|
| }
|
| @@ -164,6 +143,47 @@ class ScrollContentsView : public views::View,
|
| }
|
| }
|
|
|
| + // Paints a separator for a header view. The separator can be a horizontal
|
| + // rule or a horizontal shadow, depending on whether the header is sticking to
|
| + // the top of the scroll viewport.
|
| + void PaintDelineation(const Header& header, const ui::PaintContext& context) {
|
| + const View* view = header.view;
|
| + const bool at_top = view->y() == -y();
|
| +
|
| + // If the header is where it normally belongs, draw a separator above.
|
| + if (view->y() == header.natural_offset) {
|
| + // But if the header is at the very top of the viewport, draw nothing.
|
| + if (at_top)
|
| + return;
|
| +
|
| + // TODO(estade): look better at 1.5x scale.
|
| + ui::PaintRecorder recorder(context, size());
|
| + gfx::Canvas* canvas = recorder.canvas();
|
| + gfx::Rect separator = view->bounds();
|
| + separator.set_height(kSeparatorThickness);
|
| + canvas->FillRect(separator, kSeparatorColor);
|
| + return;
|
| + }
|
| +
|
| + // If the header is displaced but is not at the top of the viewport, it's
|
| + // being pushed out by another header. Draw nothing.
|
| + if (!at_top)
|
| + return;
|
| +
|
| + // Otherwise, draw a shadow below.
|
| + ui::PaintRecorder recorder(context, size());
|
| + gfx::Canvas* canvas = recorder.canvas();
|
| + SkPaint paint;
|
| + gfx::ShadowValues shadow;
|
| + shadow.emplace_back(gfx::Vector2d(0, kShadowOffsetY), kShadowBlur,
|
| + kSeparatorColor);
|
| + paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadow));
|
| + paint.setAntiAlias(true);
|
| + gfx::Rect rect(0, 0, view->width(), view->bounds().bottom());
|
| + canvas->ClipRect(rect, SkRegion::kDifference_Op);
|
| + canvas->DrawRect(rect, paint);
|
| + }
|
| +
|
| // Header child views that stick to the top of visible viewport when scrolled.
|
| std::vector<Header> headers_;
|
|
|
| @@ -301,8 +321,8 @@ void TrayDetailsView::CreateTitleRow(int string_id) {
|
| title_row_ = new SpecialPopupRow();
|
| title_row_->SetTextLabel(string_id, this);
|
| if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
|
| - title_row_->SetBorder(views::Border::CreateEmptyBorder(
|
| - kTitleRowPaddingTop, 0, kTitleRowPaddingBottom, 0));
|
| + title_row_->SetBorder(views::CreateEmptyBorder(kTitleRowPaddingTop, 0,
|
| + kTitleRowPaddingBottom, 0));
|
| AddChildViewAt(title_row_, 0);
|
| // In material design, we use a customized bottom border which is nomally a
|
| // simple separator (views::Separator) but can be combined with an
|
| @@ -313,7 +333,7 @@ void TrayDetailsView::CreateTitleRow(int string_id) {
|
| new views::Separator(views::Separator::HORIZONTAL);
|
| separator->SetColor(kTitleRowSeparatorBorderColor);
|
| separator->SetPreferredSize(kTitleRowSeparatorBorderHeight);
|
| - separator->SetBorder(views::Border::CreateEmptyBorder(
|
| + separator->SetBorder(views::CreateEmptyBorder(
|
| kTitleRowSeparatorHeight - kTitleRowSeparatorBorderHeight, 0, 0, 0));
|
| title_row_separator_->AddChildView(separator);
|
| AddChildViewAt(title_row_separator_, 1);
|
|
|