// Copyright 2021 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/ash/input_method/grammar_suggestion_window.h"
#include "chrome/browser/ui/ash/input_method/border_factory.h"
#include "chrome/browser/ui/ash/input_method/colors.h"
#include "chrome/browser/ui/ash/input_method/suggestion_details.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/chromeos/styles/cros_styles.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/vector_icons.h"
#include "ui/wm/core/window_animations.h"
namespace ui {
namespace ime {
namespace {
constexpr int kGrammarPaddingSize = 4;
constexpr float kSuggestionBorderRadius = 2;
// Large enough to make the background a circle.
constexpr float kIconBorderRadius = 100;
constexpr int kWindowOffsetY = -4;
bool ShouldHighlight(const views::Button& button) {
return button.GetState() == views::Button::STATE_HOVERED ||
button.GetState() == views::Button::STATE_PRESSED;
}
} // namespace
GrammarSuggestionWindow::GrammarSuggestionWindow(gfx::NativeView parent,
AssistiveDelegate* delegate)
: delegate_(delegate) {
DialogDelegate::SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
SetCanActivate(false);
DCHECK(parent);
set_parent_window(parent);
set_margins(gfx::Insets::TLBR(kGrammarPaddingSize, kGrammarPaddingSize,
kGrammarPaddingSize, kGrammarPaddingSize));
SetArrow(views::BubbleBorder::Arrow::BOTTOM_LEFT);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal));
suggestion_button_ = AddChildView(
std::make_unique<CompletionSuggestionView>(base::BindRepeating(
&AssistiveDelegate::AssistiveWindowButtonClicked,
base::Unretained(delegate_),
AssistiveWindowButton{
.id = ui::ime::ButtonId::kSuggestion,
.window_type =
ash::ime::AssistiveWindowType::kGrammarSuggestion})));
suggestion_button_->SetBackground(nullptr);
suggestion_button_->SetFocusBehavior(views::View::FocusBehavior::NEVER);
suggestion_button_->SetVisible(true);
ignore_button_ =
AddChildView(std::make_unique<views::ImageButton>(base::BindRepeating(
&AssistiveDelegate::AssistiveWindowButtonClicked,
base::Unretained(delegate_),
AssistiveWindowButton{
.id = ui::ime::ButtonId::kIgnoreSuggestion,
.window_type = ash::ime::AssistiveWindowType::kGrammarSuggestion,
})));
ignore_button_->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
ignore_button_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
ignore_button_->SetFocusBehavior(views::View::FocusBehavior::NEVER);
ignore_button_->SetVisible(true);
// Highlights buttons when they are hovered or pressed.
const auto update_button_highlight = [](views::Button* button) {
button->SetBackground(ShouldHighlight(*button)
? views::CreateSolidBackground(
ResolveSemanticColor(kButtonHighlightColor))
: nullptr);
};
subscriptions_.insert(
{suggestion_button_,
suggestion_button_->AddStateChangedCallback(base::BindRepeating(
update_button_highlight, base::Unretained(suggestion_button_)))});
subscriptions_.insert(
{ignore_button_,
ignore_button_->AddStateChangedCallback(base::BindRepeating(
update_button_highlight, base::Unretained(ignore_button_)))});
}
GrammarSuggestionWindow::~GrammarSuggestionWindow() = default;
void GrammarSuggestionWindow::OnThemeChanged() {
ignore_button_->SetBorder(
views::CreateEmptyBorder(views::LayoutProvider::Get()->GetInsetsMetric(
views::INSETS_VECTOR_IMAGE_BUTTON)));
ignore_button_->SetImageModel(
views::Button::ButtonState::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(
views::kCloseIcon,
ResolveSemanticColor(cros_styles::ColorName::kTextColorPrimary)));
BubbleDialogDelegateView::OnThemeChanged();
}
views::Widget* GrammarSuggestionWindow::InitWidget() {
views::Widget* widget = BubbleDialogDelegateView::CreateBubble(this);
wm::SetWindowVisibilityAnimationTransition(widget->GetNativeView(),
wm::ANIMATE_NONE);
GetBubbleFrameView()->SetBubbleBorder(
GetBorderForWindow(WindowBorderType::Suggestion));
GetBubbleFrameView()->OnThemeChanged();
return widget;
}
void GrammarSuggestionWindow::Show() {
GetWidget()->Show();
}
void GrammarSuggestionWindow::Hide() {
GetWidget()->Close();
}
void GrammarSuggestionWindow::SetSuggestion(const std::u16string& suggestion) {
suggestion_button_->SetView(SuggestionDetails{.text = suggestion});
}
void GrammarSuggestionWindow::SetButtonHighlighted(
const AssistiveWindowButton& button,
bool highlighted) {
if (highlighted && button.id == current_highlighted_button_id_) {
return;
}
suggestion_button_->SetBackground(nullptr);
ignore_button_->SetBackground(nullptr);
if (highlighted) {
switch (button.id) {
case ButtonId::kSuggestion:
suggestion_button_->SetBackground(views::CreateRoundedRectBackground(
ResolveSemanticColor(kButtonHighlightColor),
kSuggestionBorderRadius));
break;
case ButtonId::kIgnoreSuggestion:
ignore_button_->SetBackground(views::CreateRoundedRectBackground(
ResolveSemanticColor(kButtonHighlightColor), kIconBorderRadius));
break;
default:
break;
}
}
}
void GrammarSuggestionWindow::SetBounds(gfx::Rect bounds) {
bounds.Offset(0, kWindowOffsetY);
SetAnchorRect(bounds);
}
CompletionSuggestionView*
GrammarSuggestionWindow::GetSuggestionButtonForTesting() {
return suggestion_button_;
}
views::Button* GrammarSuggestionWindow::GetIgnoreButtonForTesting() {
return ignore_button_;
}
BEGIN_METADATA(GrammarSuggestionWindow)
END_METADATA
} // namespace ime
} // namespace ui