| 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 70ff14fd6b4feb2838251819af30b34515ef2cf2..576ae4d9cda1767041d4d31881ceb1d1025a70ef 100644
|
| --- a/ash/common/system/tray/tray_details_view.cc
|
| +++ b/ash/common/system/tray/tray_details_view.cc
|
| @@ -12,6 +12,8 @@
|
| #include "ui/gfx/canvas.h"
|
| #include "ui/views/background.h"
|
| #include "ui/views/border.h"
|
| +#include "ui/views/view_targeter.h"
|
| +#include "ui/views/view_targeter_delegate.h"
|
| #include "ui/views/controls/progress_bar.h"
|
| #include "ui/views/controls/scroll_view.h"
|
| #include "ui/views/controls/separator.h"
|
| @@ -20,6 +22,133 @@
|
| namespace ash {
|
| namespace {
|
|
|
| +const int kHeaderRowId = 1000;
|
| +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 and are painted above other children.
|
| +// To indicate that a child is a sticky header row use set_id(kHeaderRowId).
|
| +class ScrollContentsView : public views::View,
|
| + public views::ViewTargeterDelegate {
|
| + public:
|
| + ScrollContentsView(ash::TrayDetailsView* tray_details_view) {
|
| + SetEventTargeter(base::MakeUnique<views::ViewTargeter>(this));
|
| + }
|
| + ~ScrollContentsView() override {}
|
| +
|
| + protected:
|
| + // views::View.
|
| + void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
|
| + ScrollChildren();
|
| + }
|
| +
|
| + void PaintChildren(const ui::PaintContext& context) override {
|
| + for (int i = 0, count = child_count(); i < count; ++i) {
|
| + if (child_at(i)->id() != kHeaderRowId && !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);
|
| + }
|
| + }
|
| +
|
| + void Layout() override {
|
| + views::View::Layout();
|
| + headers_.clear();
|
| + for (int i = 0, count = child_count(); i < count; ++i) {
|
| + views::View* header = child_at(i);
|
| + if (header->id() == kHeaderRowId)
|
| + headers_.push_back(Header(header));
|
| + }
|
| + ScrollChildren();
|
| + }
|
| +
|
| + void ViewHierarchyChanged(
|
| + const ViewHierarchyChangedDetails& details) override {
|
| + if (!details.is_add && details.parent == this) {
|
| + auto header = std::find(headers_.begin(), headers_.end(), details.child);
|
| + if (header != headers_.end())
|
| + headers_.erase(header);
|
| + }
|
| + }
|
| +
|
| + 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;
|
| + gfx::Rect local_to_header = rect;
|
| + local_to_header.Offset(-view->x(), -view->y());
|
| + if (ViewTargeterDelegate::DoesIntersectRect(view, local_to_header))
|
| + return ViewTargeterDelegate::TargetForRect(view, local_to_header);
|
| + }
|
| + return ViewTargeterDelegate::TargetForRect(root, rect);
|
| + }
|
| +
|
| + private:
|
| + class Header {
|
| + public:
|
| + Header(views::View* header) : view(header), offset(header->bounds().y()) {}
|
| + bool operator==(views::View* other) { return view == other; }
|
| +
|
| + views::View* view;
|
| + int offset;
|
| + };
|
| +
|
| + // Sets decorations on a header row to indicate whether it is sticky.
|
| + static void ShowHeaderSticky(views::View* header, bool show_sticky) {
|
| + if (show_sticky) {
|
| + header->SetBorder(views::Border::CreateSolidSidedBorder(
|
| + 0, 0, kHeaderRowSeparatorThickness, 0, kHeaderRowSeparatorColor));
|
| + } else {
|
| + header->SetBorder(views::Border::CreateSolidSidedBorder(
|
| + kHeaderRowSeparatorThickness, 0, 0, 0, kHeaderRowSeparatorColor));
|
| + }
|
| + }
|
| +
|
| + // Adjusts y-position of header rows allowing one or two rows to stick to the
|
| + // top of the visible viewport.
|
| + void ScrollChildren() {
|
| + const int scroll_offset = -bounds().y();
|
| + Header* previous_header = nullptr;
|
| + for (auto& header : headers_) {
|
| + gfx::Rect header_bounds = header.view->bounds();
|
| + if (scroll_offset > header.offset) {
|
| + header_bounds.set_y(scroll_offset);
|
| + header.view->SetBoundsRect(header_bounds);
|
| + ShowHeaderSticky(header.view, true);
|
| + header.view->Layout();
|
| + header.view->SchedulePaint();
|
| + if (previous_header) {
|
| + header_bounds = previous_header->view->bounds();
|
| + header_bounds.set_y(previous_header->offset);
|
| + previous_header->view->SetBoundsRect(header_bounds);
|
| + ShowHeaderSticky(previous_header->view, false);
|
| + }
|
| + previous_header = &header;
|
| + } else if (previous_header &&
|
| + header_bounds.y() < previous_header->view->bounds().bottom()) {
|
| + gfx::Rect previous_header_bounds = previous_header->view->bounds();
|
| + previous_header_bounds.set_y(header_bounds.y() -
|
| + previous_header->view->bounds().height());
|
| + previous_header->view->SetBoundsRect(previous_header_bounds);
|
| + ShowHeaderSticky(previous_header->view, false);
|
| + ShowHeaderSticky(header.view, false);
|
| + } else {
|
| + ShowHeaderSticky(header.view, false);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Header child views that stick to the top of visible viewport when scrolled.
|
| + std::vector<Header> headers_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScrollContentsView);
|
| +};
|
| +
|
| // Constants for the title row in material design.
|
| const int kTitleRowVerticalPadding = 4;
|
| const int kTitleRowSeparatorBorderHeight = 1;
|
| @@ -74,17 +203,22 @@ class ScrollSeparator : public views::View {
|
| ~ScrollSeparator() override {}
|
|
|
| private:
|
| - // Overriden from views::View.
|
| + // views::View.
|
| void OnPaint(gfx::Canvas* canvas) override {
|
| - canvas->FillRect(gfx::Rect(0, height() / 2, width(), 1), kBorderLightColor);
|
| + canvas->FillRect(gfx::Rect(0, height() / 2, width(), 1),
|
| + ash::kBorderLightColor);
|
| }
|
| gfx::Size GetPreferredSize() const override {
|
| - return gfx::Size(1, kTrayPopupScrollSeparatorHeight);
|
| + return gfx::Size(1, ash::kTrayPopupScrollSeparatorHeight);
|
| }
|
|
|
| DISALLOW_COPY_AND_ASSIGN(ScrollSeparator);
|
| };
|
|
|
| +} // namespace
|
| +
|
| +namespace ash {
|
| +
|
| class ScrollBorder : public views::Border {
|
| public:
|
| ScrollBorder() {}
|
| @@ -93,7 +227,7 @@ class ScrollBorder : public views::Border {
|
| void set_visible(bool visible) { visible_ = visible; }
|
|
|
| private:
|
| - // Overridden from views::Border.
|
| + // views::Border.
|
| void Paint(const views::View& view, gfx::Canvas* canvas) override {
|
| if (!visible_)
|
| return;
|
| @@ -105,7 +239,7 @@ class ScrollBorder : public views::Border {
|
|
|
| gfx::Size GetMinimumSize() const override { return gfx::Size(0, 1); }
|
|
|
| - bool visible_;
|
| + bool visible_ = false;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(ScrollBorder);
|
| };
|
| @@ -181,7 +315,7 @@ void TrayDetailsView::CreateTitleRow(int string_id) {
|
|
|
| void TrayDetailsView::CreateScrollableList() {
|
| DCHECK(!scroller_);
|
| - scroll_content_ = new views::View;
|
| + scroll_content_ = new ScrollContentsView(this);
|
| scroll_content_->SetLayoutManager(
|
| new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
|
| scroller_ = new FixedSizedScrollView;
|
|
|