blob: 278f0f3c2151b00d0c6dd364d4740808c2346925 [file] [log] [blame]
// Copyright 2023 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/ash/arc/input_overlay/ui/edit_labels.h"
#include "base/check_op.h"
#include "base/strings/string_util.h"
#include "chrome/browser/ash/arc/input_overlay/actions/action.h"
#include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h"
#include "chrome/browser/ash/arc/input_overlay/ui/button_options_menu.h"
#include "chrome/browser/ash/arc/input_overlay/ui/edit_label.h"
#include "chrome/browser/ash/arc/input_overlay/ui/editing_list.h"
#include "chrome/browser/ash/arc/input_overlay/ui/name_tag.h"
#include "chrome/browser/ash/arc/input_overlay/ui/ui_utils.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/views/layout/table_layout.h"
#include "ui/views/view_utils.h"
namespace arc::input_overlay {
// static
std::unique_ptr<EditLabels> EditLabels::CreateEditLabels(
DisplayOverlayController* controller,
Action* action,
NameTag* name_tag,
bool for_editing_list) {
auto labels = std::make_unique<EditLabels>(controller, action, name_tag,
for_editing_list);
labels->Init();
return labels;
}
EditLabels::EditLabels(DisplayOverlayController* controller,
Action* action,
NameTag* name_tag,
bool for_editing_list)
: controller_(controller),
action_(action),
name_tag_(name_tag),
for_editing_list_(for_editing_list) {}
EditLabels::~EditLabels() = default;
void EditLabels::Init() {
switch (action_->GetType()) {
case ActionType::TAP:
InitForActionTapKeyboard();
break;
case ActionType::MOVE:
InitForActionMoveKeyboard();
break;
default:
NOTREACHED();
}
UpdateNameTag();
}
void EditLabels::OnActionInputBindingUpdated() {
for (arc::input_overlay::EditLabel* label : labels_) {
label->OnActionInputBindingUpdated();
}
UpdateNameTag();
}
void EditLabels::SetNameTagState(bool is_error,
const std::u16string& error_tooltip) {
// If an individual label doesn't need to show error, but other sibling label
// still has label unassigned, `name_tag_` still needs to show error.
if (!is_error && missing_assign_) {
name_tag_->SetState(
/*is_error=*/!action_->is_new(),
l10n_util::GetStringUTF16(IDS_INPUT_OVERLAY_EDIT_MISSING_BINDING));
} else {
name_tag_->SetState(is_error, error_tooltip);
}
}
void EditLabels::FocusLabel() {
// Clicking the edit labels with an already focused edit label causes the next
// label to gain focus.
for (size_t i = 0; i < labels_.size(); i++) {
if (auto* label = labels_[i].get(); label->HasFocus()) {
labels_[(i + 1) % labels_.size()]->RequestFocus();
return;
}
}
labels_[0]->RequestFocus();
}
std::u16string EditLabels::CalculateActionName() {
std::u16string key_string = u"";
// Check if all labels are unassigned. The prefix for the sub-title is
// different if all labels are unassigned.
bool all_unassigned = true;
// If at least one label is unassigned, it needs to show error state.
missing_assign_ = false;
DCHECK_GE(labels_.size(), 1u);
for (arc::input_overlay::EditLabel* label : labels_) {
if (label->IsInputUnbound()) {
missing_assign_ = true;
} else {
key_string.append(label->GetText());
all_unassigned = false;
}
}
int control_type_id = 0;
switch (action_->GetType()) {
case ActionType::TAP:
control_type_id = IDS_INPUT_OVERLAY_BUTTON_TYPE_SINGLE_BUTTON_LABEL;
break;
case ActionType::MOVE:
control_type_id = IDS_INPUT_OVERLAY_BUTTON_TYPE_JOYSTICK_BUTTON_LABEL;
break;
default:
NOTREACHED();
}
if (all_unassigned) {
return l10n_util::GetStringFUTF16(
IDS_INPUT_OVERLAY_CONTROL_NAME_LABEL_UNASSIGNED_TEMPLATE,
l10n_util::GetStringUTF16(control_type_id));
}
return l10n_util::GetStringFUTF16(
IDS_INPUT_OVERLAY_CONTROL_NAME_LABEL_TEMPLATE,
l10n_util::GetStringUTF16(control_type_id), key_string);
}
std::u16string EditLabels::CalculateKeyListForA11yLabel() const {
std::vector<std::u16string> keys;
for (EditLabel* label : labels_) {
keys.push_back(
GetDisplayTextAccessibleName(std::u16string(label->GetText())));
}
return base::JoinString(keys, u", ");
}
bool EditLabels::IsFirstLabelUnassigned() const {
DCHECK_GE(labels_.size(), 1u);
return labels_[0]->IsInputUnbound();
}
void EditLabels::PerformPulseAnimationOnFirstLabel() {
DCHECK_GE(labels_.size(), 1u);
labels_[0]->PerformPulseAnimation(/*pulse_count=*/0);
}
void EditLabels::InitForActionTapKeyboard() {
SetUseDefaultFillLayout(true);
labels_.emplace_back(AddChildView(
std::make_unique<EditLabel>(controller_, action_, for_editing_list_)));
}
void EditLabels::InitForActionMoveKeyboard() {
SetLayoutManager(std::make_unique<views::TableLayout>())
->AddColumn(/*h_align=*/views::LayoutAlignment::kCenter,
/*v_align=*/views::LayoutAlignment::kCenter,
/*horizontal_resize=*/1.0f,
/*size_type=*/views::TableLayout::ColumnSize::kUsePreferred,
/*fixed_width=*/0, /*min_width=*/0)
.AddPaddingColumn(/*horizontal_resize=*/views::TableLayout::kFixedSize,
/*width=*/4)
.AddColumn(/*h_align=*/views::LayoutAlignment::kCenter,
/*v_align=*/views::LayoutAlignment::kCenter,
/*horizontal_resize=*/1.0f,
/*size_type=*/views::TableLayout::ColumnSize::kUsePreferred,
/*fixed_width=*/0, /*min_width=*/0)
.AddPaddingColumn(/*horizontal_resize=*/views::TableLayout::kFixedSize,
/*width=*/4)
.AddColumn(/*h_align=*/views::LayoutAlignment::kCenter,
/*v_align=*/views::LayoutAlignment::kCenter,
/*horizontal_resize=*/1.0f,
/*size_type=*/views::TableLayout::ColumnSize::kUsePreferred,
/*fixed_width=*/0, /*min_width=*/0)
.AddRows(1, /*vertical_resize=*/views::TableLayout::kFixedSize)
.AddPaddingRow(/*vertical_resize=*/views::TableLayout::kFixedSize,
/*height=*/4)
.AddRows(1, /*vertical_resize=*/views::TableLayout::kFixedSize);
for (int i = 0; i < 6; i++) {
// Column 1 row 1 and Column 3 row 1 are empty.
if (i == 0 || i == 2) {
AddChildView(std::make_unique<views::View>());
} else {
DCHECK_LT(labels_.size(), size_t(Direction::kMaxValue) + 1);
labels_.emplace_back(AddChildView(std::make_unique<EditLabel>(
controller_, action_, for_editing_list_, labels_.size())));
}
}
}
void EditLabels::UpdateNameTag() {
// If at least one label is unassigned, it needs to show error state.
missing_assign_ = false;
DCHECK_GE(labels_.size(), 1u);
for (arc::input_overlay::EditLabel* label : labels_) {
if (label->IsInputUnbound()) {
missing_assign_ = true;
break;
}
}
name_tag_->SetState(
// The name tag is not set to be in an error state if it was newly
// created.
/*is_error=*/missing_assign_ && !action_->is_new(),
missing_assign_
? l10n_util::GetStringUTF16(IDS_INPUT_OVERLAY_EDIT_MISSING_BINDING)
: u"");
if (for_editing_list_) {
name_tag_->SetTitle(CalculateActionName());
}
}
void EditLabels::RemoveNewState() {
for (arc::input_overlay::EditLabel* label : labels_) {
label->RemoveNewState();
}
UpdateNameTag();
}
BEGIN_METADATA(EditLabels)
END_METADATA
} // namespace arc::input_overlay