chromium/ash/capture_mode/capture_mode_settings_view.h

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

#ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_SETTINGS_VIEW_H_
#define ASH_CAPTURE_MODE_CAPTURE_MODE_SETTINGS_VIEW_H_

#include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_camera_controller.h"
#include "ash/capture_mode/capture_mode_menu_group.h"
#include "ash/capture_mode/capture_mode_session_focus_cycler.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/controls/scroll_view.h"

namespace views {
class Separator;
}  // namespace views

namespace ash {

class CaptureModeBehavior;
class CaptureModeMenuGroup;
class CaptureModeSession;
class CaptureModeMenuToggleButton;
class SystemShadow;

// All the options in the CaptureMode settings view.
enum CaptureSettingsOption {
  kAudioOff = 0,
  kAudioSystem,
  kAudioMicrophone,
  kAudioSystemAndMicrophone,
  kDownloadsFolder,
  kCustomFolder,
  kCameraOff,
  kCameraDevicesBegin,
};

// A view that acts as the content view of the capture mode settings menu
// widget. It allows the settings options to scroll when the menu height is
// constrained by the top of the screen. It contains `scroll_view_contents_`
// that parents a `CaptureModeMenuGroup` for each setting, save to, audio input,
// etc.
class ASH_EXPORT CaptureModeSettingsView
    : public views::ScrollView,
      public CaptureModeMenuGroup::Delegate,
      public CaptureModeCameraController::Observer {
  METADATA_HEADER(CaptureModeSettingsView, views::ScrollView)

 public:
  CaptureModeSettingsView(CaptureModeSession* session,
                          CaptureModeBehavior* active_behavior);
  CaptureModeSettingsView(const CaptureModeSettingsView&) = delete;
  CaptureModeSettingsView& operator=(const CaptureModeSettingsView&) = delete;
  ~CaptureModeSettingsView() override;

  // Gets the ideal bounds in screen coordinates of the settings widget on
  // the given |capture_mode_bar_view|. If |content_view| is not null, it will
  // be used to get the preferred size to calculate the final bounds. Otherwise,
  // a default size will be used.
  static gfx::Rect GetBounds(CaptureModeBarView* capture_mode_bar_view,
                             CaptureModeSettingsView* content_view = nullptr);

  // Called when the folder, in which the captured files will be saved, may have
  // changed. This may result in adding or removing a menu option for the folder
  // that was added or removed. This means that the preferred size of this view
  // can possibly change, and therefore it's the responsibility of the caller to
  // to set the proper bounds on the widget.
  void OnCaptureFolderMayHaveChanged();

  // Called when we change the setting to force-use the default downloads folder
  // as the save folder. This results in updating which folder menu option is
  // currently selected.
  void OnDefaultCaptureFolderSelectionChanged();

  // Gets the highlightable `CaptureModeOption` and `CaptureModeMenuItem` inside
  // this view.
  std::vector<CaptureModeSessionFocusCycler::HighlightableView*>
  GetHighlightableItems();

  // CaptureModeMenuGroup::Delegate:
  void OnOptionSelected(int option_id) const override;
  bool IsOptionChecked(int option_id) const override;
  bool IsOptionEnabled(int option_id) const override;

  // CaptureModeCameraController::Observer:
  void OnAvailableCamerasChanged(const CameraInfoList& cameras) override;
  void OnSelectedCameraChanged(const CameraId& camera_id) override;

  CaptureModeMenuToggleButton* demo_tools_menu_toggle_button_for_testing() {
    return demo_tools_menu_toggle_button_;
  }

 private:
  friend class CaptureModeSettingsTestApi;

  // Called when the "Select folder" menu item in the |save_to_menu_group_| is
  // pressed. It opens the folder selection dialog so that user can pick a
  // location in which captured files will be saved.
  void OnSelectFolderMenuItemPressed();

  // Called back when the check for custom folder's availability is done, with
  // `available` indicating whether the custom folder is available or not. We
  // will check the custom folder's availability every time when
  // `OnCaptureFolderMayHaveChanged` is triggered and custom folder is not
  // empty.
  void OnCustomFolderAvailabilityChecked(bool available);

  // Finds the camera id by the given `option_id` from the
  // `option_camera_id_map_` if any. Otherwise, return nullptr.
  const CameraId* FindCameraIdByOptionId(int option_id) const;

  // Adds all camera options (including the option for `kCameraOff`) for the
  // given `cameras` to the `camera_menu_group_`. It deletes all options in
  // `camera_menu_group_` before adding options. Called when initializing `this`
  // or `OnAvailableCamerasChanged` is triggered. It will also trigger
  // `UpdateCameraMenuGroupVisibility` at the end.
  // Note that camera options are only added when `cameras` is not empty.
  // When cameras are disabled by policy (i.e. `managed_by_policy` is true),
  // only the "Off" option is added. Users are not allowed to choose any cameras
  // in that case.
  void AddCameraOptions(const CameraInfoList& cameras, bool managed_by_policy);

  void UpdateCameraMenuGroupVisibility(bool visible);

  void OnDemoToolsButtonToggled();

  // A reference to the session that owns this view indirectly by owning its
  // containing widget.
  const raw_ptr<CaptureModeSession, DanglingUntriaged>
      capture_mode_session_;  // Not null;

  const raw_ptr<CaptureModeBehavior> active_behavior_;

  // "Audio input" menu group that users can select an audio input from for
  // screen capture recording. It has "Off" and "Microphone" options for now.
  // "Off" is the default one which means no audio input selected.
  raw_ptr<CaptureModeMenuGroup> audio_input_menu_group_ = nullptr;

  // The separator between audio input and camera menus.
  raw_ptr<views::Separator> separator_1_ = nullptr;

  // Camera menu group that users can select a camera device from for selfie
  // cam while video recording. It has an `Off` option and options for all
  // available camera devices. `Off` is the default one which means no camera is
  // selected.
  raw_ptr<CaptureModeMenuGroup> camera_menu_group_ = nullptr;

  // A mapping from option id to camera id for camera devices.
  base::flat_map<int, CameraId> option_camera_id_map_;

  // `demo_tools_menu_toggle_button_` will be used as the entry point to enable
  // the capture mode demo tools feature. Currently
  // `demo_tools_menu_toggle_button_` and `separator_2_` are guarded by the
  // feature flag and will only be visible when the feature is enabled.
  raw_ptr<views::Separator> separator_2_ = nullptr;
  raw_ptr<CaptureModeMenuToggleButton> demo_tools_menu_toggle_button_ = nullptr;

  // Can be null if `ShouldSaveToSettingsBeIncluded()` is false for the active
  // behavior of current capture mode session, since then it's not needed as the
  // "Save-to" menu group will not be added at all.
  raw_ptr<views::Separator> separator_3_ = nullptr;

  // "Save to" menu group that users can select a folder to save the captured
  // files to. It will include the "Downloads" folder as the default one and
  // one more folder selected by users.
  // This menu group is not added when in `ShouldSaveToSettingsBeIncluded()` is
  // false for the active behavior of current capture mode session.
  raw_ptr<CaptureModeMenuGroup> save_to_menu_group_ = nullptr;

  // If not set, custom folder is not set. If true, customer folder is set and
  // available. If false, customer folder is set but unavailable.
  std::optional<bool> is_custom_folder_available_;

  // If set, it will be called when the settings menu is refreshed.
  base::OnceClosure on_settings_menu_refreshed_callback_for_test_;

  std::unique_ptr<SystemShadow> shadow_;

  base::WeakPtrFactory<CaptureModeSettingsView> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_CAPTURE_MODE_CAPTURE_MODE_SETTINGS_VIEW_H_