Reland: "Show an error message on fetch error for classroom glanceables"
This is a reland of commit a29a268694
The error message changed to be updated to spec: "Can't show items. Try
reopening this panel."
Original change's description:
> Show an error message on fetch error for classroom glanceables
>
> If an error occurs while the glanceables classroom client attempts to
> update the data on the bubble, show an error message 'Failed to reload
> the data'
>
> Bug: b:293640451
> Change-Id: I201cd38a34b8ad3804dba609a77b207da70ad976
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4750560
> Reviewed-by: Toni Barzic <tbarzic@chromium.org>
> Commit-Queue: Ana Salazar <anasalazar@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1192648}
Bug: b:293640451
Change-Id: I1f729e8fbe5d07a43f8b6cce8a7cd2c8d2e3a01f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4846540
Reviewed-by: Toni Barzic <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1193340}
This commit is contained in:
ash
@ -6996,6 +6996,9 @@ New install
|
||||
<message name="IDS_GLANCEABLES_TASKS_HEADER_ICON_ACCESSIBLE_NAME" desc="The glanceable displays tasks items fetched from Google Tasks API. This is the a11y name announced by ChromeVox for the top left corner header icon that opens Google Calendar website with the companion app panel open.">
|
||||
Google tasks on web
|
||||
</message>
|
||||
<message name="IDS_GLANCEABLES_CLASSROOM_FETCH_ERROR" desc="A glanceables classroom bubble displays a notification with an error message when an error occurs while fetching data. This is the text that describes the error to the user and appears on the notification.">
|
||||
Can't show items. Try reopening this panel.
|
||||
</message>
|
||||
|
||||
<!-- Do Not Disturb notification -->
|
||||
<message name="IDS_ASH_DO_NOT_DISTURB_NOTIFICATION_TITLE" desc="Label used for the notification that shows up when the 'Do Not Disturb' feature is enabled.">
|
||||
|
@ -0,0 +1 @@
|
||||
d3180d72d36ae97bc52279426de81caa8da9a2ac
|
@ -1304,6 +1304,11 @@ BASE_FEATURE(kGlanceablesV2ClassroomTeacherView,
|
||||
"GlanceablesV2ClassroomTeacherView",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// Enables showing error messages for glanceables bubbles.
|
||||
BASE_FEATURE(kGlanceablesV2ErrorMessage,
|
||||
"GlanceablesV2ErrorMessage",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// Enables the Gaia reauth endpoint for all online reauth flows on login screen.
|
||||
// Note that the reauth endpoint is used when the user is a child user or in
|
||||
// potential recovery flows, regardless of the flag value.
|
||||
@ -3388,6 +3393,10 @@ bool IsGlanceablesV2ClassroomTeacherViewEnabled() {
|
||||
return base::FeatureList::IsEnabled(kGlanceablesV2ClassroomTeacherView);
|
||||
}
|
||||
|
||||
bool IsGlanceablesV2ErrorMessageEnabled() {
|
||||
return base::FeatureList::IsEnabled(kGlanceablesV2ErrorMessage);
|
||||
}
|
||||
|
||||
bool IsHibernateEnabled() {
|
||||
return base::FeatureList::IsEnabled(kHibernate);
|
||||
}
|
||||
|
@ -400,6 +400,8 @@ COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
BASE_DECLARE_FEATURE(kGlanceablesV2TrustedTesters);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
BASE_DECLARE_FEATURE(kGlanceablesV2ClassroomTeacherView);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
BASE_DECLARE_FEATURE(kGlanceablesV2ErrorMessage);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kGaiaReauthEndpoint);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kGameDashboard);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
@ -964,6 +966,8 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool AreGlanceablesV2Enabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool AreGlanceablesV2EnabledForTrustedTesters();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
bool IsGlanceablesV2ClassroomTeacherViewEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
bool IsGlanceablesV2ErrorMessageEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHibernateEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideArcMediaNotificationsEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideShelfControlsInTabletModeEnabled();
|
||||
|
@ -29,8 +29,9 @@
|
||||
namespace {
|
||||
|
||||
constexpr int kErrorMessageViewSize = 40;
|
||||
constexpr int kErrorMessageHorizontalMargin = 16;
|
||||
constexpr int kErrorMessageBottomMargin = 12;
|
||||
constexpr gfx::Insets kButtonInsets = gfx::Insets::TLBR(10, 0, 10, 16);
|
||||
constexpr gfx::Insets kContainerInsets = gfx::Insets::TLBR(0, 0, 12, 0);
|
||||
constexpr gfx::Insets kLabelInsets = gfx::Insets::VH(10, 16);
|
||||
|
||||
} // namespace
|
||||
@ -59,7 +60,6 @@ GlanceablesErrorMessageView::GlanceablesErrorMessageView(
|
||||
const std::u16string& error_message) {
|
||||
SetBackground(views::CreateThemedRoundedRectBackground(
|
||||
cros_tokens::kCrosSysError, kErrorMessageViewSize / 2));
|
||||
SetProperty(views::kMarginsKey, kContainerInsets);
|
||||
|
||||
const auto* const typography_provider = TypographyProvider::Get();
|
||||
|
||||
@ -86,6 +86,19 @@ GlanceablesErrorMessageView::GlanceablesErrorMessageView(
|
||||
std::make_unique<DismissErrorLabelButton>(std::move(callback)));
|
||||
}
|
||||
|
||||
void GlanceablesErrorMessageView::UpdateBoundsToContainer(
|
||||
const gfx::Rect& container_bounds) {
|
||||
gfx::Rect preferred_bounds(container_bounds);
|
||||
|
||||
preferred_bounds.Inset(gfx::Insets::TLBR(
|
||||
preferred_bounds.height() - kErrorMessageViewSize -
|
||||
kErrorMessageBottomMargin,
|
||||
kErrorMessageHorizontalMargin, kErrorMessageBottomMargin,
|
||||
kErrorMessageHorizontalMargin));
|
||||
|
||||
SetBoundsRect(preferred_bounds);
|
||||
}
|
||||
|
||||
BEGIN_METADATA(GlanceablesErrorMessageView, views::View)
|
||||
END_METADATA
|
||||
|
||||
|
@ -33,6 +33,10 @@ class ASH_EXPORT GlanceablesErrorMessageView : public views::FlexLayoutView {
|
||||
delete;
|
||||
~GlanceablesErrorMessageView() override = default;
|
||||
|
||||
// Updates the error message view to display proportionally to the given
|
||||
// `container_bounds`.
|
||||
void UpdateBoundsToContainer(const gfx::Rect& container_bounds);
|
||||
|
||||
private:
|
||||
raw_ptr<views::Label> error_message_label_ = nullptr;
|
||||
raw_ptr<views::LabelButton> dismiss_button_ = nullptr;
|
||||
|
@ -48,6 +48,7 @@ enum class GlanceablesViewId {
|
||||
// `GlanceablesErrorMessageView`
|
||||
kGlanceablesErrorMessageLabel,
|
||||
kGlanceablesErrorMessageButton,
|
||||
kGlanceablesErrorMessageView,
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/glanceables/classroom/glanceables_classroom_item_view.h"
|
||||
#include "ash/glanceables/classroom/glanceables_classroom_types.h"
|
||||
#include "ash/glanceables/common/glanceables_error_message_view.h"
|
||||
#include "ash/glanceables/common/glanceables_list_footer_view.h"
|
||||
#include "ash/glanceables/common/glanceables_progress_bar_view.h"
|
||||
#include "ash/glanceables/common/glanceables_view_id.h"
|
||||
@ -53,9 +55,8 @@ ClassroomBubbleBaseView::ClassroomBubbleBaseView(
|
||||
DetailedViewDelegate* delegate,
|
||||
std::unique_ptr<ui::ComboboxModel> combobox_model)
|
||||
: GlanceableTrayChildBubble(delegate, /*for_glanceables_container=*/true) {
|
||||
auto* layout_manager =
|
||||
SetLayoutManager(std::make_unique<views::FlexLayout>());
|
||||
layout_manager
|
||||
layout_manager_ = SetLayoutManager(std::make_unique<views::FlexLayout>());
|
||||
layout_manager_
|
||||
->SetInteriorMargin(gfx::Insets::TLBR(kInteriorGlanceableBubbleMargin,
|
||||
kInteriorGlanceableBubbleMargin, 0,
|
||||
kInteriorGlanceableBubbleMargin))
|
||||
@ -204,6 +205,19 @@ void ClassroomBubbleBaseView::OnGetAssignments(
|
||||
|
||||
list_shown_start_time_ = base::TimeTicks::Now();
|
||||
first_assignment_list_shown_ = true;
|
||||
|
||||
if (features::IsGlanceablesV2ErrorMessageEnabled()) {
|
||||
if (success) {
|
||||
MaybeDismissErrorMessage();
|
||||
} else {
|
||||
ShowErrorMessage(
|
||||
l10n_util::GetStringUTF16(IDS_GLANCEABLES_CLASSROOM_FETCH_ERROR));
|
||||
|
||||
// Explicitly signal to the layout manager to ignore the view.
|
||||
layout_manager_->SetChildViewIgnoredByLayout(error_message(),
|
||||
/*ignored=*/true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClassroomBubbleBaseView::OpenUrl(const GURL& url) const {
|
||||
|
@ -9,11 +9,13 @@
|
||||
#include "ash/system/unified/glanceable_tray_child_bubble.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/views/layout/flex_layout.h"
|
||||
#include "ui/views/view_observer.h"
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace views {
|
||||
class FlexLayout;
|
||||
class Label;
|
||||
}
|
||||
|
||||
@ -88,6 +90,7 @@ class ASH_EXPORT ClassroomBubbleBaseView : public GlanceableTrayChildBubble,
|
||||
nullptr;
|
||||
raw_ptr<GlanceablesProgressBarView, ExperimentalAsh> progress_bar_ = nullptr;
|
||||
raw_ptr<views::Label, ExperimentalAsh> empty_list_label_ = nullptr;
|
||||
raw_ptr<views::FlexLayout, ExperimentalAsh> layout_manager_ = nullptr;
|
||||
|
||||
base::ScopedObservation<views::View, views::ViewObserver>
|
||||
combobox_view_observation_{this};
|
||||
|
@ -109,6 +109,13 @@ std::vector<std::unique_ptr<GlanceablesClassroomAssignment>> CreateAssignments(
|
||||
|
||||
class ClassroomBubbleViewTest : public AshTestBase {
|
||||
public:
|
||||
ClassroomBubbleViewTest() {
|
||||
feature_list_.InitWithFeatures(
|
||||
/*enabled_features=*/{features::kGlanceablesV2,
|
||||
features::kGlanceablesV2ErrorMessage},
|
||||
/*disabled_features=*/{});
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
AshTestBase::SetUp();
|
||||
SimulateUserLogin(account_id_);
|
||||
@ -163,6 +170,17 @@ class ClassroomBubbleViewTest : public AshTestBase {
|
||||
base::to_underlying(GlanceablesViewId::kProgressBar)));
|
||||
}
|
||||
|
||||
const views::View* GetMessageError() const {
|
||||
return views::AsViewClass<views::View>(view_->GetViewByID(
|
||||
base::to_underlying(GlanceablesViewId::kGlanceablesErrorMessageView)));
|
||||
}
|
||||
|
||||
const views::LabelButton* GetMessageErrorDismissButton() const {
|
||||
return views::AsViewClass<views::LabelButton>(
|
||||
view_->GetViewByID(base::to_underlying(
|
||||
GlanceablesViewId::kGlanceablesErrorMessageButton)));
|
||||
}
|
||||
|
||||
protected:
|
||||
testing::StrictMock<TestClient> classroom_client_;
|
||||
std::unique_ptr<views::Widget> widget_;
|
||||
@ -177,7 +195,7 @@ class ClassroomBubbleViewTest : public AshTestBase {
|
||||
const GlanceablesTestNewWindowDelegate new_window_delegate_;
|
||||
|
||||
private:
|
||||
base::test::ScopedFeatureList feature_list_{features::kGlanceablesV2};
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
AccountId account_id_ = AccountId::FromUserEmail("test_user@gmail.com");
|
||||
};
|
||||
|
||||
@ -210,7 +228,8 @@ class ClassroomBubbleTeacherViewTest : public ClassroomBubbleViewTest {
|
||||
ClassroomBubbleTeacherViewTest() {
|
||||
feature_list_.InitWithFeatures(
|
||||
/*enabled_features=*/{features::kGlanceablesV2,
|
||||
features::kGlanceablesV2ClassroomTeacherView},
|
||||
features::kGlanceablesV2ClassroomTeacherView,
|
||||
features::kGlanceablesV2ErrorMessage},
|
||||
/*disabled_features=*/{});
|
||||
}
|
||||
|
||||
@ -685,4 +704,130 @@ TEST_F(ClassroomBubbleStudentViewTest, ClickItemViewUserAction) {
|
||||
/*expected_bucket_count=*/1);
|
||||
}
|
||||
|
||||
TEST_F(ClassroomBubbleStudentViewTest, ShowErrorMessageBubble) {
|
||||
EXPECT_CALL(classroom_client_, GetCompletedStudentAssignments(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
// Error message is not initialized before replying to pending request.
|
||||
EXPECT_FALSE(GetMessageError());
|
||||
|
||||
std::move(cb).Run(/*success=*/false, {});
|
||||
|
||||
// Error message is created and visible after receiving unsuccessful
|
||||
// response.
|
||||
ASSERT_TRUE(GetMessageError());
|
||||
EXPECT_TRUE(GetMessageError()->GetVisible());
|
||||
});
|
||||
|
||||
ASSERT_FALSE(GetMessageError());
|
||||
ASSERT_TRUE(GetComboBoxView());
|
||||
GetComboBoxView()->SelectMenuItemForTest(3);
|
||||
}
|
||||
|
||||
TEST_F(ClassroomBubbleTeacherViewTest, ShowErrorMessageBubble) {
|
||||
EXPECT_CALL(classroom_client_, GetGradedTeacherAssignments(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
// Error message is not initialized before replying to pending request.
|
||||
EXPECT_FALSE(GetMessageError());
|
||||
|
||||
std::move(cb).Run(/*success=*/false, {});
|
||||
|
||||
// Error message is created and visible after receiving unsuccessful
|
||||
// response.
|
||||
ASSERT_TRUE(GetMessageError());
|
||||
EXPECT_TRUE(GetMessageError()->GetVisible());
|
||||
});
|
||||
|
||||
ASSERT_FALSE(GetMessageError());
|
||||
ASSERT_TRUE(GetComboBoxView());
|
||||
GetComboBoxView()->SelectMenuItemForTest(3);
|
||||
}
|
||||
|
||||
TEST_F(ClassroomBubbleStudentViewTest, DismissErrorMessageBubble) {
|
||||
EXPECT_CALL(classroom_client_, GetCompletedStudentAssignments(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
std::move(cb).Run(/*success=*/false, {});
|
||||
ASSERT_TRUE(GetMessageError()->GetVisible());
|
||||
});
|
||||
|
||||
ASSERT_FALSE(GetMessageError());
|
||||
ASSERT_TRUE(GetComboBoxView());
|
||||
GetComboBoxView()->SelectMenuItemForTest(3);
|
||||
|
||||
// Trigger layout after receiving unsuccessful response.
|
||||
widget_->LayoutRootViewIfNecessary();
|
||||
ASSERT_TRUE(GetMessageError());
|
||||
|
||||
LeftClickOn(GetMessageErrorDismissButton());
|
||||
EXPECT_FALSE(GetMessageError());
|
||||
}
|
||||
|
||||
TEST_F(ClassroomBubbleTeacherViewTest, DismissErrorMessageBubble) {
|
||||
EXPECT_CALL(classroom_client_, GetGradedTeacherAssignments(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
std::move(cb).Run(/*success=*/false, {});
|
||||
ASSERT_TRUE(GetMessageError()->GetVisible());
|
||||
});
|
||||
|
||||
ASSERT_FALSE(GetMessageError());
|
||||
ASSERT_TRUE(GetComboBoxView());
|
||||
GetComboBoxView()->SelectMenuItemForTest(3);
|
||||
|
||||
// Trigger layout after receiving unsuccessful response.
|
||||
widget_->LayoutRootViewIfNecessary();
|
||||
ASSERT_TRUE(GetMessageError());
|
||||
|
||||
LeftClickOn(GetMessageErrorDismissButton());
|
||||
EXPECT_FALSE(GetMessageError());
|
||||
}
|
||||
|
||||
TEST_F(ClassroomBubbleStudentViewTest, DismissErrorMessageBubbleAfterSuccess) {
|
||||
EXPECT_CALL(classroom_client_, GetCompletedStudentAssignments(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
std::move(cb).Run(/*success=*/false, {});
|
||||
});
|
||||
|
||||
ASSERT_FALSE(GetMessageError());
|
||||
ASSERT_TRUE(GetComboBoxView());
|
||||
GetComboBoxView()->SelectMenuItemForTest(3);
|
||||
|
||||
// Trigger layout after receiving unsuccessful response.
|
||||
widget_->LayoutRootViewIfNecessary();
|
||||
ASSERT_TRUE(GetMessageError());
|
||||
|
||||
EXPECT_CALL(classroom_client_, GetStudentAssignmentsWithoutDueDate(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
std::move(cb).Run(/*success=*/true, CreateAssignments(1));
|
||||
});
|
||||
GetComboBoxView()->SelectMenuItemForTest(1);
|
||||
|
||||
// Trigger layout after receiving successful response.
|
||||
widget_->LayoutRootViewIfNecessary();
|
||||
EXPECT_FALSE(GetMessageError());
|
||||
}
|
||||
|
||||
TEST_F(ClassroomBubbleTeacherViewTest, DismissErrorMessageBubbleAfterSuccess) {
|
||||
EXPECT_CALL(classroom_client_, GetGradedTeacherAssignments(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
std::move(cb).Run(/*success=*/false, {});
|
||||
});
|
||||
|
||||
ASSERT_FALSE(GetMessageError());
|
||||
ASSERT_TRUE(GetComboBoxView());
|
||||
GetComboBoxView()->SelectMenuItemForTest(3);
|
||||
|
||||
// Trigger layout after receiving unsuccessful response.
|
||||
widget_->LayoutRootViewIfNecessary();
|
||||
ASSERT_TRUE(GetMessageError());
|
||||
|
||||
EXPECT_CALL(classroom_client_, GetTeacherAssignmentsRecentlyDue(_))
|
||||
.WillOnce([&](GlanceablesClassroomClient::GetAssignmentsCallback cb) {
|
||||
std::move(cb).Run(/*success=*/true, CreateAssignments(1));
|
||||
});
|
||||
GetComboBoxView()->SelectMenuItemForTest(1);
|
||||
|
||||
// Trigger layout after receiving successful response.
|
||||
widget_->LayoutRootViewIfNecessary();
|
||||
EXPECT_FALSE(GetMessageError());
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -7,8 +7,12 @@
|
||||
#include <memory>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/glanceables/common/glanceables_error_message_view.h"
|
||||
#include "ash/glanceables/common/glanceables_view_id.h"
|
||||
#include "ash/public/cpp/style/color_provider.h"
|
||||
#include "ash/style/ash_color_id.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/types/cxx23_to_underlying.h"
|
||||
#include "chromeos/constants/chromeos_features.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
|
||||
@ -52,6 +56,34 @@ GlanceableTrayChildBubble::GlanceableTrayChildBubble(
|
||||
}
|
||||
}
|
||||
|
||||
void GlanceableTrayChildBubble::Layout() {
|
||||
views::View::Layout();
|
||||
if (error_message_) {
|
||||
error_message_->UpdateBoundsToContainer(GetLocalBounds());
|
||||
}
|
||||
}
|
||||
|
||||
void GlanceableTrayChildBubble::ShowErrorMessage(
|
||||
const std::u16string& error_message) {
|
||||
MaybeDismissErrorMessage();
|
||||
|
||||
error_message_ = AddChildView(std::make_unique<GlanceablesErrorMessageView>(
|
||||
base::BindRepeating(&GlanceableTrayChildBubble::MaybeDismissErrorMessage,
|
||||
base::Unretained(this)),
|
||||
error_message));
|
||||
error_message_->SetProperty(views::kViewIgnoredByLayoutKey, true);
|
||||
error_message_->SetID(
|
||||
base::to_underlying(GlanceablesViewId::kGlanceablesErrorMessageView));
|
||||
}
|
||||
|
||||
void GlanceableTrayChildBubble::MaybeDismissErrorMessage() {
|
||||
if (!error_message_.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveChildViewT(std::exchange(error_message_, nullptr));
|
||||
}
|
||||
|
||||
BEGIN_METADATA(GlanceableTrayChildBubble, views::View)
|
||||
END_METADATA
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#ifndef ASH_SYSTEM_UNIFIED_GLANCEABLE_TRAY_CHILD_BUBBLE_H_
|
||||
#define ASH_SYSTEM_UNIFIED_GLANCEABLE_TRAY_CHILD_BUBBLE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/system/tray/tray_detailed_view.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
@ -12,6 +14,8 @@
|
||||
|
||||
namespace ash {
|
||||
|
||||
class GlanceablesErrorMessageView;
|
||||
|
||||
// Child bubble of the `GlanceableTrayBubbleView`.
|
||||
class ASH_EXPORT GlanceableTrayChildBubble : public TrayDetailedView {
|
||||
public:
|
||||
@ -28,6 +32,20 @@ class ASH_EXPORT GlanceableTrayChildBubble : public TrayDetailedView {
|
||||
GlanceableTrayChildBubble& operator-(const GlanceableTrayChildBubble&) =
|
||||
delete;
|
||||
~GlanceableTrayChildBubble() override = default;
|
||||
|
||||
GlanceablesErrorMessageView* error_message() { return error_message_; }
|
||||
|
||||
// views::View:
|
||||
void Layout() override;
|
||||
|
||||
protected:
|
||||
// Removes an active `error_message_` from the view, if any.
|
||||
void MaybeDismissErrorMessage();
|
||||
void ShowErrorMessage(const std::u16string& error_message);
|
||||
|
||||
private:
|
||||
raw_ptr<GlanceablesErrorMessageView, ExperimentalAsh> error_message_ =
|
||||
nullptr;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
Reference in New Issue
Block a user