blob: acfc8d2a93fa796245ed13668106afa427c2fd84 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/permissions/chip/permission_dashboard_view.h"
#include <algorithm>
#include <vector>
#include "base/time/time.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/permissions/chip/permission_chip_view.h"
#include "components/vector_icons/vector_icons.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPathBuilder.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/view_class_properties.h"
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(PermissionDashboardView,
kDashboardElementId);
namespace {
// Distance drawn under the indicator chip.
constexpr int kExtraArcPadding = 2;
class IndicatorDividerBackground : public views::Background {
public:
// Background will have right rounded side with |arc_radius|.
IndicatorDividerBackground(SkColor color, SkScalar arc_radius)
: arc_radius_(arc_radius) {
SetColor(color);
}
IndicatorDividerBackground(const IndicatorDividerBackground&) = delete;
IndicatorDividerBackground& operator=(const IndicatorDividerBackground&) =
delete;
~IndicatorDividerBackground() override = default;
// views::Background
void Paint(gfx::Canvas* canvas, views::View* view) const override {
/*
* The background draws a rectangle with arc right side.
* * * * * * \
* * \
* * |
* * /
* * * * * * /
*/
const SkScalar height = view->height();
// The arc is drawn between two chips and its width is equal to a distance
// between chips and an extra padding that is drawn under the indicator
// chip.
const int arc_width =
GetLayoutConstant(LOCATION_BAR_CHIP_PADDING) + kExtraArcPadding;
const SkScalar arc_x = view->width() - arc_width;
const SkPath path =
SkPathBuilder()
.lineTo(arc_x, 0)
.rArcTo(SkVector(arc_radius_, arc_radius_), 0,
SkPathBuilder::kSmall_ArcSize, SkPathDirection::kCW,
SkPoint(0, height))
.lineTo(0, height)
.close()
.detach();
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setColor(color().ResolveToSkColor(view->GetColorProvider()));
canvas->DrawPath(path, flags);
}
private:
SkScalar arc_radius_;
};
} // namespace
PermissionDashboardView::PermissionDashboardView() {
SetProperty(views::kElementIdentifierKey, kDashboardElementId);
SetVisible(false);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal));
// Left-Hand Side Activity indicators chip.
anchored_chip_ = AddChildView(std::make_unique<PermissionChipView>(
PermissionChipView::PressedCallback()));
// An empty view is created to be placed between the LHS activity indicator
// chip and the permission request chip. This view is a divider that creates
// an illusion of an empty space between chips.
chip_divider_view_ = AddChildView(std::make_unique<views::View>());
chip_divider_view_->SetPaintToLayer();
chip_divider_view_->layer()->SetFillsBoundsOpaquely(false);
chip_divider_view_->SetVisible(false);
// Permission request chip.
secondary_chip_ = AddChildView(std::make_unique<PermissionChipView>(
PermissionChipView::PressedCallback()));
// It is unclear which chip will be shown first, hence hide both of them.
secondary_chip_->SetVisible(false);
anchored_chip_->SetVisible(false);
// This is needed to make sure that the permission dashboard view is
// recognized as a single button. Individual elements inside this view should
// not be accessible and/or focusable.
anchored_chip_->GetViewAccessibility().SetIsIgnored(true);
secondary_chip_->GetViewAccessibility().SetIsIgnored(true);
chip_divider_view_->GetViewAccessibility().SetIsIgnored(true);
GetViewAccessibility().SetRole(ax::mojom::Role::kButton);
}
PermissionDashboardView::~PermissionDashboardView() = default;
void PermissionDashboardView::SetDividerBackgroundColor(
SkColor background_color) {
constexpr int kDividerViewArcRadius = 16;
chip_divider_view_->SetBackground(
std::make_unique<IndicatorDividerBackground>(background_color,
kDividerViewArcRadius));
}
void PermissionDashboardView::UpdateDividerViewVisibility() {
const bool is_visible =
anchored_chip_->GetVisible() && secondary_chip_->GetVisible();
if (is_visible) {
int width = anchored_chip_->GetIconViewWidth();
chip_divider_view_->SetPreferredSize(
gfx::Size(width, anchored_chip_->GetHeightForWidth(width)));
// `chip_divider_view_` should be shown under `anchored_chip_`. Move
// `chip_divider_view_` to the left by setting a negative margin.
chip_divider_view_->SetProperty(
views::kMarginsKey,
gfx::Insets::TLBR(
0, GetLayoutConstant(LOCATION_BAR_CHIP_PADDING) - width, 0, 0));
}
// The divivder arc's width is needed to offset the request chip and draw it
// under the arc.
const int arc_width =
GetLayoutConstant(LOCATION_BAR_CHIP_PADDING) + kExtraArcPadding;
secondary_chip_->UpdateForDividerVisibility(is_visible, arc_width);
chip_divider_view_->SetVisible(is_visible);
}
gfx::Size PermissionDashboardView::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
if (!secondary_chip_->GetVisible() && !anchored_chip_->GetVisible()) {
return gfx::Size();
}
if (!secondary_chip_->GetVisible()) {
return anchored_chip_->GetPreferredSize();
}
if (!anchored_chip_->GetVisible()) {
return secondary_chip_->GetPreferredSize();
}
// Part of the request chip that is drawn under the arc.
const int secondary_chip_margin =
GetLayoutConstant(LOCATION_BAR_CHIP_PADDING) + kExtraArcPadding;
// Visible width of the request chip.
int secondary_chip_visible_width =
secondary_chip_->GetPreferredSize().width() - secondary_chip_margin;
gfx::Size size = anchored_chip_->GetPreferredSize();
size.Enlarge(secondary_chip_visible_width +
GetLayoutConstant(LOCATION_BAR_CHIP_PADDING),
0);
return size;
}
views::View::Views PermissionDashboardView::GetChildrenInZOrder() {
View::Views paint_order = View::GetChildrenInZOrder();
std::ranges::reverse(paint_order);
return paint_order;
}
BEGIN_METADATA(PermissionDashboardView)
END_METADATA