blob: d66eb354ee756e3e54988476b19d54b3042b6eef [file] [log] [blame]
// Copyright 2021 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/projector/projector_annotation_tray.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/projector/projector_controller_impl.h"
#include "ash/projector/ui/projector_color_button.h"
#include "ash/public/cpp/projector/annotator_tool.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/system_shadow.h"
#include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/tray/tray_bubble_wrapper.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/tray/tray_utils.h"
#include "components/prefs/pref_service.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// Margins between the title view and the edges around it (dp).
constexpr int kPaddingBetweenBottomAndLastTrayItem = 4;
// Width of the bubble itself (dp).
constexpr int kBubbleWidth = 196;
// Insets for the views (dp).
constexpr auto kPenViewPadding = gfx::Insets::TLBR(4, 16, 0, 16);
// Spacing between buttons (dp).
constexpr int kButtonsPadding = 12;
// Size of menu rows.
constexpr int kMenuRowHeight = 48;
// Color selection buttons.
constexpr int kColorButtonColorViewSize = 16;
constexpr int kColorButtonViewRadius = 28;
constexpr SkColor kPenColors[] = {
kProjectorMagentaPenColor, kProjectorBluePenColor, kProjectorYellowPenColor,
kProjectorRedPenColor};
// TODO(b/201664243): Use AnnotatorToolType.
enum ProjectorTool { kToolNone, kToolPen };
ProjectorTool GetCurrentTool() {
auto* controller = Shell::Get()->projector_controller();
// ProjctorController may not be available yet as the ProjectorAnnotationTray
// is created before it.
if (!controller)
return kToolNone;
if (controller->IsAnnotatorEnabled())
return kToolPen;
return kToolNone;
}
const gfx::VectorIcon& GetIconForTool(ProjectorTool tool, SkColor color) {
switch (tool) {
case kToolNone:
return kPaletteTrayIconProjectorIcon;
case kToolPen:
switch (color) {
case kProjectorMagentaPenColor:
return kPaletteTrayIconProjectorMagentaIcon;
case kProjectorBluePenColor:
return kPaletteTrayIconProjectorBlueIcon;
case kProjectorRedPenColor:
return kPaletteTrayIconProjectorRedIcon;
case kProjectorYellowPenColor:
return kPaletteTrayIconProjectorYellowIcon;
}
}
NOTREACHED();
return kPaletteTrayIconProjectorIcon;
}
} // namespace
ProjectorAnnotationTray::ProjectorAnnotationTray(Shelf* shelf)
: TrayBackgroundView(shelf),
image_view_(
tray_container()->AddChildView(std::make_unique<views::ImageView>())),
pen_view_(nullptr) {
image_view_->SetTooltipText(GetTooltip());
image_view_->SetHorizontalAlignment(views::ImageView::Alignment::kCenter);
image_view_->SetVerticalAlignment(views::ImageView::Alignment::kCenter);
image_view_->SetPreferredSize(gfx::Size(kTrayItemSize, kTrayItemSize));
ResetTray();
session_observer_.Observe(Shell::Get()->session_controller());
}
ProjectorAnnotationTray::~ProjectorAnnotationTray() = default;
bool ProjectorAnnotationTray::PerformAction(const ui::Event& event) {
ToggleAnnotator();
return true;
}
void ProjectorAnnotationTray::OnMouseEvent(ui::MouseEvent* event) {
if (event->type() != ui::ET_MOUSE_PRESSED) {
return;
}
if (event->IsRightMouseButton()) {
ShowBubble();
} else if (event->IsLeftMouseButton()) {
ToggleAnnotator();
}
}
void ProjectorAnnotationTray::OnGestureEvent(ui::GestureEvent* event) {
if (event->details().type() == ui::ET_GESTURE_LONG_PRESS) {
ShowBubble();
} else if (event->details().type() == ui::ET_GESTURE_TAP) {
ToggleAnnotator();
}
}
void ProjectorAnnotationTray::ClickedOutsideBubble() {
CloseBubble();
}
std::u16string ProjectorAnnotationTray::GetAccessibleNameForTray() {
std::u16string enabled_state = l10n_util::GetStringUTF16(
GetCurrentTool() == kToolNone
? IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_OFF_STATE
: IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_ON_STATE);
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_ACCESSIBLE_TITLE,
enabled_state);
}
void ProjectorAnnotationTray::HandleLocaleChange() {}
void ProjectorAnnotationTray::HideBubbleWithView(
const TrayBubbleView* bubble_view) {
if (bubble_->bubble_view() == bubble_view)
CloseBubble();
}
void ProjectorAnnotationTray::CloseBubble() {
pen_view_ = nullptr;
bubble_.reset();
shelf()->UpdateAutoHideState();
}
void ProjectorAnnotationTray::ShowBubble() {
if (bubble_)
return;
DCHECK(tray_container());
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
init_params.parent_window = GetBubbleWindowContainer();
init_params.anchor_view = nullptr;
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.anchor_rect = GetBubbleAnchor()->GetAnchorBoundsInScreen();
init_params.anchor_rect.Inset(GetBubbleAnchorInsets());
init_params.shelf_alignment = shelf()->alignment();
init_params.preferred_width = kBubbleWidth;
init_params.close_on_deactivate = true;
init_params.translucent = true;
init_params.has_shadow = false;
init_params.corner_radius = kTrayItemCornerRadius;
init_params.reroute_event_handler = true;
// Create and customize bubble view.
TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
bubble_view->set_margins(GetSecondaryBubbleInsets());
bubble_view->SetBorder(views::CreateEmptyBorder(
gfx::Insets::TLBR(0, 0, kPaddingBetweenBottomAndLastTrayItem, 0)));
auto setup_layered_view = [](views::View* view) {
view->SetPaintToLayer();
view->layer()->SetFillsBoundsOpaquely(false);
};
// Add drawing tools
{
auto* marker_view_container =
bubble_view->AddChildView(std::make_unique<views::View>());
auto box_layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, kPenViewPadding,
kButtonsPadding);
box_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
box_layout->set_minimum_cross_axis_size(kMenuRowHeight);
marker_view_container->SetLayoutManager(std::move(box_layout));
for (SkColor color : kPenColors) {
auto* color_button = marker_view_container->AddChildView(
std::make_unique<ProjectorColorButton>(
base::BindRepeating(&ProjectorAnnotationTray::OnPenColorPressed,
base::Unretained(this), color),
color, kColorButtonColorViewSize, kColorButtonViewRadius,
l10n_util::GetStringUTF16(GetAccessibleNameForColor(color))));
color_button->SetToggled(current_pen_color_ == color);
}
setup_layered_view(marker_view_container);
}
// Show the bubble.
bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view);
SetIsActive(true);
// Create a shadow for bubble widget.
shadow_ = SystemShadow::CreateShadowForWidget(
GetBubbleWidget(), SystemShadow::Type::kElevation12);
shadow_->SetRoundedCornerRadius(init_params.corner_radius);
shadow_->SetContentBounds(bubble_view->GetContentsBounds());
}
TrayBubbleView* ProjectorAnnotationTray::GetBubbleView() {
return bubble_ ? bubble_->bubble_view() : nullptr;
}
views::Widget* ProjectorAnnotationTray::GetBubbleWidget() const {
return bubble_ ? bubble_->GetBubbleWidget() : nullptr;
}
void ProjectorAnnotationTray::OnThemeChanged() {
TrayBackgroundView::OnThemeChanged();
UpdateIcon();
}
void ProjectorAnnotationTray::OnActiveUserPrefServiceChanged(
PrefService* pref_service) {
const uint64_t color =
pref_service->GetUint64(prefs::kProjectorAnnotatorLastUsedMarkerColor);
current_pen_color_ = !color ? kProjectorDefaultPenColor : color;
}
void ProjectorAnnotationTray::HideAnnotationTray() {
SetVisiblePreferred(false);
UpdateIcon();
PrefService* pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
pref_service->SetUint64(prefs::kProjectorAnnotatorLastUsedMarkerColor,
current_pen_color_);
ResetTray();
}
void ProjectorAnnotationTray::OnCanvasInitializationFailed() {
// Set icon color to kIconColorPrimary with 30% opacity.
SkColor disabled_icon_color =
SkColorSetA(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary),
0x4D);
image_view_->SetImage(gfx::CreateVectorIcon(kPaletteTrayIconProjectorIcon,
disabled_icon_color));
image_view_->SetTooltipText(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_UNAVAILABLE));
}
void ProjectorAnnotationTray::ToggleAnnotator() {
if (GetCurrentTool() == kToolNone) {
EnableAnnotatorWithPenColor();
} else {
DeactivateActiveTool();
}
if (bubble_) {
CloseBubble();
}
UpdateIcon();
}
void ProjectorAnnotationTray::EnableAnnotatorWithPenColor() {
auto* controller = Shell::Get()->projector_controller();
DCHECK(controller);
AnnotatorTool tool;
tool.color = current_pen_color_;
controller->SetAnnotatorTool(tool);
controller->EnableAnnotatorTool();
}
void ProjectorAnnotationTray::DeactivateActiveTool() {
auto* controller = Shell::Get()->projector_controller();
DCHECK(controller);
controller->ResetTools();
}
void ProjectorAnnotationTray::UpdateIcon() {
const ProjectorTool tool = GetCurrentTool();
image_view_->SetImage(gfx::CreateVectorIcon(
GetIconForTool(tool, current_pen_color_),
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary)));
image_view_->SetTooltipText(GetTooltip());
SetIsActive(tool != kToolNone);
}
void ProjectorAnnotationTray::OnPenColorPressed(SkColor color) {
current_pen_color_ = color;
EnableAnnotatorWithPenColor();
CloseBubble();
UpdateIcon();
}
int ProjectorAnnotationTray::GetAccessibleNameForColor(SkColor color) {
switch (color) {
case kProjectorRedPenColor:
return IDS_RED_COLOR_BUTTON;
case kProjectorBluePenColor:
return IDS_BLUE_COLOR_BUTTON;
case kProjectorYellowPenColor:
return IDS_YELLOW_COLOR_BUTTON;
case kProjectorMagentaPenColor:
return IDS_MAGENTA_COLOR_BUTTON;
}
NOTREACHED();
return IDS_RED_COLOR_BUTTON;
}
void ProjectorAnnotationTray::ResetTray() {
// Disable the tray icon. It is enabled once the ink canvas is initialized.
SetEnabled(false);
}
std::u16string ProjectorAnnotationTray::GetTooltip() {
std::u16string enabled_state = l10n_util::GetStringUTF16(
GetCurrentTool() == kToolNone
? IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_OFF_STATE
: IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_ON_STATE);
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_TOOLTIP, enabled_state);
}
} // namespace ash