chromium/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc

// 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/ui/quick_answers/ui/rich_answers_translation_view.h"

#include "base/functional/bind.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/quick_answers/quick_answers_ui_controller.h"
#include "chrome/browser/ui/quick_answers/ui/quick_answers_text_label.h"
#include "chrome/browser/ui/quick_answers/ui/quick_answers_util.h"
#include "chromeos/components/quick_answers/quick_answers_model.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/color/color_id.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/flex_layout_view.h"

namespace {

// The combined width of the read and copy buttons and all their
// associated margins.
constexpr int kReadAndCopyButtonsWidth = 64;

}  // namespace

namespace quick_answers {

// RichAnswersTranslationView
// -----------------------------------------------------------

RichAnswersTranslationView::RichAnswersTranslationView(
    const gfx::Rect& anchor_view_bounds,
    base::WeakPtr<QuickAnswersUiController> controller,
    const TranslationResult& translation_result)
    : RichAnswersView(anchor_view_bounds,
                      controller,
                      ResultType::kTranslationResult),
      translation_result_(translation_result) {
  InitLayout();

  // TODO (b/274184294): Add custom focus behavior according to
  // approved greenlines.
}

RichAnswersTranslationView::~RichAnswersTranslationView() = default;

void RichAnswersTranslationView::InitLayout() {
  content_view_ = GetContentView();

  // Source language.
  AddLanguageTitle(translation_result_.source_locale, /*is_header_view=*/true);

  // Text to translate.
  AddLanguageText(translation_result_.text_to_translate,
                  /*maybe_append_buttons=*/false);

  // Separator.
  content_view_->AddChildView(CreateSeparatorView());

  // Target language.
  AddLanguageTitle(translation_result_.target_locale, /*is_header_view=*/false);

  // Translated text.
  views::FlexLayoutView* translated_text_view = AddLanguageText(
      translation_result_.translated_text, /*maybe_append_buttons=*/true);

  // Read and copy buttons.
  //
  // If `translated_text_view` is a valid container view, then append the button
  // views to it. Else, create a separate horizontal child view in
  // `content_view_` to contain the buttons.
  views::View* buttons_container_view;
  if (translated_text_view) {
    buttons_container_view = translated_text_view;
  } else {
    buttons_container_view =
        content_view_->AddChildView(CreateHorizontalBoxLayoutView());
  }
  AddReadAndCopyButtons(buttons_container_view);
}

void RichAnswersTranslationView::AddLanguageTitle(const std::string& locale,
                                                  bool is_header_view) {
  auto locale_name = l10n_util::GetDisplayNameForLocale(
      locale, translation_result_.target_locale, true);

  if (is_header_view) {
    AddHeaderViewsTo(content_view_, base::UTF16ToUTF8(locale_name));
    return;
  }

  content_view_->AddChildView(QuickAnswersTextLabel::CreateLabelWithStyle(
      base::UTF16ToUTF8(locale_name),
      GetFontList(TypographyToken::kCrosButton2), kContentHeaderWidth,
      /*is_multi_line=*/false, ui::kColorSysSecondary));
}

views::FlexLayoutView* RichAnswersTranslationView::AddLanguageText(
    const std::string& language_text,
    bool maybe_append_buttons) {
  std::unique_ptr<QuickAnswersTextLabel> language_text_label =
      QuickAnswersTextLabel::CreateLabelWithStyle(
          language_text, GetFontList(TypographyToken::kCrosTitle1),
          kContentTextWidth,
          /*is_multi_line=*/true, ui::kColorSysOnSurface);

  // If appending the read and copy buttons is an option, then check if
  // the buttons can fit on a single line with the language text label.
  //
  // If there's enough space to append the buttons, return the container view
  // that the button views should be added to.
  if (maybe_append_buttons && (language_text_label->GetPreferredSize().width() +
                               kReadAndCopyButtonsWidth) <= kContentTextWidth) {
    views::View* box_layout_view =
        content_view_->AddChildView(CreateHorizontalBoxLayoutView());
    box_layout_view->AddChildView(std::move(language_text_label));
    views::FlexLayoutView* buttons_view =
        box_layout_view->AddChildView(CreateHorizontalFlexLayoutView());

    return buttons_view;
  }

  content_view_->AddChildView(std::move(language_text_label));
  return nullptr;
}

void RichAnswersTranslationView::AddReadAndCopyButtons(
    views::View* container_view) {
  CHECK(container_view);

  // Setup an invisible web view to play TTS audio.
  tts_audio_web_view_ = container_view->AddChildView(
      std::make_unique<views::WebView>(ProfileManager::GetActiveUserProfile()));
  tts_audio_web_view_->SetVisible(false);

  // Read button.
  base::RepeatingClosure read_closure = base::BindRepeating(
      &RichAnswersTranslationView::OnReadButtonPressed,
      weak_factory_.GetWeakPtr(), translation_result_.translated_text,
      translation_result_.target_locale);
  ui::ImageModel read_image_model = ui::ImageModel::FromVectorIcon(
      vector_icons::kVolumeUpIcon, ui::kColorSysOnSurface,
      /*icon_size=*/kRichAnswersIconSizeDip);
  container_view->AddChildView(CreateImageButtonView(
      read_closure, read_image_model, ui::kColorSysStateHoverOnSubtle,
      l10n_util::GetStringUTF16(
          IDS_RICH_ANSWERS_VIEW_TRANSLATION_READ_BUTTON_A11Y_NAME_TEXT)));

  // Copy button.
  base::RepeatingClosure copy_closure = base::BindRepeating(
      &RichAnswersTranslationView::OnCopyButtonPressed,
      weak_factory_.GetWeakPtr(), translation_result_.translated_text);
  ui::ImageModel copy_image_model = ui::ImageModel::FromVectorIcon(
      vector_icons::kContentCopyIcon, ui::kColorSysOnSurface,
      /*icon_size=*/kRichAnswersIconSizeDip);
  container_view->AddChildView(CreateImageButtonView(
      copy_closure, copy_image_model, ui::kColorSysStateHoverOnSubtle,
      l10n_util::GetStringUTF16(
          IDS_RICH_ANSWERS_VIEW_TRANSLATION_COPY_BUTTON_A11Y_NAME_TEXT)));
}

void RichAnswersTranslationView::OnReadButtonPressed(
    const std::string& read_text,
    const std::string& locale) {
  GenerateTTSAudio(tts_audio_web_view_->GetBrowserContext(), read_text, locale);
}

void RichAnswersTranslationView::OnCopyButtonPressed(
    const std::string& copy_text) {
  ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste);
  writer.WriteText(base::UTF8ToUTF16(copy_text));
}

BEGIN_METADATA(RichAnswersTranslationView)
END_METADATA

}  // namespace quick_answers