Commit 8ba027e4 authored by My Nguyen's avatar My Nguyen Committed by Commit Bot

Update emoji suggestion UI

First cut based on spec at http://go/e14s-emoji-addition-mock

Bug: 1093178
Change-Id: I274d95df44ccc04d03fddec60b90b6a679ee3000
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2239908Reviewed-by: default avatarKeith Lee <keithlee@chromium.org>
Reviewed-by: default avatarDarren Shen <shend@chromium.org>
Commit-Queue: My Nguyen <myy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#777274}
parent f3fb2edb
......@@ -106,6 +106,18 @@ void AssistiveWindowController::ShowSuggestion(const base::string16& text,
suggestion_window_view_->Show(text, confirmed_length, show_tab);
}
void AssistiveWindowController::ShowMultipleSuggestions(
const std::vector<base::string16>& suggestions) {
if (!suggestion_window_view_)
InitSuggestionWindow();
suggestion_window_view_->ShowMultipleCandidates(suggestions);
}
void AssistiveWindowController::HighlightSuggestionCandidate(int index) {
if (suggestion_window_view_)
suggestion_window_view_->HighlightCandidate(index);
}
base::string16 AssistiveWindowController::GetSuggestionText() const {
return suggestion_text_;
}
......
......@@ -45,6 +45,9 @@ class AssistiveWindowController : public views::WidgetObserver,
void ShowSuggestion(const base::string16& text,
const size_t confirmed_length,
const bool show_tab) override;
void ShowMultipleSuggestions(
const std::vector<base::string16>& suggestions) override;
void HighlightSuggestionCandidate(int index) override;
void HideSuggestion() override;
base::string16 GetSuggestionText() const override;
size_t GetConfirmedLength() const override;
......
......@@ -20,7 +20,6 @@ namespace {
const int kMaxCandidateSize = 5;
const char kSpaceChar = ' ';
const char kDefaultEngine[] = "default_engine";
const base::FilePath::CharType kEmojiMapFilePath[] =
FILE_PATH_LITERAL("/emoji/emoji-map.csv");
const int kMaxSuggestionIndex = 31;
......@@ -60,17 +59,6 @@ std::string GetLastWord(const std::string& str) {
last_pos_to_search - space_before_last_word);
}
// Create emoji suggestion's candidate window property.
InputMethodEngine::CandidateWindowProperty CreateProperty(int candidates_size) {
InputMethodEngine::CandidateWindowProperty properties_out;
properties_out.is_cursor_visible = true;
properties_out.page_size = std::min(candidates_size, kMaxCandidateSize);
properties_out.show_window_at_composition = false;
properties_out.is_vertical = true;
properties_out.is_auxiliary_text_visible = false;
return properties_out;
}
} // namespace
EmojiSuggester::EmojiSuggester(InputMethodEngine* engine) : engine_(engine) {
......@@ -97,9 +85,11 @@ void EmojiSuggester::OnEmojiDataLoaded(const std::string& emoji_data) {
const auto comma_pos = line.find_first_of(",");
DCHECK(comma_pos != std::string::npos);
std::string word = line.substr(0, comma_pos);
std::string emojis = line.substr(comma_pos + 1);
base::string16 emojis = base::UTF8ToUTF16(line.substr(comma_pos + 1));
// Build emoji_map_ from splitting the string of emojis.
emoji_map_[word] = SplitString(emojis, ";");
emoji_map_[word] =
base::SplitString(emojis, base::UTF8ToUTF16(";"), base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
// TODO(crbug/1093179): Implement arrow to indicate more emojis available.
// Only loads 5 emojis for now until arrow is implemented.
if (emoji_map_[word].size() > kMaxCandidateSize)
......@@ -130,22 +120,21 @@ SuggestionStatus EmojiSuggester::HandleKeyEvent(
std::string error;
if (event.key == "Tab" || event.key == "Right" || event.key == "Enter") {
suggestion_shown_ = false;
engine_->CommitText(context_id_, candidates_[candidate_id_].value.c_str(),
&error);
engine_->SetCandidateWindowVisible(false, &error);
engine_->AcceptSuggestionCandidate(context_id_, candidates_[candidate_id_],
&error);
RecordAcceptanceIndex(candidate_id_);
status = SuggestionStatus::kAccept;
} else if (event.key == "Down") {
candidate_id_ < static_cast<int>(candidates_.size()) - 1
? candidate_id_++
: candidate_id_ = 0;
engine_->SetCursorPosition(context_id_, candidate_id_, &error);
engine_->HighlightSuggestionCandidate(context_id_, candidate_id_, &error);
status = SuggestionStatus::kBrowsing;
} else if (event.key == "Up") {
candidate_id_ > 0
? candidate_id_--
: candidate_id_ = static_cast<int>(candidates_.size()) - 1;
engine_->SetCursorPosition(context_id_, candidate_id_, &error);
engine_->HighlightSuggestionCandidate(context_id_, candidate_id_, &error);
status = SuggestionStatus::kBrowsing;
} else if (event.key == "Esc") {
DismissSuggestion();
......@@ -173,20 +162,9 @@ void EmojiSuggester::ShowSuggestion(const std::string& text) {
std::string error;
suggestion_shown_ = true;
candidates_.clear();
const std::vector<std::string>& candidates = emoji_map_.at(text);
for (size_t i = 0; i < candidates.size(); i++) {
candidates_.emplace_back();
candidates_.back().value = candidates[i];
candidates_.back().id = i;
candidates_.back().label = base::UTF16ToUTF8(base::FormatNumber(i + 1));
}
engine_->SetCandidates(context_id_, candidates_, &error);
candidate_id_ = 0;
engine_->SetCandidateWindowProperty(
kDefaultEngine, CreateProperty(static_cast<int>(candidates_.size())));
engine_->SetCandidateWindowVisible(true, &error);
engine_->SetCursorPosition(context_id_, candidate_id_, &error);
candidate_id_ = -1;
candidates_ = emoji_map_.at(text);
engine_->ShowMultipleSuggestions(context_id_, candidates_, &error);
if (!error.empty()) {
LOG(ERROR) << "Fail to show suggestion. " << error;
}
......@@ -195,7 +173,7 @@ void EmojiSuggester::ShowSuggestion(const std::string& text) {
void EmojiSuggester::DismissSuggestion() {
std::string error;
suggestion_shown_ = false;
engine_->SetCandidateWindowVisible(false, &error);
engine_->DismissSuggestion(context_id_, &error);
if (!error.empty()) {
LOG(ERROR) << "Failed to dismiss suggestion. " << error;
}
......
......@@ -47,13 +47,13 @@ class EmojiSuggester : public Suggester {
bool suggestion_shown_ = false;
// The current list of candidates.
std::vector<InputMethodEngine::Candidate> candidates_;
std::vector<base::string16> candidates_;
// The current candidate_id chosen.
int candidate_id_;
int candidate_id_ = -1;
// The map holding one-word-mapping to emojis.
std::map<std::string, std::vector<std::string>> emoji_map_;
std::map<std::string, std::vector<base::string16>> emoji_map_;
// Pointer for callback, must be the last declared in the file.
base::WeakPtrFactory<EmojiSuggester> weak_factory_{this};
......
......@@ -139,6 +139,65 @@ void InputMethodEngine::OnSuggestionsChanged(
observer_->OnSuggestionsChanged(suggestions);
}
bool InputMethodEngine::ShowMultipleSuggestions(
int context_id,
const std::vector<base::string16>& suggestions,
std::string* error) {
if (!IsActive()) {
*error = kErrorNotActive;
return false;
}
if (context_id != context_id_ || context_id_ == -1) {
*error = kErrorWrongContext;
return false;
}
IMEAssistiveWindowHandlerInterface* aw_handler =
ui::IMEBridge::Get()->GetAssistiveWindowHandler();
if (aw_handler)
aw_handler->ShowMultipleSuggestions(suggestions);
return true;
}
bool InputMethodEngine::HighlightSuggestionCandidate(int context_id,
int index,
std::string* error) {
if (!IsActive()) {
*error = kErrorNotActive;
return false;
}
if (context_id != context_id_ || context_id_ == -1) {
*error = kErrorWrongContext;
return false;
}
IMEAssistiveWindowHandlerInterface* aw_handler =
ui::IMEBridge::Get()->GetAssistiveWindowHandler();
if (aw_handler)
aw_handler->HighlightSuggestionCandidate(index);
return true;
}
bool InputMethodEngine::AcceptSuggestionCandidate(
int context_id,
const base::string16& suggestion,
std::string* error) {
if (!IsActive()) {
*error = kErrorNotActive;
return false;
}
if (context_id != context_id_ || context_id_ == -1) {
*error = kErrorWrongContext;
return false;
}
CommitText(context_id, base::UTF16ToUTF8(suggestion).c_str(), error);
IMEAssistiveWindowHandlerInterface* aw_handler =
ui::IMEBridge::Get()->GetAssistiveWindowHandler();
if (aw_handler)
aw_handler->HideSuggestion();
return true;
}
const InputMethodEngine::CandidateWindowProperty&
InputMethodEngine::GetCandidateWindowProperty(const std::string& engine_id) {
if (candidate_window_property_.first != engine_id)
......
......@@ -121,6 +121,18 @@ class InputMethodEngine : public InputMethodEngineBase,
void OnSuggestionsChanged(
const std::vector<std::string>& suggestions) override;
bool ShowMultipleSuggestions(int context_id,
const std::vector<base::string16>& suggestions,
std::string* error) override;
bool HighlightSuggestionCandidate(int context_id,
int index,
std::string* error) override;
bool AcceptSuggestionCandidate(int context_id,
const base::string16& candidate,
std::string* error) override;
// This function returns the current property of the candidate window of the
// corresponding engine_id. If the CandidateWindowProperty is not set for the
// engine_id, a default value is set. The caller can use the returned value as
......
......@@ -67,6 +67,24 @@ class TestSuggestionHandler : public SuggestionHandlerInterface {
previous_suggestions_ = suggestions;
}
bool ShowMultipleSuggestions(int context_id,
const std::vector<base::string16>& candidates,
std::string* error) override {
return false;
}
bool HighlightSuggestionCandidate(int context_id,
int index,
std::string* error) override {
return false;
}
bool AcceptSuggestionCandidate(int context_id,
const base::string16& candidate,
std::string* error) override {
return false;
}
void VerifyShowTab(const bool show_tab) { EXPECT_EQ(show_tab_, show_tab); }
bool IsSuggestionAccepted() { return suggestion_accepted_; }
......
......@@ -33,6 +33,19 @@ class SuggestionHandlerInterface {
virtual void OnSuggestionsChanged(
const std::vector<std::string>& suggestions) = 0;
virtual bool ShowMultipleSuggestions(
int context_id,
const std::vector<base::string16>& candidates,
std::string* error) = 0;
virtual bool HighlightSuggestionCandidate(int context_id,
int index,
std::string* error) = 0;
virtual bool AcceptSuggestionCandidate(int context_id,
const base::string16& candidate,
std::string* error) = 0;
};
} // namespace chromeos
......
......@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/input_method/ui/suggestion_view.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/gfx/color_utils.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/background.h"
......@@ -17,6 +18,17 @@ namespace ime {
namespace {
// Creates the index label, and returns it (never returns nullptr).
// The label text is not set in this function.
std::unique_ptr<views::Label> CreateIndexLabel() {
auto index_label = std::make_unique<views::Label>();
index_label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
index_label->SetBorder(
views::CreateEmptyBorder(gfx::Insets(kPadding / 2, 0)));
return index_label;
}
// Creates the suggestion label, and returns it (never returns nullptr).
// The label text is not set in this function.
std::unique_ptr<views::StyledLabel> CreateSuggestionLabel() {
......@@ -56,8 +68,11 @@ std::unique_ptr<views::Label> CreateAnnotationLabel() {
} // namespace
SuggestionView::SuggestionView() {
index_label_ = AddChildView(CreateIndexLabel());
index_label_->SetVisible(false);
suggestion_label_ = AddChildView(CreateSuggestionLabel());
annotation_label_ = AddChildView(CreateAnnotationLabel());
annotation_label_->SetVisible(false);
}
SuggestionView::~SuggestionView() = default;
......@@ -70,6 +85,15 @@ void SuggestionView::SetView(const base::string16& text,
annotation_label_->SetVisible(show_tab);
}
void SuggestionView::SetViewWithIndex(const base::string16& index,
const base::string16& text) {
index_label_->SetText(index);
index_label_->SetVisible(true);
index_width_ = index_label_->GetPreferredSize().width();
suggestion_label_->SetText(text);
suggestion_width_ = suggestion_label_->GetPreferredSize().width();
}
void SuggestionView::SetSuggestionText(const base::string16& text,
const size_t confirmed_length) {
// SetText clears the existing style only if the text to set is different from
......@@ -93,15 +117,41 @@ void SuggestionView::SetSuggestionText(const base::string16& text,
suggestion_style);
}
void SuggestionView::SetHighlighted(bool highlighted) {
if (highlighted_ == highlighted)
return;
highlighted_ = highlighted;
if (highlighted) {
NotifyAccessibilityEvent(ax::mojom::Event::kSelection, false);
ui::NativeTheme* theme = GetNativeTheme();
SetBackground(views::CreateSolidBackground(theme->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)));
SetBorder(views::CreateSolidBorder(
1,
theme->GetSystemColor(ui::NativeTheme::kColorId_FocusedBorderColor)));
} else {
SetBackground(nullptr);
SetBorder(views::CreateEmptyBorder(1, 1, 1, 1));
}
SchedulePaint();
}
const char* SuggestionView::GetClassName() const {
return "SuggestionView";
}
void SuggestionView::Layout() {
suggestion_label_->SetBounds(kPadding, 0, suggestion_width_, height());
int left = kPadding;
if (index_label_->GetVisible()) {
index_label_->SetBounds(left, 0, index_width_, height());
left += index_width_ + kPadding;
}
suggestion_label_->SetBounds(left, 0, suggestion_width_, height());
if (annotation_label_->GetVisible()) {
int annotation_left = kPadding + suggestion_width_ + kPadding;
int annotation_left = left + suggestion_width_ + kPadding;
int right = bounds().right();
annotation_label_->SetBounds(annotation_left, kAnnotationPaddingHeight,
right - annotation_left - kPadding / 2,
......@@ -111,7 +161,11 @@ void SuggestionView::Layout() {
gfx::Size SuggestionView::CalculatePreferredSize() const {
gfx::Size size;
if (index_label_->GetVisible()) {
size = index_label_->GetPreferredSize();
size.SetToMax(gfx::Size(index_width_, 0));
size.Enlarge(kPadding, 0);
}
gfx::Size suggestion_size = suggestion_label_->GetPreferredSize();
suggestion_size.SetToMax(gfx::Size(suggestion_width_, 0));
size.Enlarge(suggestion_size.width() + 2 * kPadding, 0);
......
......@@ -38,6 +38,11 @@ class UI_CHROMEOS_EXPORT SuggestionView : public views::View {
const size_t confirmed_length,
const bool show_tab);
void SetViewWithIndex(const base::string16& index,
const base::string16& text);
void SetHighlighted(bool highlighted);
private:
friend class SuggestionWindowViewTest;
FRIEND_TEST_ALL_PREFIXES(SuggestionWindowViewTest, ShortcutSettingTest);
......@@ -53,12 +58,15 @@ class UI_CHROMEOS_EXPORT SuggestionView : public views::View {
void SetSuggestionText(const base::string16& text,
const size_t confirmed_length);
views::Label* index_label_ = nullptr;
// The suggestion label renders suggestions.
views::StyledLabel* suggestion_label_ = nullptr;
// The annotation label renders annotations.
views::Label* annotation_label_ = nullptr;
int suggestion_width_ = 0;
int index_width_ = 0;
bool highlighted_ = false;
DISALLOW_COPY_AND_ASSIGN(SuggestionView);
};
......
......@@ -8,6 +8,7 @@
#include <string>
#include "base/i18n/number_formatting.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/input_method/ui/suggestion_view.h"
......@@ -64,7 +65,9 @@ SuggestionWindowView::SuggestionWindowView(gfx::NativeView parent) {
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
suggestion_view_ = AddChildView(std::make_unique<SuggestionView>());
candidate_area_ = AddChildView(std::make_unique<views::View>());
candidate_area_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
}
SuggestionWindowView::~SuggestionWindowView() = default;
......@@ -85,24 +88,50 @@ void SuggestionWindowView::Hide() {
GetWidget()->Close();
}
void SuggestionWindowView::MakeVisible() {
candidate_area_->SetVisible(true);
SizeToContents();
}
void SuggestionWindowView::Show(const base::string16& text,
const size_t confirmed_length,
const bool show_tab) {
UpdateSuggestion(text, confirmed_length, show_tab);
suggestion_view_->SetVisible(true);
SizeToContents();
MaybeInitializeSuggestionViews(1);
candidate_views_[0]->SetEnabled(true);
candidate_views_[0]->SetView(text, confirmed_length, show_tab);
MakeVisible();
}
void SuggestionWindowView::ShowMultipleCandidates(
const std::vector<base::string16>& candidates) {
MaybeInitializeSuggestionViews(candidates.size());
for (size_t i = 0; i < candidates.size(); i++) {
SuggestionView* candidate_view = candidate_views_[i].get();
candidate_view->SetViewWithIndex(base::FormatNumber(i + 1), candidates[i]);
candidate_view->SetEnabled(true);
}
MakeVisible();
}
void SuggestionWindowView::UpdateSuggestion(const base::string16& text,
const size_t confirmed_length,
const bool show_tab) {
suggestion_view_->SetView(text, confirmed_length, show_tab);
void SuggestionWindowView::MaybeInitializeSuggestionViews(
size_t candidates_size) {
if (candidate_views_.size() > candidates_size)
candidate_views_.resize(candidates_size);
std::unique_ptr<SuggestionWindowBorder> border =
std::make_unique<SuggestionWindowBorder>();
while (candidate_views_.size() < candidates_size) {
auto new_candidate = std::make_unique<SuggestionView>();
candidate_area_->AddChildView(new_candidate.get());
candidate_views_.push_back(std::move(new_candidate));
}
}
GetBubbleFrameView()->SetBubbleBorder(std::move(border));
GetBubbleFrameView()->OnThemeChanged();
void SuggestionWindowView::HighlightCandidate(int index) {
if (selected_index_ != -1)
candidate_views_[selected_index_]->SetHighlighted(false);
if (index < static_cast<int>(candidate_views_.size())) {
candidate_views_[index]->SetHighlighted(true);
selected_index_ = index;
}
}
void SuggestionWindowView::SetBounds(const gfx::Rect& cursor_bounds) {
......
......@@ -32,20 +32,29 @@ class UI_CHROMEOS_EXPORT SuggestionWindowView
const size_t confirmed_length,
const bool show_tab);
void ShowMultipleCandidates(const std::vector<base::string16>& candidates);
void HighlightCandidate(int index);
void SetBounds(const gfx::Rect& cursor_bounds);
private:
friend class SuggestionWindowViewTest;
void UpdateSuggestion(const base::string16& text,
const size_t confirmed_length,
const bool show_tab);
void MaybeInitializeSuggestionViews(size_t candidates_size);
void MakeVisible();
// views::BubbleDialogDelegateView:
const char* GetClassName() const override;
// The suggestion view is used for rendering suggestion.
SuggestionView* suggestion_view_;
// The view containing all the suggestions.
views::View* candidate_area_;
// The items in view_
std::vector<std::unique_ptr<SuggestionView>> candidate_views_;
int selected_index_ = -1;
DISALLOW_COPY_AND_ASSIGN(SuggestionWindowView);
};
......
......@@ -30,6 +30,13 @@ class COMPONENT_EXPORT(UI_BASE_IME) IMEAssistiveWindowHandlerInterface {
virtual void ShowSuggestion(const base::string16& text,
const size_t confirmed_length,
const bool show_tab) {}
virtual void ShowMultipleSuggestions(
const std::vector<base::string16>& suggestions) {}
// Highlight a candidate when multiple suggestions is shown.
virtual void HighlightSuggestionCandidate(int index) {}
virtual void HideSuggestion() {}
// Called to get the current suggestion text.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment