Commit 9536741c authored by David Black's avatar David Black Committed by Commit Bot

Pulls UiElementContainerView out of AssistantBubbleView.

AssistantBubbleView should be concerned with choreography of its child
views, not their specific implementations.

Will also make it easier to unit test view classes in isolation.

Note that this CL also creates a constants file for Assistant UI.

Bug: b:80245607
Change-Id: I05be99e93383c5df61c279a54b811a03a8a3cb29
Reviewed-on: https://chromium-review.googlesource.com/1072147Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Commit-Queue: David Black <dmblack@google.com>
Cr-Commit-Position: refs/heads/master@{#562570}
parent c4bbe637
...@@ -107,6 +107,7 @@ component("ash") { ...@@ -107,6 +107,7 @@ component("ash") {
"assistant/ui/assistant_bubble.h", "assistant/ui/assistant_bubble.h",
"assistant/ui/assistant_bubble_view.cc", "assistant/ui/assistant_bubble_view.cc",
"assistant/ui/assistant_bubble_view.h", "assistant/ui/assistant_bubble_view.h",
"assistant/ui/assistant_ui_constants.h",
"assistant/ui/caption_bar.cc", "assistant/ui/caption_bar.cc",
"assistant/ui/caption_bar.h", "assistant/ui/caption_bar.h",
"assistant/ui/dialog_plate/action_view.cc", "assistant/ui/dialog_plate/action_view.cc",
...@@ -117,6 +118,8 @@ component("ash") { ...@@ -117,6 +118,8 @@ component("ash") {
"assistant/ui/logo_view/base_logo_view.h", "assistant/ui/logo_view/base_logo_view.h",
"assistant/ui/suggestion_container_view.cc", "assistant/ui/suggestion_container_view.cc",
"assistant/ui/suggestion_container_view.h", "assistant/ui/suggestion_container_view.h",
"assistant/ui/ui_element_container_view.cc",
"assistant/ui/ui_element_container_view.h",
"autoclick/autoclick_controller.cc", "autoclick/autoclick_controller.cc",
"autoclick/autoclick_controller.h", "autoclick/autoclick_controller.h",
"cancel_mode.cc", "cancel_mode.cc",
......
...@@ -47,7 +47,7 @@ class AssistantCardElement : public AssistantUiElement { ...@@ -47,7 +47,7 @@ class AssistantCardElement : public AssistantUiElement {
~AssistantCardElement() override = default; ~AssistantCardElement() override = default;
const std::string& GetHtml() const { return html_; } const std::string& html() const { return html_; }
private: private:
const std::string html_; const std::string html_;
...@@ -65,7 +65,7 @@ class AssistantTextElement : public AssistantUiElement { ...@@ -65,7 +65,7 @@ class AssistantTextElement : public AssistantUiElement {
~AssistantTextElement() override = default; ~AssistantTextElement() override = default;
const std::string& GetText() const { return text_; } const std::string& text() const { return text_; }
private: private:
const std::string text_; const std::string text_;
......
This diff is collapsed.
...@@ -5,11 +5,6 @@ ...@@ -5,11 +5,6 @@
#ifndef ASH_ASSISTANT_UI_ASSISTANT_BUBBLE_VIEW_H_ #ifndef ASH_ASSISTANT_UI_ASSISTANT_BUBBLE_VIEW_H_
#define ASH_ASSISTANT_UI_ASSISTANT_BUBBLE_VIEW_H_ #define ASH_ASSISTANT_UI_ASSISTANT_BUBBLE_VIEW_H_
#include <deque>
#include <memory>
#include <string>
#include <vector>
#include "ash/assistant/model/assistant_interaction_model_observer.h" #include "ash/assistant/model/assistant_interaction_model_observer.h"
#include "base/macros.h" #include "base/macros.h"
#include "ui/views/view.h" #include "ui/views/view.h"
...@@ -20,16 +15,13 @@ class BoxLayout; ...@@ -20,16 +15,13 @@ class BoxLayout;
namespace ash { namespace ash {
class AssistantCardElement;
class AssistantController; class AssistantController;
class AssistantTextElement;
class AssistantUiElement;
class DialogPlate; class DialogPlate;
class SuggestionContainerView; class SuggestionContainerView;
class UiElementContainerView;
namespace { namespace {
class InteractionContainer; class InteractionContainer;
class UiElementContainer;
} // namespace } // namespace
class AssistantBubbleView : public views::View, class AssistantBubbleView : public views::View,
...@@ -46,49 +38,22 @@ class AssistantBubbleView : public views::View, ...@@ -46,49 +38,22 @@ class AssistantBubbleView : public views::View,
// AssistantInteractionModelObserver: // AssistantInteractionModelObserver:
void OnInputModalityChanged(InputModality input_modality) override; void OnInputModalityChanged(InputModality input_modality) override;
void OnUiElementAdded(const AssistantUiElement* ui_element) override;
void OnUiElementsCleared() override;
void OnQueryChanged(const AssistantQuery& query) override; void OnQueryChanged(const AssistantQuery& query) override;
void OnQueryCleared() override; void OnQueryCleared() override;
private: private:
void InitLayout(); void InitLayout();
// Assistant cards are rendered asynchronously before being added to the view AssistantController* const assistant_controller_; // Owned by Shell.
// hierarchy. For this reason, it is necessary to pend any UI elements that
// arrive between the time a render request is sent and the time at which the
// view is finally embedded. Failure to do so could result in a mismatch
// between the ordering of UI elements received and their corresponding views.
void SetProcessingUiElement(bool is_processing);
void ProcessPendingUiElements();
void OnCardAdded(const AssistantCardElement* card_element);
void OnCardReady(const base::UnguessableToken& embed_token);
void OnReleaseCards();
void OnTextAdded(const AssistantTextElement* text_element);
AssistantController* assistant_controller_; // Owned by Shell.
views::View* caption_bar_; // Owned by view hierarchy. views::View* caption_bar_; // Owned by view hierarchy.
InteractionContainer* interaction_container_; // Owned by view hierarchy. InteractionContainer* interaction_container_; // Owned by view hierarchy.
UiElementContainer* ui_element_container_; // Owned by view hierarchy. UiElementContainerView* ui_element_container_; // Owned by view hierarchy.
SuggestionContainerView* suggestions_container_; // Owned by view hierarchy. SuggestionContainerView* suggestions_container_; // Owned by view hierarchy.
DialogPlate* dialog_plate_; // Owned by view hierarchy. DialogPlate* dialog_plate_; // Owned by view hierarchy.
views::BoxLayout* layout_manager_ = nullptr; // Owned by view hierarchy. views::BoxLayout* layout_manager_ = nullptr; // Owned by view hierarchy.
// Uniquely identifies cards owned by AssistantCardRenderer.
std::vector<base::UnguessableToken> id_token_list_;
// Owned by AssistantInteractionModel.
std::deque<const AssistantUiElement*> pending_ui_element_list_;
// Whether a UI element is currently being processed. If true, new UI elements
// are added to |pending_ui_element_list_| and processed later.
bool is_processing_ui_element_ = false;
// Weak pointer factory used for card rendering requests.
base::WeakPtrFactory<AssistantBubbleView> render_request_weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AssistantBubbleView); DISALLOW_COPY_AND_ASSIGN(AssistantBubbleView);
}; };
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_ASSISTANT_UI_ASSISTANT_UI_CONSTANTS_H_
#define ASH_ASSISTANT_UI_ASSISTANT_UI_CONSTANTS_H_
#include "third_party/skia/include/core/SkColor.h"
namespace ash {
// Appearance.
constexpr int kPaddingDip = 14;
constexpr int kPreferredWidthDip = 640;
constexpr int kSpacingDip = 8;
// Typography.
constexpr SkColor kTextColorHint = SkColorSetA(SK_ColorBLACK, 0x42);
constexpr SkColor kTextColorPrimary = SkColorSetA(SK_ColorBLACK, 0xDE);
} // namespace ash
#endif // ASH_ASSISTANT_UI_ASSISTANT_UI_CONSTANTS_H_
...@@ -15,6 +15,11 @@ namespace ash { ...@@ -15,6 +15,11 @@ namespace ash {
class AssistantController; class AssistantController;
// DialogPlate is the child of AssistantBubbleView concerned with providing the
// means by which a user converses with Assistant. To this end, DialogPlate
// provides a textfield for use with the keyboard input modality, and an
// ActionView which serves to either commit a text query, or toggle voice
// interaction as appropriate for the user's current input modality.
class DialogPlate : public views::View, class DialogPlate : public views::View,
public views::TextfieldController, public views::TextfieldController,
public ActionViewListener, public ActionViewListener,
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "ash/assistant/assistant_controller.h" #include "ash/assistant/assistant_controller.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "ui/views/controls/scrollbar/overlay_scroll_bar.h" #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
...@@ -15,11 +16,8 @@ namespace ash { ...@@ -15,11 +16,8 @@ namespace ash {
namespace { namespace {
// TODO(dmblack): Move common dimensions to shared constant file.
// Appearance. // Appearance.
constexpr int kPaddingDip = 14;
constexpr int kPreferredHeightDip = 48; constexpr int kPreferredHeightDip = 48;
constexpr int kSpacingDip = 8;
// InvisibleScrollBar ---------------------------------------------------------- // InvisibleScrollBar ----------------------------------------------------------
......
...@@ -16,6 +16,9 @@ namespace ash { ...@@ -16,6 +16,9 @@ namespace ash {
class AssistantController; class AssistantController;
// SuggestionContainerView is the child of AssistantBubbleView concerned with
// laying out SuggestionChipViews in response to Assistant interaction model
// suggestion events.
class SuggestionContainerView : public views::ScrollView, class SuggestionContainerView : public views::ScrollView,
public AssistantInteractionModelObserver, public AssistantInteractionModelObserver,
public app_list::SuggestionChipListener { public app_list::SuggestionChipListener {
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/assistant/ui/ui_element_container_view.h"
#include "ash/assistant/assistant_controller.h"
#include "ash/assistant/model/assistant_ui_element.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/public/cpp/app_list/answer_card_contents_registry.h"
#include "base/callback.h"
#include "base/strings/utf_string_conversions.h"
#include "base/unguessable_token.h"
#include "ui/gfx/canvas.h"
#include "ui/views/background.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// Appearance.
constexpr SkColor kTextBackgroundColor = SkColorSetARGB(0x8A, 0x42, 0x85, 0xF4);
constexpr int kTextCornerRadiusDip = 16;
constexpr int kTextPaddingHorizontalDip = 12;
constexpr int kTextPaddingVerticalDip = 4;
// RoundRectBackground ---------------------------------------------------------
class RoundRectBackground : public views::Background {
public:
RoundRectBackground(SkColor color, int corner_radius)
: color_(color), corner_radius_(corner_radius) {}
~RoundRectBackground() override = default;
// views::Background:
void Paint(gfx::Canvas* canvas, views::View* view) const override {
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setColor(color_);
canvas->DrawRoundRect(view->GetContentsBounds(), corner_radius_, flags);
}
private:
const SkColor color_;
const int corner_radius_;
DISALLOW_COPY_AND_ASSIGN(RoundRectBackground);
};
} // namespace
// UiElementContainerView ------------------------------------------------------
UiElementContainerView::UiElementContainerView(
AssistantController* assistant_controller)
: assistant_controller_(assistant_controller),
render_request_weak_factory_(this) {
InitLayout();
// The Assistant controller indirectly owns the view hierarchy to which
// UiElementContainerView belongs so is guaranteed to outlive it.
assistant_controller_->AddInteractionModelObserver(this);
}
UiElementContainerView::~UiElementContainerView() {
assistant_controller_->RemoveInteractionModelObserver(this);
ReleaseAllCards();
}
void UiElementContainerView::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged();
}
void UiElementContainerView::InitLayout() {
views::BoxLayout* layout_manager =
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(0, kPaddingDip),
kSpacingDip));
layout_manager->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_START);
}
void UiElementContainerView::OnUiElementAdded(
const AssistantUiElement* ui_element) {
// If we are processing a UI element we need to pend the incoming element
// instead of handling it immediately.
if (is_processing_ui_element_) {
pending_ui_element_list_.push_back(ui_element);
return;
}
switch (ui_element->GetType()) {
case AssistantUiElementType::kCard:
OnCardElementAdded(static_cast<const AssistantCardElement*>(ui_element));
break;
case AssistantUiElementType::kText:
OnTextElementAdded(static_cast<const AssistantTextElement*>(ui_element));
break;
}
}
void UiElementContainerView::OnUiElementsCleared() {
// Prevent any in-flight card rendering requests from returning.
render_request_weak_factory_.InvalidateWeakPtrs();
SetVisible(false);
RemoveAllChildViews(/*delete_children=*/true);
PreferredSizeChanged();
ReleaseAllCards();
// We can clear any pending UI elements as they are no longer relevant.
pending_ui_element_list_.clear();
SetProcessingUiElement(false);
}
void UiElementContainerView::OnCardElementAdded(
const AssistantCardElement* card_element) {
DCHECK(!is_processing_ui_element_);
// We need to pend any further UI elements until the card has been rendered.
// This insures that views will be added to the view hierarchy in the order in
// which they were received.
SetProcessingUiElement(true);
// Generate a unique identifier for the card. This will be used to clean up
// card resources when it is no longer needed.
base::UnguessableToken id_token = base::UnguessableToken::Create();
// Configure parameters for the card.
ash::mojom::AssistantCardParamsPtr params(
ash::mojom::AssistantCardParams::New());
params->html = card_element->html();
params->min_width_dip = kPreferredWidthDip - 2 * kPaddingDip;
params->max_width_dip = kPreferredWidthDip - 2 * kPaddingDip;
// The card will be rendered by AssistantCardRenderer, running the specified
// callback when the card is ready for embedding.
assistant_controller_->RenderCard(
id_token, std::move(params),
base::BindOnce(&UiElementContainerView::OnCardReady,
render_request_weak_factory_.GetWeakPtr()));
// Cache the card identifier for freeing up resources when it is no longer
// needed.
id_token_list_.push_back(id_token);
}
void UiElementContainerView::OnCardReady(
const base::UnguessableToken& embed_token) {
// When the card has been rendered in the same process, its view is
// available in the AnswerCardContentsRegistry's token-to-view map.
if (app_list::AnswerCardContentsRegistry::Get()) {
AddChildView(
app_list::AnswerCardContentsRegistry::Get()->GetView(embed_token));
}
// TODO(dmblack): Handle Mash case.
SetVisible(true);
// Once the card has been rendered and embedded, we can resume processing
// any UI elements that are in the pending queue.
SetProcessingUiElement(false);
}
void UiElementContainerView::OnTextElementAdded(
const AssistantTextElement* text_element) {
DCHECK(!is_processing_ui_element_);
// Container.
views::View* text_container = new views::View();
text_container->SetBackground(std::make_unique<RoundRectBackground>(
kTextBackgroundColor, kTextCornerRadiusDip));
text_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(kTextPaddingVerticalDip, kTextPaddingHorizontalDip)));
// Label.
views::Label* text_view =
new views::Label(base::UTF8ToUTF16(text_element->text()));
text_view->SetAutoColorReadabilityEnabled(false);
text_view->SetEnabledColor(kTextColorPrimary);
text_view->SetFontList(text_view->font_list().DeriveWithSizeDelta(4));
text_view->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
text_view->SetMultiLine(true);
text_container->AddChildView(text_view);
AddChildView(text_container);
SetVisible(true);
PreferredSizeChanged();
}
void UiElementContainerView::SetProcessingUiElement(bool is_processing) {
if (is_processing == is_processing_ui_element_)
return;
is_processing_ui_element_ = is_processing;
// If we are no longer processing a UI element, we need to handle anything
// that was put in the pending queue. Note that the elements left in the
// pending queue may themselves require processing that again pends the queue.
if (!is_processing_ui_element_)
ProcessPendingUiElements();
}
void UiElementContainerView::ProcessPendingUiElements() {
while (!is_processing_ui_element_ && !pending_ui_element_list_.empty()) {
const AssistantUiElement* ui_element = pending_ui_element_list_.front();
pending_ui_element_list_.pop_front();
OnUiElementAdded(ui_element);
}
}
void UiElementContainerView::ReleaseAllCards() {
if (id_token_list_.empty())
return;
// Release any resources associated with the cards identified in
// |id_token_list_| owned by AssistantCardRenderer.
assistant_controller_->ReleaseCards(id_token_list_);
id_token_list_.clear();
}
} // namespace ash
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_ASSISTANT_UI_UI_ELEMENT_CONTAINER_VIEW_H_
#define ASH_ASSISTANT_UI_UI_ELEMENT_CONTAINER_VIEW_H_
#include <deque>
#include <memory>
#include <vector>
#include "ash/assistant/model/assistant_interaction_model_observer.h"
#include "base/macros.h"
#include "ui/views/view.h"
namespace ash {
class AssistantCardElement;
class AssistantController;
class AssistantTextElement;
class AssistantUiElement;
// UiElementContainerView is the child of AssistantBubbleView concerned with
// laying out text views and embedded card views in response to Assistant
// interaction model UI element events.
class UiElementContainerView : public views::View,
public AssistantInteractionModelObserver {
public:
explicit UiElementContainerView(AssistantController* assistant_controller);
~UiElementContainerView() override;
// views::View:
void ChildPreferredSizeChanged(views::View* child) override;
// AssistantInteractionModelObserver:
void OnUiElementAdded(const AssistantUiElement* ui_element) override;
void OnUiElementsCleared() override;
private:
void InitLayout();
void OnCardElementAdded(const AssistantCardElement* card_element);
void OnCardReady(const base::UnguessableToken& embed_token);
void OnTextElementAdded(const AssistantTextElement* text_element);
// Assistant cards are rendered asynchronously before being added to the view
// hierarchy. For this reason, it is necessary to pend any UI elements that
// arrive between the time a render request is sent and the time at which the
// view is finally embedded. Failure to do so could result in a mismatch
// between the ordering of UI elements received and their corresponding views.
void SetProcessingUiElement(bool is_processing);
void ProcessPendingUiElements();
void ReleaseAllCards();
AssistantController* const assistant_controller_; // Owned by Shell.
// Uniquely identifies cards owned by AssistantCardRenderer.
std::vector<base::UnguessableToken> id_token_list_;
// Owned by AssistantInteractionModel.
std::deque<const AssistantUiElement*> pending_ui_element_list_;
// Whether a UI element is currently being processed. If true, new UI elements
// are added to |pending_ui_element_list_| and processed later.
bool is_processing_ui_element_ = false;
// Weak pointer factory used for card rendering requests.
base::WeakPtrFactory<UiElementContainerView> render_request_weak_factory_;
DISALLOW_COPY_AND_ASSIGN(UiElementContainerView);
};
} // namespace ash
#endif // ASH_ASSISTANT_UI_UI_ELEMENT_CONTAINER_VIEW_H_
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