Commit e12533a6 authored by David Black's avatar David Black Committed by Commit Bot

Add scroll indication to Assistant's UiElementContainerView.

Scroll indicator is drawn over UiElementContainerView's empty bottom
border to work around issue of drawing over top of Assistant cards.

Visibility of the scroll indicator view is synced to vertical scroll
bar update/visibility events.

Bug: b:112669597
Change-Id: Ib8ae57428bb827913957fdd92168868c181ab4d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2024285Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Commit-Queue: David Black <dmblack@google.com>
Cr-Commit-Position: refs/heads/master@{#736437}
parent 91e0f47e
......@@ -10,6 +10,7 @@ include_rules = [
"+ash/strings",
"+base",
"+build/buildflag.h",
"+cc/base",
"+cc/paint",
"+chromeos/assistant",
"+chromeos/services/assistant/public",
......
......@@ -47,15 +47,32 @@ class ContentView : public views::View, views::ViewObserver {
class InvisibleScrollBar : public views::OverlayScrollBar {
public:
explicit InvisibleScrollBar(bool horizontal)
: views::OverlayScrollBar(horizontal) {}
InvisibleScrollBar(AssistantScrollView* parent, bool horizontal)
: views::OverlayScrollBar(horizontal), parent_(parent) {}
~InvisibleScrollBar() override = default;
// views::OverlayScrollBar:
int GetThickness() const override { return 0; }
void Update(int viewport_size,
int content_size,
int content_scroll_offset) override {
views::OverlayScrollBar::Update(viewport_size, content_size,
content_scroll_offset);
parent_->OnScrollBarUpdated(this, viewport_size, content_size,
content_scroll_offset);
}
void VisibilityChanged(views::View* starting_from, bool is_visible) override {
if (starting_from == this)
parent_->OnScrollBarVisibilityChanged(this, is_visible);
}
private:
AssistantScrollView* const parent_; // Owned by view hierarchy, owns |this|.
DISALLOW_COPY_AND_ASSIGN(InvisibleScrollBar);
};
......@@ -89,10 +106,10 @@ void AssistantScrollView::InitLayout() {
// Scroll bars.
horizontal_scroll_bar_ = SetHorizontalScrollBar(
std::make_unique<InvisibleScrollBar>(/*horizontal=*/true));
std::make_unique<InvisibleScrollBar>(this, /*horizontal=*/true));
vertical_scroll_bar_ = SetVerticalScrollBar(
std::make_unique<InvisibleScrollBar>(/*horizontal=*/false));
std::make_unique<InvisibleScrollBar>(this, /*horizontal=*/false));
}
} // namespace ash
......@@ -27,12 +27,19 @@ class COMPONENT_EXPORT(ASSISTANT_UI) AssistantScrollView
virtual void OnContentsPreferredSizeChanged(views::View* content_view) = 0;
virtual void OnScrollBarUpdated(views::ScrollBar* scroll_bar,
int viewport_size,
int content_size,
int content_scroll_offset) {}
virtual void OnScrollBarVisibilityChanged(views::ScrollBar* scroll_bar,
bool is_visible) {}
protected:
views::View* content_view() { return content_view_; }
const views::View* content_view() const { return content_view_; }
views::ScrollBar* horizontal_scroll_bar() { return horizontal_scroll_bar_; }
views::ScrollBar* vertical_scroll_bar() { return vertical_scroll_bar_; }
private:
......
......@@ -20,7 +20,9 @@
#include "ash/public/cpp/app_list/app_list_features.h"
#include "base/callback.h"
#include "base/time/time.h"
#include "cc/base/math_util.h"
#include "ui/aura/window.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/layout/box_layout.h"
......@@ -33,6 +35,7 @@ constexpr int kEmbeddedUiFirstCardMarginTopDip = 8;
constexpr int kEmbeddedUiPaddingBottomDip = 8;
constexpr int kMainUiFirstCardMarginTopDip = 40;
constexpr int kMainUiPaddingBottomDip = 24;
constexpr int kScrollIndicatorHeightDip = 1;
// Helpers ---------------------------------------------------------------------
......@@ -86,6 +89,14 @@ gfx::Size UiElementContainerView::GetMinimumSize() const {
return gfx::Size(INT_MAX, 1);
}
void UiElementContainerView::Layout() {
AnimatedContainerView::Layout();
// Scroll indicator.
scroll_indicator_->SetBounds(0, height() - kScrollIndicatorHeightDip, width(),
kScrollIndicatorHeightDip);
}
void UiElementContainerView::OnContentsPreferredSizeChanged(
views::View* content_view) {
const int preferred_height = content_view->GetHeightForWidth(width());
......@@ -93,11 +104,33 @@ void UiElementContainerView::OnContentsPreferredSizeChanged(
}
void UiElementContainerView::InitLayout() {
// Content.
content_view()->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
gfx::Insets(0, kUiElementHorizontalMarginDip, GetPaddingBottomDip(),
kUiElementHorizontalMarginDip),
kSpacingDip));
// Scroll indicator.
scroll_indicator_ = AddChildView(std::make_unique<views::View>());
scroll_indicator_->SetBackground(
views::CreateSolidBackground(gfx::kGoogleGrey300));
// The scroll indicator paints to its own layer which is animated in/out using
// implicit animation settings.
scroll_indicator_->SetPaintToLayer();
scroll_indicator_->layer()->SetAnimator(
ui::LayerAnimator::CreateImplicitAnimator());
scroll_indicator_->layer()->SetFillsBoundsOpaquely(false);
scroll_indicator_->layer()->SetOpacity(0.f);
// We cannot draw |scroll_indicator_| over Assistant cards due to issues w/
// layer ordering. Because |kScrollIndicatorHeightDip| is sufficiently small,
// we'll use an empty bottom border to reserve space for |scroll_indicator_|.
// When |scroll_indicator_| is not visible, this just adds a negligible amount
// of margin to the bottom of the content. Otherwise, |scroll_indicator_| will
// occupy this space.
SetBorder(views::CreateEmptyBorder(0, 0, kScrollIndicatorHeightDip, 0));
}
void UiElementContainerView::OnCommittedQueryChanged(
......@@ -176,4 +209,34 @@ void UiElementContainerView::OnAllViewsAnimatedIn() {
NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
}
void UiElementContainerView::OnScrollBarUpdated(views::ScrollBar* scroll_bar,
int viewport_size,
int content_size,
int content_scroll_offset) {
if (scroll_bar != vertical_scroll_bar())
return;
// When the vertical scroll bar is updated, we update our |scroll_indicator_|.
bool can_scroll = content_size > (content_scroll_offset + viewport_size);
UpdateScrollIndicator(can_scroll);
}
void UiElementContainerView::OnScrollBarVisibilityChanged(
views::ScrollBar* scroll_bar,
bool is_visible) {
// When the vertical scroll bar is hidden, we need to update our
// |scroll_indicator_|. This may occur during a layout pass when the new
// content no longer requires a vertical scroll bar while the old content did.
if (scroll_bar == vertical_scroll_bar() && !is_visible)
UpdateScrollIndicator(/*can_scroll=*/false);
}
void UiElementContainerView::UpdateScrollIndicator(bool can_scroll) {
const float target_opacity = can_scroll ? 1.f : 0.f;
ui::Layer* layer = scroll_indicator_->layer();
if (!cc::MathUtil::IsWithinEpsilon(layer->GetTargetOpacity(), target_opacity))
layer->SetOpacity(target_opacity);
}
} // namespace ash
......@@ -37,6 +37,7 @@ class COMPONENT_EXPORT(ASSISTANT_UI) UiElementContainerView
gfx::Size CalculatePreferredSize() const override;
int GetHeightForWidth(int width) const override;
gfx::Size GetMinimumSize() const override;
void Layout() override;
void OnContentsPreferredSizeChanged(views::View* content_view) override;
void OnCommittedQueryChanged(const AssistantQuery& query) override;
......@@ -47,9 +48,19 @@ class COMPONENT_EXPORT(ASSISTANT_UI) UiElementContainerView
void HandleResponse(const AssistantResponse& response) override;
void OnAllViewsRemoved() override;
void OnAllViewsAnimatedIn() override;
void OnScrollBarUpdated(views::ScrollBar* scroll_bar,
int viewport_size,
int content_size,
int content_scroll_offset) override;
void OnScrollBarVisibilityChanged(views::ScrollBar* scroll_bar,
bool is_visible) override;
void OnCardElementAdded(const AssistantCardElement* card_element);
void UpdateScrollIndicator(bool can_scroll);
views::View* scroll_indicator_ = nullptr; // Owned by view hierarchy.
// Factory instance used to construct views for modeled UI elements.
std::unique_ptr<AssistantUiElementViewFactory> view_factory_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment