chromium/chrome/browser/ui/quick_answers/ui/quick_answers_view.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_QUICK_ANSWERS_UI_QUICK_ANSWERS_VIEW_H_
#define CHROME_BROWSER_UI_QUICK_ANSWERS_UI_QUICK_ANSWERS_VIEW_H_

#include <optional>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/chromeos/read_write_cards/read_write_cards_view.h"
#include "chrome/browser/ui/quick_answers/ui/loading_view.h"
#include "chrome/browser/ui/quick_answers/ui/quick_answers_stage_button.h"
#include "chrome/browser/ui/quick_answers/ui/result_view.h"
#include "chrome/browser/ui/quick_answers/ui/retry_view.h"
#include "chromeos/components/quick_answers/public/cpp/constants.h"
#include "chromeos/components/quick_answers/quick_answers_model.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/view.h"
#include "ui/views/view_tracker.h"
#include "ui/views/widget/unique_widget_ptr.h"

namespace views {
class ImageButton;
class ImageView;
class WebView;
}  // namespace views

namespace chromeos::editor_menu {
class FocusSearch;
}  // namespace chromeos::editor_menu

class QuickAnswersUiController;

namespace quick_answers {
struct QuickAnswer;
struct PhoneticsInfo;

// A bubble style view to show QuickAnswer.
class QuickAnswersView : public chromeos::ReadWriteCardsView {
  METADATA_HEADER(QuickAnswersView, chromeos::ReadWriteCardsView)

 public:
  struct Params {
   public:
    std::string title;
    Design design = Design::kCurrent;
    // Set true to show a Google internal variant of Qucik Answers UI.
    bool is_internal = false;
  };

  using MockGenerateTtsCallback =
      base::RepeatingCallback<void(const PhoneticsInfo&)>;

  QuickAnswersView(const Params& params,
                   base::WeakPtr<QuickAnswersUiController> controller);

  QuickAnswersView(const QuickAnswersView&) = delete;
  QuickAnswersView& operator=(const QuickAnswersView&) = delete;

  ~QuickAnswersView() override;

  // chromeos::ReadWriteCardsView:
  void RequestFocus() override;
  bool HasFocus() const override;
  void OnFocus() override;
  views::FocusTraversable* GetPaneFocusTraversable() override;
  gfx::Size GetMaximumSize() const override;
  void UpdateBoundsForQuickAnswers() override;

  // Called when a click happens to trigger Assistant Query.
  void SendQuickAnswersQuery();

  // `std::nullopt` means an unknown intent, which is used only from
  // Linux-ChromeOS. On Linux-ChromeOS, intent generation code is not exercised.
  // Intent is initially set to `kUnknown` on Linux-ChromeOS and set it to an
  // actual intent later by the backend, i.e., intent transition `std::nullopt`
  // -> an intent value. There can be a case where pre-process find an intent
  // but the backend doesn't find the intent. For that case, the spec is that we
  // keep the original intent icon/ui, i.e., no transition of an intent value ->
  // `std::nullopt`.
  void SetIntent(Intent intent);
  std::optional<Intent> GetIntent() const;

  void SetResult(const StructuredResult& structured_result);

  void ShowRetryView();

  LoadingView* GetLoadingViewForTesting() { return loading_view_; }
  RetryView* GetRetryViewForTesting() { return retry_view_; }
  ResultView* GetResultViewForTesting() { return result_view_; }
  void SetMockGenerateTtsCallbackForTesting(
      MockGenerateTtsCallback mock_generate_tts_callback);
  views::ImageButton* GetSettingsButtonForTesting() { return settings_button_; }
  views::ImageButton* GetDogfoodButtonForTesting() { return dogfood_button_; }

 private:
  bool HasFocusInside();
  void AddFrameButtons();
  bool ShouldAddPhoneticsAudioButton(ResultType result_type,
                                     GURL phonetics_audio,
                                     bool tts_audio_enabled);
  void AddPhoneticsAudioButton(
      const quick_answers::PhoneticsInfo& phonetics_info,
      View* container);
  int GetLabelWidth(bool is_title);
  void ResetContentView();
  void UpdateQuickAnswerResult(const quick_answers::QuickAnswer& quick_answer);
  void GenerateTts(const PhoneticsInfo& phonetics_info);
  void SwitchTo(views::View* view);

  // FocusSearch::GetFocusableViewsCallback to poll currently focusable views.
  std::vector<views::View*> GetFocusableViews();

  // Invoked when user clicks the phonetics audio button.
  void OnPhoneticsAudioButtonPressed(
      const quick_answers::PhoneticsInfo& phonetics_info);

  void UpdateUiText();
  void UpdateAccessibleName();
  void UpdateIcon();

  base::WeakPtr<QuickAnswersUiController> controller_;
  std::string title_;
  const Design design_;
  std::optional<Intent> intent_ = std::nullopt;
  const bool is_internal_;

  raw_ptr<QuickAnswersStageButton> quick_answers_stage_button_ = nullptr;
  raw_ptr<views::Label> refreshed_ui_header_ = nullptr;

  raw_ptr<LoadingView> loading_view_ = nullptr;
  raw_ptr<RetryView> retry_view_ = nullptr;
  raw_ptr<ResultView> result_view_ = nullptr;
  raw_ptr<views::ImageButton> settings_button_ = nullptr;
  raw_ptr<views::ImageButton> dogfood_button_ = nullptr;

  raw_ptr<views::ImageView> icon_ = nullptr;

  MockGenerateTtsCallback mock_generate_tts_callback_;

  // Invisible WebView to play phonetics audio for definition results. WebView
  // is lazy created to improve performance.
  views::ViewTracker phonetics_audio_web_view_;

  std::unique_ptr<chromeos::editor_menu::FocusSearch> focus_search_;
  base::WeakPtrFactory<QuickAnswersView> weak_factory_{this};
};

BEGIN_VIEW_BUILDER(/* no export */,
                   QuickAnswersView,
                   chromeos::ReadWriteCardsView)
VIEW_BUILDER_PROPERTY(Intent, Intent)
END_VIEW_BUILDER

}  // namespace quick_answers

DEFINE_VIEW_BUILDER(/* no export */, quick_answers::QuickAnswersView)

#endif  // CHROME_BROWSER_UI_QUICK_ANSWERS_UI_QUICK_ANSWERS_VIEW_H_