Brave search conversion promotion - UI part

Implement BraveOmniboxResultView by subclassing OmniboxResultView to provide
promotion specfic views. BraveOmniboxResultView controls visibility upstream children and
promotion children based on match. When promotion match is set, upstream children are all
hidden and promotion children get visible.
This commit is contained in:
Simon Hong
2022-06-15 17:01:51 +09:00
parent 76f7f02336
commit 9750e82fb3
18 changed files with 946 additions and 0 deletions
+28
View File
@@ -574,6 +574,34 @@ Or change later at <ph name="SETTINGS_EXTENIONS_LINK">$2<ex>brave://settings/ext
<message name="IDS_WINDOW_CLOSING_CONFIRM_DLG_DONT_ASK_AGAIN_LABEL" desc="The text for checkbox in the dialog">
Don't ask me again
</message>
<!-- Brave search conversion promotion -->
<message name="IDS_BRAVE_SEARCH_CONVERSION_INPUT_APPEND_LABEL" desc="The text that appended to input query in button type match">
- Brave Search
</message>
<message name="IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_DESC_LABEL" desc="The text for detailed description about brave search in promotion match">
Support independent search with better privacy. Brave search doesn't track you, your queries, or your clicks.
</message>
<message name="IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_DESC_LABEL_FIRST_PART" desc="The text for finding first sentence from above. Both should have same translation.">
Support independent search with better privacy.
</message>
<message name="IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_BANNER_TITLE" desc="The text for banner type title">
Support independent search with better privacy
</message>
<message name="IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_BANNER_DESC" desc="The text for banner type description">
Brave search doesn't track you, your queries, or your clicks.
</message>
<message name="IDS_BRAVE_SEARCH_CONVERSION_TRY_BUTTON_LABEL" desc="The text for try button in promotion match">
Try Brave Search
</message>
<message name="IDS_BRAVE_SEARCH_CONVERSION_DISMISS_BUTTON_TOOLTIP" desc="The tooltip text for dismiss button in promotion match">
Dismiss brave search conversion
</message>
<message name="IDS_BRAVE_SEARCH_CONVERSION_DISMISS_BUTTON_LABEL" desc="The text for dismiss button in promotion match">
Dismiss
</message>
<message name="IDS_ACC_BRAVE_SEARCH_CONVERSION_DISMISS_BUTTON" desc="Announcement when dismiss button is focused.">">
Dismiss brave search conversion, press Enter to remove this suggestion
</message>
</if>
<!-- Brave Ads -->
+3
View File
@@ -95,7 +95,10 @@
<structure type="chrome_scaled_image" name="IDR_PROFILE_AVATAR_88" file="common/avatars/avatar_bonbon_royal.png" />
<structure type="chrome_scaled_image" name="IDR_PROFILE_AVATAR_89" file="common/avatars/avatar_bonbon_ninja.png" />
<structure type="chrome_scaled_image" name="IDR_BRAVE_SEARCH_CONVERSION_BANNER_GRAPHIC" file="brave/brave_search_conversion_banner_graphic.png" />
<structure type="chrome_scaled_image" name="IDR_BRAVE_SEARCH_CONVERSION_BANNER_GRAPHIC_DARK" file="brave/brave_search_conversion_banner_graphic_dark.png" />
<structure type="chrome_scaled_image" name="IDR_BRAVE_SEARCH_LOGO_IN_WEB_DISCOVERY_DIALOG" file="brave/brave_search_logo_in_web_discovery_dialog.png" />
<structure type="chrome_scaled_image" name="IDR_BRAVE_SEARCH_LOGO_IN_SEARCH_PROMOTION" file="brave/brave_search_logo_in_search_promotion.png" />
</if>
</structures>
</release>
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

+46
View File
@@ -98,6 +98,29 @@ absl::optional<SkColor> MaybeGetDefaultColorForBraveLightUi(int id) {
case BraveThemeProperties::COLOR_BRAVE_VPN_BUTTON_TEXT_DISCONNECTED:
return SkColorSetRGB(0x86, 0x8E, 0x96);
#endif
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BANNER_TYPE_DESC_TEXT:
return SkColorSetRGB(0x2E, 0x30, 0x39);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_BORDER:
return SkColorSetRGB(0xE2, 0xE3, 0xF8);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_FROM:
return SkColorSetARGB(104, 0xFF, 0xFF, 0xFF);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_TO:
return SkColorSetARGB(104, 0xEF, 0xEF, 0xFB);
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BUTTON_TYPE_INPUT_APPEND:
return SkColorSetRGB(0x58, 0x5C, 0x6D);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_NORMAL:
return SkColorSetRGB(0xED, 0xEE, 0xFA);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_HOVERED:
return SkColorSetRGB(0xE2, 0xE3, 0xF8);
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_NORMAL:
return SkColorSetRGB(0x44, 0x4d, 0xd0);
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_HOVERED:
return SkColorSetRGB(0x1F, 0x25, 0x7A);
case BraveThemeProperties::COLOR_FOR_TEST:
return BraveThemeProperties::kLightColorForTest;
default:
@@ -194,6 +217,29 @@ absl::optional<SkColor> MaybeGetDefaultColorForBraveDarkUi(int id) {
case BraveThemeProperties::COLOR_BRAVE_VPN_BUTTON_TEXT_DISCONNECTED:
return SkColorSetRGB(0xF0, 0xF2, 0xFF);
#endif
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BANNER_TYPE_DESC_TEXT:
return SkColorSetRGB(0xE2, 0xE3, 0xE7);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_BORDER:
return SkColorSetRGB(0x1F, 0x25, 0x7A);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_FROM:
return SkColorSetARGB(104, 0x17, 0x19, 0x1E);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_TO:
return SkColorSetARGB(104, 0x1F, 0x25, 0x7A);
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BUTTON_TYPE_INPUT_APPEND:
return SkColorSetRGB(0xAC, 0xAF, 0xBB);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_NORMAL:
return SkColorSetRGB(0x1A, 0x1C, 0x3B);
case BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_HOVERED:
return SkColorSetRGB(0x1F, 0x25, 0x7A);
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_NORMAL:
return SkColorSetRGB(0xA6, 0xAB, 0xE9);
case BraveThemeProperties::COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_HOVERED:
return SkColorSetRGB(0xE2, 0xE3, 0xF8);
case BraveThemeProperties::COLOR_FOR_TEST:
return BraveThemeProperties::kDarkColorForTest;
default:
+9
View File
@@ -57,6 +57,15 @@ enum ThemeProperties {
COLOR_BRAVE_VPN_BUTTON_TEXT_CONNECTED,
COLOR_BRAVE_VPN_BUTTON_TEXT_DISCONNECTED,
#endif
COLOR_SEARCH_CONVERSION_BANNER_TYPE_DESC_TEXT,
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_BORDER,
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_FROM,
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_TO,
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_INPUT_APPEND,
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_NORMAL,
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_HOVERED,
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_NORMAL,
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_HOVERED,
COLOR_ICON_BASE,
BRAVE_THEME_PROPERTIES_LAST = COLOR_ICON_BASE,
};
+5
View File
@@ -178,6 +178,10 @@ source_set("ui") {
"views/frame/brave_opaque_browser_frame_view.h",
"views/frame/brave_window_frame_graphic.cc",
"views/frame/brave_window_frame_graphic.h",
"views/omnibox/brave_omnibox_result_view.cc",
"views/omnibox/brave_omnibox_result_view.h",
"views/omnibox/brave_search_conversion_promotion_view.cc",
"views/omnibox/brave_search_conversion_promotion_view.h",
"views/rounded_separator.cc",
"views/rounded_separator.h",
"views/tabs/brave_browser_tab_strip_controller.cc",
@@ -287,6 +291,7 @@ source_set("ui") {
"//brave/components/brave_rewards/resources:page_generated_resources",
"//brave/components/brave_rewards/resources:tip_generated_resources",
"//brave/components/brave_search/common",
"//brave/components/brave_search_conversion",
"//brave/components/brave_shields/browser",
"//brave/components/brave_vpn/buildflags",
"//brave/components/brave_wayback_machine:buildflags",
@@ -0,0 +1,103 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "brave/browser/ui/views/omnibox/brave_omnibox_result_view.h"
#include <memory>
#include "base/time/time.h"
#include "brave/browser/ui/views/omnibox/brave_search_conversion_promotion_view.h"
#include "brave/components/brave_search_conversion/types.h"
#include "brave/components/brave_search_conversion/utils.h"
#include "brave/components/omnibox/browser/promotion_utils.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h"
#include "chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.h"
#include "components/omnibox/browser/autocomplete_controller.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_result.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/window_open_disposition.h"
#include "ui/views/controls/button/image_button.h"
using brave_search_conversion::ConversionType;
using brave_search_conversion::GetConversionType;
using brave_search_conversion::SetDismissed;
BraveOmniboxResultView::~BraveOmniboxResultView() = default;
void BraveOmniboxResultView::ResetChildrenVisibility() {
// Reset children visibility. Their visibility could be configured later
// based on |match_| and current input.
suggestion_container_->SetVisible(true);
button_row_->SetVisible(true);
if (brave_search_promotion_view_) {
brave_search_promotion_view_->SetVisible(false);
}
}
void BraveOmniboxResultView::SetMatch(const AutocompleteMatch& match) {
ResetChildrenVisibility();
OmniboxResultView::SetMatch(match);
UpdateForBraveSearchConversion();
}
void BraveOmniboxResultView::OnSelectionStateChanged() {
OmniboxResultView::OnSelectionStateChanged();
HandleSelectionStateChangedForPromotionView();
}
void BraveOmniboxResultView::OpenMatch() {
popup_contents_view_->OpenMatch(
model_index_, WindowOpenDisposition::CURRENT_TAB, base::TimeTicks::Now());
}
void BraveOmniboxResultView::Dismiss() {
auto* controller = model_->autocomplete_controller();
auto* prefs = controller->autocomplete_provider_client()->GetPrefs();
SetDismissed(prefs);
// To refresh autocomplete result after dismiss, start again with current
// input. Then, popup gets same autocomplete matches w/o promotion match.
controller->Start(controller->input());
}
void BraveOmniboxResultView::HandleSelectionStateChangedForPromotionView() {
if (brave_search_promotion_view_ && IsBraveSearchPromotion()) {
brave_search_promotion_view_->OnSelectionStateChanged(
GetMatchSelected() && popup_contents_view_->GetSelection().state ==
OmniboxPopupSelection::NORMAL);
}
}
void BraveOmniboxResultView::UpdateForBraveSearchConversion() {
const bool is_brave_search_promotion = IsBraveSearchPromotion();
if (!is_brave_search_promotion)
return;
// Hide upstream children and show our promotion view.
suggestion_container_->SetVisible(false);
button_row_->SetVisible(false);
if (!brave_search_promotion_view_) {
brave_search_promotion_view_ = AddChildView(
std::make_unique<BraveSearchConversionPromotionView>(this));
}
brave_search_promotion_view_->SetVisible(true);
brave_search_promotion_view_->SetTypeAndInput(
GetConversionType(), model_->autocomplete_controller()->input().text());
}
bool BraveOmniboxResultView::IsBraveSearchPromotion() const {
const auto input = model_->autocomplete_controller()->input();
return IsBraveSearchPromotionMatch(match_, input.text());
}
BEGIN_METADATA(BraveOmniboxResultView, OmniboxResultView)
END_METADATA
@@ -0,0 +1,43 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_OMNIBOX_RESULT_VIEW_H_
#define BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_OMNIBOX_RESULT_VIEW_H_
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/views/omnibox/omnibox_result_view.h"
#include "ui/base/metadata/metadata_header_macros.h"
class BraveSearchConversionPromotionView;
// This will render brave specific matches such as braver search conversion
// promotion.
class BraveOmniboxResultView : public OmniboxResultView {
public:
METADATA_HEADER(BraveOmniboxResultView);
using OmniboxResultView::OmniboxResultView;
BraveOmniboxResultView(const BraveOmniboxResultView&) = delete;
BraveOmniboxResultView& operator=(const BraveOmniboxResultView&) = delete;
~BraveOmniboxResultView() override;
// OmniboxResultView overrides:
void SetMatch(const AutocompleteMatch& match) override;
void OnSelectionStateChanged() override;
void OpenMatch();
void Dismiss();
private:
bool IsBraveSearchPromotion() const;
void ResetChildrenVisibility();
void UpdateForBraveSearchConversion();
void HandleSelectionStateChangedForPromotionView();
// Brave search conversion promotion
raw_ptr<BraveSearchConversionPromotionView> brave_search_promotion_view_ =
nullptr;
};
#endif // BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_OMNIBOX_RESULT_VIEW_H_
@@ -0,0 +1,587 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "brave/browser/ui/views/omnibox/brave_search_conversion_promotion_view.h"
#include <utility>
#include "base/logging.h"
#include "brave/browser/themes/theme_properties.h"
#include "brave/browser/ui/views/omnibox/brave_omnibox_result_view.h"
#include "brave/components/l10n/common/locale_util.h"
#include "brave/grit/brave_generated_resources.h"
#include "brave/grit/brave_theme_resources.h"
#include "cc/paint/paint_flags.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/omnibox/omnibox_theme.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/skia_paint_util.h"
#include "ui/views/background.h"
#include "ui/views/cascading_property.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/flex_layout.h"
using brave_search_conversion::ConversionType;
namespace {
constexpr int kBannerTypeMargin = 12;
// Use small margin because omnibox popup has its own bottom padding.
constexpr int kBannerTypeMarginBottom = 4;
constexpr int kBannerTypeRadius = 8;
gfx::FontList GetFont(int font_size, gfx::Font::Weight weight) {
gfx::FontList font_list;
return font_list.DeriveWithSizeDelta(font_size - font_list.GetFontSize())
.DeriveWithWeight(weight);
}
// Draw graphic over gradient background for banner type.
class HorizontalGradientBackground : public views::Background {
public:
using Background::Background;
~HorizontalGradientBackground() override = default;
HorizontalGradientBackground(const HorizontalGradientBackground&) = delete;
HorizontalGradientBackground& operator=(const HorizontalGradientBackground&) =
delete;
void Paint(gfx::Canvas* canvas, views::View* view) const override {
// Fill with base color first.
canvas->DrawColor(GetOmniboxColor(view->GetThemeProvider(),
OmniboxPart::RESULTS_BACKGROUND));
SkColor from_color = gfx::kPlaceholderColor;
SkColor to_color = gfx::kPlaceholderColor;
if (auto* tp = view->GetThemeProvider()) {
from_color = tp->GetColor(
BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_FROM);
to_color = tp->GetColor(
BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_GRADIENT_TO);
}
cc::PaintFlags flags;
SkPoint points[2] = {SkPoint::Make(0, 0), SkPoint::Make(view->width(), 0)};
SkColor colors[2] = {from_color, to_color};
SkScalar positions[2] = {0.196f, 1.f};
flags.setShader(cc::PaintShader::MakeLinearGradient(
points, colors, positions, 2, SkTileMode::kClamp));
flags.setStyle(cc::PaintFlags::kFill_Style);
gfx::RectF bounds(view->GetLocalBounds());
canvas->DrawRect(bounds, flags);
auto& bundle = ui::ResourceBundle::GetSharedInstance();
const auto* graphic = bundle.GetImageSkiaNamed(
ui::NativeTheme::GetInstanceForNativeUi()->ShouldUseDarkColors()
? IDR_BRAVE_SEARCH_CONVERSION_BANNER_GRAPHIC_DARK
: IDR_BRAVE_SEARCH_CONVERSION_BANNER_GRAPHIC);
canvas->DrawImageInt(*graphic, bounds.width() - graphic->width(), 0);
}
};
class TryButton : public views::MdTextButton {
public:
METADATA_HEADER(TryButton);
using MdTextButton::MdTextButton;
TryButton(const TryButton&) = delete;
TryButton& operator=(const TryButton&) = delete;
void SetFontSize(int size) {
label()->SetFontList(GetFont(size, gfx::Font::Weight::SEMIBOLD));
}
};
BEGIN_METADATA(TryButton, views::MdTextButton)
END_METADATA
class BannerTypeDismissButton : public views::LabelButton {
public:
METADATA_HEADER(BannerTypeDismissButton);
using LabelButton::LabelButton;
BannerTypeDismissButton(const BannerTypeDismissButton&) = delete;
BannerTypeDismissButton& operator=(const BannerTypeDismissButton&) = delete;
void SetFontSize(int size) {
label()->SetFontList(GetFont(size, gfx::Font::Weight::SEMIBOLD));
}
};
BEGIN_METADATA(BannerTypeDismissButton, views::LabelButton)
END_METADATA
class ButtonTypeDismissButton : public views::ImageButton {
public:
METADATA_HEADER(ButtonTypeDismissButton);
explicit ButtonTypeDismissButton(PressedCallback callback = PressedCallback())
: ImageButton(std::move(callback)) {
views::ConfigureVectorImageButton(this);
}
ButtonTypeDismissButton(const ButtonTypeDismissButton&) = delete;
ButtonTypeDismissButton& operator=(const ButtonTypeDismissButton&) = delete;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
node_data->SetName(brave_l10n::GetLocalizedResourceUTF16String(
IDS_ACC_BRAVE_SEARCH_CONVERSION_DISMISS_BUTTON));
// Although this appears visually as a button, expose as a list box option
// so that it matches the other options within its list box container.
node_data->role = ax::mojom::Role::kListBoxOption;
}
};
BEGIN_METADATA(ButtonTypeDismissButton, views::ImageButton)
END_METADATA
// For rounded.
class BannerTypeContainer : public views::View {
public:
METADATA_HEADER(BannerTypeContainer);
using View::View;
BannerTypeContainer(const BannerTypeContainer&) = delete;
BannerTypeContainer& operator=(const BannerTypeContainer&) = delete;
// views::View overrides:
void OnPaint(gfx::Canvas* canvas) override {
SkPath mask;
mask.addRoundRect(gfx::RectToSkRect(GetLocalBounds()), kBannerTypeRadius,
kBannerTypeRadius);
canvas->ClipPath(mask, true);
View::OnPaint(canvas);
}
};
BEGIN_METADATA(BannerTypeContainer, views::View)
END_METADATA
////////////////////////////////////////////////////////////////////////////////
// OmniboxResultSelectionIndicator
class BraveOmniboxResultSelectionIndicator : public views::View {
public:
METADATA_HEADER(BraveOmniboxResultSelectionIndicator);
static constexpr int kStrokeThickness = 3;
explicit BraveOmniboxResultSelectionIndicator(
BraveSearchConversionPromotionView* parent_view)
: parent_view_(parent_view) {
SetPreferredSize({kStrokeThickness, 0});
}
// views::View:
void OnPaint(gfx::Canvas* canvas) override {
SkPath path = GetPath();
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setColor(color_);
flags.setStyle(cc::PaintFlags::kFill_Style);
canvas->DrawPath(path, flags);
}
// views::View:
void OnThemeChanged() override {
views::View::OnThemeChanged();
color_ = views::GetCascadingAccentColor(parent_view_);
}
private:
SkColor color_;
// Pointer to the parent view.
const raw_ptr<BraveSearchConversionPromotionView> parent_view_;
// The focus bar is a straight vertical line with half-rounded endcaps. Since
// this geometry is nontrivial to represent using primitives, it's instead
// represented using a fill path. This matches the style and implementation
// used in Tab Groups.
SkPath GetPath() const {
SkPath path;
path.moveTo(0, 0);
path.arcTo(kStrokeThickness, kStrokeThickness, 0, SkPath::kSmall_ArcSize,
SkPathDirection::kCW, kStrokeThickness, kStrokeThickness);
path.lineTo(kStrokeThickness, height() - kStrokeThickness);
path.arcTo(kStrokeThickness, kStrokeThickness, 0, SkPath::kSmall_ArcSize,
SkPathDirection::kCW, 0, height());
path.close();
return path;
}
};
BEGIN_METADATA(BraveOmniboxResultSelectionIndicator, views::View)
END_METADATA
} // namespace
BraveSearchConversionPromotionView::BraveSearchConversionPromotionView(
BraveOmniboxResultView* result_view)
: result_view_(result_view),
mouse_enter_exit_handler_(base::BindRepeating(
&BraveSearchConversionPromotionView::UpdateHoverState,
base::Unretained(this))) {
SetLayoutManager(std::make_unique<views::FlexLayout>());
}
BraveSearchConversionPromotionView::~BraveSearchConversionPromotionView() =
default;
void BraveSearchConversionPromotionView::ResetChildrenVisibility() {
// Reset all children visibility and configure later based on type.
if (button_type_container_)
button_type_container_->SetVisible(false);
if (banner_type_container_)
banner_type_container_->SetVisible(false);
}
void BraveSearchConversionPromotionView::SetTypeAndInput(
brave_search_conversion::ConversionType type,
const std::u16string& input) {
DCHECK_NE(ConversionType::kNone, type);
type_ = type;
input_ = input;
selected_ = false;
ResetChildrenVisibility();
if (type_ == ConversionType::kButton) {
ConfigureForButtonType();
} else {
ConfigureForBannerType();
}
UpdateState();
}
void BraveSearchConversionPromotionView::OnSelectionStateChanged(
bool selected) {
selected_ = selected;
UpdateState();
}
void BraveSearchConversionPromotionView::UpdateState() {
if (type_ == ConversionType::kButton) {
UpdateButtonTypeState();
} else {
UpdateBannerTypeState();
}
}
void BraveSearchConversionPromotionView::OpenMatch() {
result_view_->OpenMatch();
}
void BraveSearchConversionPromotionView::Dismiss() {
result_view_->Dismiss();
}
void BraveSearchConversionPromotionView::UpdateButtonTypeState() {
if (!button_type_container_)
return;
button_type_container_->SetVisible(true);
button_type_selection_indicator_->SetVisible(selected_);
button_type_contents_input_->SetText(input_);
if (auto* tp = GetThemeProvider()) {
auto desc_color_id =
BraveThemeProperties::COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_NORMAL;
if (IsMouseHovered()) {
desc_color_id = BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_DESC_HOVERED;
}
button_type_description_->SetEnabledColor(tp->GetColor(desc_color_id));
append_for_input_->SetEnabledColor(
tp->GetColor(BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_INPUT_APPEND));
}
SetBackground(GetButtonTypeBackground());
}
void BraveSearchConversionPromotionView::UpdateBannerTypeState() {
if (!banner_type_container_)
return;
banner_type_container_->SetVisible(true);
SkColor desc_color = gfx::kPlaceholderColor;
SkColor border_color = gfx::kPlaceholderColor;
if (auto* tp = GetThemeProvider()) {
desc_color = tp->GetColor(
BraveThemeProperties::COLOR_SEARCH_CONVERSION_BANNER_TYPE_DESC_TEXT);
border_color =
tp->GetColor(BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BANNER_TYPE_BACKGROUND_BORDER);
}
banner_type_container_->SetBorder(
views::CreateRoundedRectBorder(1, kBannerTypeRadius, border_color));
banner_type_description_->SetEnabledColor(desc_color);
SetBackground(views::CreateSolidBackground(
GetOmniboxColor(GetThemeProvider(), OmniboxPart::RESULTS_BACKGROUND)));
banner_type_container_->SetBackground(
std::make_unique<HorizontalGradientBackground>());
}
void BraveSearchConversionPromotionView::ConfigureForButtonType() {
if (button_type_container_)
return;
button_type_container_ = AddChildView(std::make_unique<views::View>());
button_type_container_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded));
button_type_container_
->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal);
auto* selection_container =
button_type_container_->AddChildView(std::make_unique<views::View>());
selection_container->SetPreferredSize(gfx::Size(3, height()));
selection_container->SetLayoutManager(std::make_unique<views::FillLayout>());
button_type_selection_indicator_ = selection_container->AddChildView(
std::make_unique<BraveOmniboxResultSelectionIndicator>(this));
auto& bundle = ui::ResourceBundle::GetSharedInstance();
auto* icon_view =
button_type_container_->AddChildView(std::make_unique<views::ImageView>(
ui::ImageModel::FromImageSkia(*bundle.GetImageSkiaNamed(
IDR_BRAVE_SEARCH_LOGO_IN_SEARCH_PROMOTION))));
icon_view->SetProperty(views::kMarginsKey, gfx::Insets::TLBR(14, 12, 36, 8));
auto* contents_container =
button_type_container_->AddChildView(std::make_unique<views::View>());
contents_container->SetProperty(views::kMarginsKey,
gfx::Insets::TLBR(12, 12, 12, 0));
contents_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(), 2));
auto* input_container =
contents_container->AddChildView(std::make_unique<views::View>());
input_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 8));
// Use 14px font size for input text.
views::Label::CustomFont emphasized_font = {
GetFont(14, gfx::Font::Weight::SEMIBOLD)};
button_type_contents_input_ = input_container->AddChildView(
std::make_unique<views::Label>(input_, emphasized_font));
button_type_contents_input_->SetAutoColorReadabilityEnabled(false);
button_type_contents_input_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
views::Label::CustomFont normal_font = {
GetFont(14, gfx::Font::Weight::NORMAL)};
append_for_input_ =
input_container->AddChildView(std::make_unique<views::Label>(
brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_INPUT_APPEND_LABEL),
normal_font));
append_for_input_->SetAutoColorReadabilityEnabled(false);
append_for_input_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
// Configure description
const std::u16string first_part = brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_DESC_LABEL_FIRST_PART);
const std::u16string desc_label = brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_DESC_LABEL);
button_type_description_ = contents_container->AddChildView(
std::make_unique<views::Label>(desc_label, normal_font));
button_type_description_->SetAutoColorReadabilityEnabled(false);
button_type_description_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
button_type_description_->SetTextStyleRange(
ChromeTextStyle::STYLE_EMPHASIZED, gfx::Range(0, first_part.length()));
contents_container->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded)
.WithOrder(2));
auto* try_button = button_type_container_->AddChildView(
std::make_unique<TryButton>(views::Button::PressedCallback(
base::BindRepeating(&BraveSearchConversionPromotionView::OpenMatch,
base::Unretained(this)))));
try_button->SetProperty(views::kMarginsKey, gfx::Insets::TLBR(17, 0, 17, 0));
try_button->SetProminent(true);
try_button->SetText(brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_TRY_BUTTON_LABEL));
try_button->SetFontSize(12);
auto* dismiss_button = button_type_container_->AddChildView(
std::make_unique<ButtonTypeDismissButton>(views::Button::PressedCallback(
base::BindRepeating(&BraveSearchConversionPromotionView::Dismiss,
base::Unretained(this)))));
views::SetImageFromVectorIconWithColor(
dismiss_button, vector_icons::kCloseRoundedIcon,
GetLayoutConstant(LOCATION_BAR_ICON_SIZE),
SkColorSetRGB(0x6B, 0x70, 0x84), gfx::kPlaceholderColor);
views::InstallCircleHighlightPathGenerator(dismiss_button);
views::FocusRing::Install(dismiss_button);
dismiss_button->SetProperty(views::kMarginsKey, gfx::Insets::VH(25, 8));
dismiss_button->SetTooltipText(brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_DISMISS_BUTTON_TOOLTIP));
mouse_enter_exit_handler_.ObserveMouseEnterExitOn(button_type_container_);
}
void BraveSearchConversionPromotionView::ConfigureForBannerType() {
if (banner_type_container_)
return;
banner_type_container_ =
AddChildView(std::make_unique<BannerTypeContainer>());
banner_type_container_->SetProperty(
views::kMarginsKey,
gfx::Insets::TLBR(kBannerTypeMargin, kBannerTypeMargin,
kBannerTypeMarginBottom, kBannerTypeMargin));
banner_type_container_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded));
banner_type_container_
->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal);
// contents has title/label and buttons.
auto* banner_contents =
banner_type_container_->AddChildView(std::make_unique<views::View>());
banner_contents->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kVertical);
banner_contents->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded)
.WithOrder(2));
banner_contents->SetProperty(views::kMarginsKey,
gfx::Insets::TLBR(16, 16, 0, 0));
const std::u16string title_label =
brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_BANNER_TITLE);
views::Label::CustomFont title_font = {
GetFont(16, gfx::Font::Weight::SEMIBOLD)};
auto* banner_title = banner_contents->AddChildView(
std::make_unique<views::Label>(title_label, title_font));
banner_title->SetAutoColorReadabilityEnabled(false);
banner_title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
const std::u16string desc_label = brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_PROMOTION_BANNER_DESC);
views::Label::CustomFont desc_font = {GetFont(14, gfx::Font::Weight::NORMAL)};
banner_type_description_ = banner_contents->AddChildView(
std::make_unique<views::Label>(desc_label, desc_font));
banner_type_description_->SetProperty(views::kMarginsKey,
gfx::Insets::TLBR(4, 0, 0, 0));
banner_type_description_->SetAutoColorReadabilityEnabled(false);
banner_type_description_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
auto* button_row =
banner_contents->AddChildView(std::make_unique<views::View>());
button_row->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kPreferred));
button_row->SetProperty(views::kMarginsKey, gfx::Insets::TLBR(12, 0, 0, 0));
button_row->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal);
auto* try_button = button_row->AddChildView(
std::make_unique<TryButton>(views::Button::PressedCallback(
base::BindRepeating(&BraveSearchConversionPromotionView::OpenMatch,
base::Unretained(this)))));
try_button->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kPreferred));
try_button->SetProminent(true);
try_button->SetText(brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_TRY_BUTTON_LABEL));
try_button->SetFontSize(14);
auto* dismiss_button = button_row->AddChildView(
std::make_unique<BannerTypeDismissButton>(views::Button::PressedCallback(
base::BindRepeating(&BraveSearchConversionPromotionView::Dismiss,
base::Unretained(this)))));
dismiss_button->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kPreferred));
dismiss_button->SetText(brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_DISMISS_BUTTON_LABEL));
dismiss_button->SetFontSize(14);
dismiss_button->SetTextColor(views::Button::STATE_NORMAL,
SkColorSetRGB(0xff, 0x76, 0x54));
dismiss_button->SetTextColor(views::Button::STATE_PRESSED,
SkColorSetRGB(0xff, 0x76, 0x54));
dismiss_button->SetTextColor(views::Button::STATE_HOVERED,
SkColorSetRGB(0xff, 0x97, 0x7d));
dismiss_button->SetProperty(views::kMarginsKey,
gfx::Insets::TLBR(0, 13, 0, 0));
dismiss_button->SetTooltipText(brave_l10n::GetLocalizedResourceUTF16String(
IDS_BRAVE_SEARCH_CONVERSION_DISMISS_BUTTON_TOOLTIP));
}
std::unique_ptr<views::Background>
BraveSearchConversionPromotionView::GetButtonTypeBackground() {
SkColor bg = gfx::kPlaceholderColor;
if (auto* tp = GetThemeProvider()) {
auto bg_color_id = BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_NORMAL;
if (IsMouseHovered()) {
bg_color_id = BraveThemeProperties::
COLOR_SEARCH_CONVERSION_BUTTON_TYPE_BACKGROUND_HOVERED;
}
bg = tp->GetColor(bg_color_id);
}
return views::CreateSolidBackground(bg);
}
gfx::Size BraveSearchConversionPromotionView::CalculatePreferredSize() const {
const int height = type_ == ConversionType::kButton ? 66 : 132;
return gfx::Size(0, height);
}
void BraveSearchConversionPromotionView::OnThemeChanged() {
View::OnThemeChanged();
UpdateState();
}
void BraveSearchConversionPromotionView::UpdateHoverState() {
UpdateState();
}
BEGIN_METADATA(BraveSearchConversionPromotionView, views::View)
END_METADATA
@@ -0,0 +1,81 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_SEARCH_CONVERSION_PROMOTION_VIEW_H_
#define BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_SEARCH_CONVERSION_PROMOTION_VIEW_H_
#include <memory>
#include <string>
#include "base/memory/raw_ptr.h"
#include "brave/components/brave_search_conversion/types.h"
#include "chrome/browser/ui/views/omnibox/omnibox_mouse_enter_exit_handler.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/view.h"
class BraveOmniboxResultView;
namespace views {
class Background;
class Label;
} // namespace views
class BraveSearchConversionPromotionView : public views::View {
public:
METADATA_HEADER(BraveSearchConversionPromotionView);
explicit BraveSearchConversionPromotionView(
BraveOmniboxResultView* result_view);
BraveSearchConversionPromotionView(
const BraveSearchConversionPromotionView&) = delete;
BraveSearchConversionPromotionView& operator=(
const BraveSearchConversionPromotionView&) = delete;
~BraveSearchConversionPromotionView() override;
void SetTypeAndInput(brave_search_conversion::ConversionType type,
const std::u16string& input);
void OnSelectionStateChanged(bool selected);
// views::View overrides:
gfx::Size CalculatePreferredSize() const override;
void OnThemeChanged() override;
private:
void ConfigureForButtonType();
void UpdateButtonTypeState();
void ConfigureForBannerType();
void UpdateBannerTypeState();
void ResetChildrenVisibility();
void UpdateHoverState();
void UpdateState();
void OpenMatch();
void Dismiss();
// Gives buttton type background based on selected or hovered state.
std::unique_ptr<views::Background> GetButtonTypeBackground();
raw_ptr<BraveOmniboxResultView> result_view_ = nullptr;
// Children for button type promotion.
raw_ptr<views::View> button_type_container_ = nullptr;
raw_ptr<views::View> button_type_selection_indicator_ = nullptr;
raw_ptr<views::Label> button_type_description_ = nullptr;
raw_ptr<views::Label> button_type_contents_input_ = nullptr;
raw_ptr<views::Label> append_for_input_ = nullptr;
// Children for banner type promotion.
raw_ptr<views::View> banner_type_container_ = nullptr;
raw_ptr<views::Label> banner_type_description_ = nullptr;
brave_search_conversion::ConversionType type_ =
brave_search_conversion::ConversionType::kNone;
bool selected_ = false;
std::u16string input_;
// Keeps track of mouse-enter and mouse-exit events of child Views.
OmniboxMouseEnterExitHandler mouse_enter_exit_handler_;
};
#endif // BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_SEARCH_CONVERSION_PROMOTION_VIEW_H_
@@ -0,0 +1,8 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "brave/browser/ui/views/omnibox/brave_omnibox_result_view.h"
#include "src/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc"
@@ -0,0 +1,20 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_CHROMIUM_SRC_CHROME_BROWSER_UI_VIEWS_OMNIBOX_OMNIBOX_RESULT_VIEW_H_
#define BRAVE_CHROMIUM_SRC_CHROME_BROWSER_UI_VIEWS_OMNIBOX_OMNIBOX_RESULT_VIEW_H_
#define SetMatch \
virtual SetMatch(const AutocompleteMatch& match); \
friend class BraveOmniboxResultView; \
void SetMatchUnUsed
#define OnSelectionStateChanged virtual OnSelectionStateChanged
#include "src/chrome/browser/ui/views/omnibox/omnibox_result_view.h"
#undef OnSelectionStateChanged
#undef SetMatch
#endif // BRAVE_CHROMIUM_SRC_CHROME_BROWSER_UI_VIEWS_OMNIBOX_OMNIBOX_RESULT_VIEW_H_
@@ -0,0 +1,13 @@
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index a5405c946c95ec09f66df6ffc279a6d7bba5d212..5ca0e49f440fac12d2916f1c6ee7582718a468a8 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -348,7 +348,7 @@ void OmniboxPopupContentsView::UpdatePopupAppearance() {
if (children().size() == i) {
AddChildView(std::make_unique<OmniboxRowView>(
i, edit_model_,
- std::make_unique<OmniboxResultView>(this, edit_model_, i),
+ std::make_unique<BraveOmniboxResultView>(this, edit_model_, i),
pref_service));
}