0

Update education footer text in clipboard history menu.

When the clipboard history menu is not shown as a result of a Ctrl+V
long press event, the education footer has different text.

Note that the logic which shows the footer at the appropriate times
has not yet been implemented.

Keyboard w/ Search key: http://shortn/_GiwsaGrHPN
Keyboard w/ Launcher key, Assistant enabled: http://shortn/_l0VUEPNT4H
Keyboard w/ Launcher key, Assistant disabled: http://shortn/_cxEi9XFi0J

Bug: b:267694412
Change-Id: I0503378f3c3c3abccdb72c0ae26c39b80f1b9146
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4750340
Reviewed-by: Angus McLean <angusmclean@google.com>
Commit-Queue: David Black <dmblack@google.com>
Cr-Commit-Position: refs/heads/main@{#1179364}
This commit is contained in:
David Black
2023-08-04 00:27:04 +00:00
committed by Chromium LUCI CQ
parent 56e579403a
commit 5a76f69a71
7 changed files with 127 additions and 24 deletions

@ -6028,6 +6028,9 @@ Here are some things you can try to get started.
<message name="IDS_ASH_CLIPBOARD_HISTORY_CONTROL_V_LONGPRESS_FOOTER" desc="The message used to educate users about the clipboard history menu that shows when they hold down Ctrl+V.">
Youll see the clipboard when you press and hold Ctrl + V. You can turn off this shortcut by disabling the #clipboard-history-longpress flag in chrome://flags (os://flags if using Lacros).
</message>
<message name="IDS_ASH_CLIPBOARD_HISTORY_FOOTER" desc="The message used to educate users about the clipboard history menu that shows when they use the feature for the first time (or for the first time in a long time).">
Select an item to paste it. You can see the clipboard by pressing <ph name="SHORTCUT_KEY_NAME">$1<ex>Launcher</ex></ph> + v.
</message>
<message name="IDS_ASH_CLIPBOARD_HISTORY_HEADER_TITLE" desc="The title of the ChromeOS clipboard history menu, which shows the user's most recently copied items.">
Clipboard
</message>

@ -0,0 +1 @@
2a12f38fbc26673c249afd40154e9b401f013880

@ -57,6 +57,11 @@ namespace ash {
namespace {
// Returns a font list resolved from the specified `typography_token`.
gfx::FontList Resolve(TypographyToken typography_token) {
return TypographyProvider::Get()->ResolveTypographyToken(typography_token);
}
// Returns whether the clipboard history menu requires a header.
bool IsHeaderRequired() {
return chromeos::features::IsClipboardHistoryRefreshEnabled();
@ -94,10 +99,74 @@ void InsertHeaderContent(views::MenuItemView* container) {
}
// TODO(http://b/267694412): Add pixel test.
// Populates `container` with education content to appear at the bottom of the
// Populates `styled_label` with educational text to appear at the bottom of the
// clipboard history menu. This method may only be called when clipboard history
// refresh is enabled.
void InsertFooterContentV2(views::MenuItemView* container) {
void InsertFooterContentV2LabelStyledText(
crosapi::mojom::ClipboardHistoryControllerShowSource show_source,
views::StyledLabel* styled_label) {
CHECK(chromeos::features::IsClipboardHistoryRefreshEnabled());
// Create text style.
views::StyledLabel::RangeStyleInfo text_style;
text_style.custom_font = Resolve(TypographyToken::kCrosAnnotation1);
text_style.override_color_id = cros_tokens::kCrosSysOnSurfaceVariant;
// When the clipboard history menu is shown from a Ctrl+V long press event, a
// specific educational text is used which does not require inline icons.
using crosapi::mojom::ClipboardHistoryControllerShowSource;
if (show_source == ClipboardHistoryControllerShowSource::kControlVLongpress) {
styled_label->SetText(l10n_util::GetStringUTF16(
IDS_ASH_CLIPBOARD_HISTORY_CONTROL_V_LONGPRESS_FOOTER));
styled_label->AddStyleRange(gfx::Range(0u, styled_label->GetText().size()),
std::move(text_style));
return;
}
// When the clipboard history menu is *not* shown from a Ctrl+V long press
// event, set text based on keyboard layout, caching the offset where an
// inline icon should be inserted.
size_t inline_icon_offset;
const auto& shortcut_key = clipboard_history_util::GetShortcutKeyName();
styled_label->SetText(l10n_util::GetStringFUTF16(
IDS_ASH_CLIPBOARD_HISTORY_FOOTER, shortcut_key, &inline_icon_offset));
inline_icon_offset += shortcut_key.size();
// Set text styles.
styled_label->AddStyleRange(gfx::Range(0u, inline_icon_offset), text_style);
styled_label->AddStyleRange(
gfx::Range(inline_icon_offset + 1u, styled_label->GetText().size()),
std::move(text_style));
// Create inline icon.
auto inline_icon =
views::Builder<views::ImageView>()
.SetBorder(views::CreateEmptyBorder(
ClipboardHistoryViews::kInlineIconMargins))
.SetImage(ui::ImageModel::FromVectorIcon(
clipboard_history_util::GetShortcutKeyIcon(),
cros_tokens::kCrosSysOnSurfaceVariant,
ClipboardHistoryViews::kFooterContentV2InlineIconSize))
.Build();
// Insert inline icon.
views::StyledLabel::RangeStyleInfo inline_icon_style;
inline_icon_style.custom_view = inline_icon.get();
styled_label->AddStyleRange(
gfx::Range(inline_icon_offset, inline_icon_offset + 1u),
std::move(inline_icon_style));
// Transfer inline icon ownership.
styled_label->AddCustomView(std::move(inline_icon));
}
// TODO(http://b/267694412): Add pixel test.
// Populates `container` with educational content to appear at the bottom of the
// clipboard history menu. This method may only be called when clipboard history
// refresh is enabled.
void InsertFooterContentV2(
views::MenuItemView* container,
crosapi::mojom::ClipboardHistoryControllerShowSource show_source) {
CHECK(chromeos::features::IsClipboardHistoryRefreshEnabled());
// Cache `menu_padding`.
@ -132,38 +201,25 @@ void InsertFooterContentV2(views::MenuItemView* container) {
ClipboardHistoryViews::kFooterContentV2IconSize)),
views::Builder<views::StyledLabel>()
.SetAutoColorReadabilityEnabled(false)
.SetText(l10n_util::GetStringUTF16(
IDS_ASH_CLIPBOARD_HISTORY_CONTROL_V_LONGPRESS_FOOTER))
.SetID(clipboard_history_util::kFooterContentV2LabelID)
.SizeToFit(
footer_width -
ClipboardHistoryViews::kFooterContentV2Insets.width() -
ClipboardHistoryViews::kFooterContentV2IconSize -
ClipboardHistoryViews::kFooterContentV2ChildSpacing)
.CustomConfigure(
base::BindOnce([](views::StyledLabel* label) {
views::StyledLabel::RangeStyleInfo style;
style.custom_font =
TypographyProvider::Get()->ResolveTypographyToken(
TypographyToken::kCrosAnnotation1);
style.override_color_id =
cros_tokens::kCrosSysOnSurfaceVariant;
// NOTE: `SetText()` must precede `AddStyleRange()`.
label->AddStyleRange(
gfx::Range(0u, label->GetText().size()),
std::move(style));
})))
.CustomConfigure(base::BindOnce(
&InsertFooterContentV2LabelStyledText, show_source)))
.Build());
}
// TODO(http://b/267694412): Add pixel test.
// Populates `container` with educational content to appear at the bottom of the
// clipboard history menu.
void InsertFooterContent(views::MenuItemView* container) {
void InsertFooterContent(
views::MenuItemView* container,
crosapi::mojom::ClipboardHistoryControllerShowSource show_source) {
if (chromeos::features::IsClipboardHistoryRefreshEnabled()) {
InsertFooterContentV2(container);
InsertFooterContentV2(container, show_source);
return;
}
@ -286,6 +342,7 @@ void ClipboardHistoryMenuModelAdapter::Run(
run_before_ = true;
menu_open_time_ = base::TimeTicks::Now();
menu_show_source_ = show_source;
int command_id = clipboard_history_util::kFirstItemCommandId;
const auto& items = clipboard_history_->GetItems();
@ -642,7 +699,7 @@ views::MenuItemView* ClipboardHistoryMenuModelAdapter::AppendMenuItem(
InsertHeaderContent(container);
} else if (footer_index_ == index) {
CHECK_EQ(model->GetTypeAt(index), ui::MenuModel::ItemType::TYPE_TITLE);
InsertFooterContent(container);
InsertFooterContent(container, menu_show_source_.value());
} else {
CHECK_EQ(model->GetTypeAt(index), ui::MenuModel::ItemType::TYPE_COMMAND);
std::unique_ptr<ClipboardHistoryItemView> item_view =

@ -146,6 +146,10 @@ class ASH_EXPORT ClipboardHistoryMenuModelAdapter
// The timestamp taken when the menu is opened. Used in metrics.
base::TimeTicks menu_open_time_;
// The source which opened the menu, absent until the menu is `Run()`.
absl::optional<crosapi::mojom::ClipboardHistoryControllerShowSource>
menu_show_source_;
// The mapping between the command ids and items that are copied from
// `clipboard_history_` when the menu is created. It is used to solve the
// possible inconsistency between the menu model data and the clipboard

@ -4,12 +4,15 @@
#include "ash/clipboard/clipboard_history_menu_model_adapter.h"
#include <string>
#include "ash/clipboard/clipboard_history.h"
#include "ash/clipboard/clipboard_history_controller_impl.h"
#include "ash/clipboard/clipboard_history_util.h"
#include "ash/clipboard/views/clipboard_history_view_constants.h"
#include "ash/constants/ash_features.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
@ -19,11 +22,13 @@
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/view_utils.h"
namespace ash {
@ -36,6 +41,7 @@ using ::testing::Eq;
using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::Property;
using ::testing::ResultOf;
using ::testing::ValuesIn;
using ::testing::WithParamInterface;
@ -43,6 +49,8 @@ using crosapi::mojom::ClipboardHistoryControllerShowSource;
namespace {
// Helpers ---------------------------------------------------------------------
ClipboardHistoryControllerImpl* GetClipboardHistoryController() {
return Shell::Get()->clipboard_history_controller();
}
@ -66,8 +74,21 @@ void FlushMessageLoop() {
run_loop.Run();
}
// Matchers --------------------------------------------------------------------
template <typename ViewType, typename MatcherType>
auto GetViewById(int id, MatcherType m) {
return ResultOf(
[id](const auto* arg) {
return views::AsViewClass<ViewType>(arg->GetViewByID(id));
},
m);
}
} // namespace
// ClipboardHistoryMenuModelAdapterRefreshTest ---------------------------------
// Base class for `ClipboardHistoryMenuModelAdapter` tests whose only required
// parameterization is whether the clipboard history refresh is enabled.
class ClipboardHistoryMenuModelAdapterRefreshTest
@ -321,7 +342,20 @@ TEST_P(ClipboardHistoryMenuModelAdapterMenuItemTest,
Conditional(IsClipboardHistoryRefreshEnabled(), IsNull(), NotNull()));
EXPECT_THAT(
footer->GetViewByID(clipboard_history_util::kFooterContentV2ViewID),
Conditional(IsClipboardHistoryRefreshEnabled(), NotNull(), IsNull()));
Conditional(
IsClipboardHistoryRefreshEnabled(),
GetViewById<views::StyledLabel>(
clipboard_history_util::kFooterContentV2LabelID,
Property(
&views::StyledLabel::GetText,
Conditional(
IsClipboardHistoryLongpressEnabled(),
l10n_util::GetStringUTF16(
IDS_ASH_CLIPBOARD_HISTORY_CONTROL_V_LONGPRESS_FOOTER),
l10n_util::GetStringFUTF16(
IDS_ASH_CLIPBOARD_HISTORY_FOOTER,
clipboard_history_util::GetShortcutKeyName())))),
IsNull()));
}
} // namespace ash

@ -63,6 +63,7 @@ enum MenuViewID {
kDeleteButtonViewID,
kDisplayTextLabelID,
kFooterContentViewID,
kFooterContentV2LabelID,
kFooterContentV2ViewID,
kSecondaryDisplayTextLabelID,
};

@ -25,6 +25,9 @@ constexpr int kFooterContentV2ChildSpacing = 6;
// The icon size for footer content v2.
constexpr int kFooterContentV2IconSize = 20;
// The inline icon size for footer content v2.
constexpr int kFooterContentV2InlineIconSize = 16;
// The insets for footer content v2.
constexpr auto kFooterContentV2Insets = gfx::Insets(8);