chromium/ash/capture_mode/recording_type_menu_view.cc

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

#include "ash/capture_mode/recording_type_menu_view.h"

#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/capture_mode_util.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/style/color_provider.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/system_shadow.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/background.h"
#include "ui/views/highlight_border.h"

namespace ash {

namespace {

// The padding around the menu options.
constexpr auto kMenuPadding = gfx::Insets::VH(12, 0);

// The vertical space between the two nearest edges of the capture label widget
// and the recording type menu widget.
constexpr int kYOffsetFromLabelWidget = 8;

constexpr int kMinimumWidth = 184;
constexpr gfx::Size kIdealSize{kMinimumWidth, 96};

constexpr int kCornerRadius = 12;
constexpr gfx::RoundedCornersF kRoundedCorners{kCornerRadius};

// Gets the ideal size of the widget hosting the `RecordingTypeMenuView` either
// from the preferred size of `contents_view` (if given), or the default size.
gfx::Size GetIdealSize(views::View* contents_view) {
  gfx::Size size =
      contents_view ? contents_view->GetPreferredSize() : kIdealSize;
  if (size.width() < kMinimumWidth)
    size.set_width(kMinimumWidth);
  return size;
}

}  // namespace

RecordingTypeMenuView::RecordingTypeMenuView(
    base::RepeatingClosure on_option_selected_callback)
    : CaptureModeMenuGroup(this, kMenuPadding),
      on_option_selected_callback_(std::move(on_option_selected_callback)),
      shadow_(SystemShadow::CreateShadowOnNinePatchLayerForView(
          this,
          SystemShadow::Type::kElevation12)) {
  SetPaintToLayer();
  SetBackground(views::CreateThemedSolidBackground(kColorAshShieldAndBase80));
  layer()->SetFillsBoundsOpaquely(false);
  layer()->SetRoundedCornerRadius(kRoundedCorners);
  layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
  layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);

  AddOption(
      &kCaptureModeVideoIcon,
      l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD),
      ToInt(RecordingType::kWebM));
  AddOption(&kCaptureGifIcon,
            l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_LABEL_GIF_RECORD),
            ToInt(RecordingType::kGif));

  capture_mode_util::SetHighlightBorder(
      this, kCornerRadius,
      views::HighlightBorder::Type::kHighlightBorderOnShadow);

  shadow_->SetRoundedCornerRadius(kCornerRadius);
}

RecordingTypeMenuView::~RecordingTypeMenuView() = default;

// static
gfx::Rect RecordingTypeMenuView::GetIdealScreenBounds(
    const gfx::Rect& capture_label_widget_screen_bounds,
    const gfx::Rect& target_display_screen_bounds,
    views::View* contents_view) {
  const auto size = GetIdealSize(contents_view);
  const auto bottom_center = capture_label_widget_screen_bounds.bottom_center();
  // Make sure the left and right edges are within the screen bounds.
  int x = std::max(0, bottom_center.x() - (size.width() / 2));
  if (x + size.width() > target_display_screen_bounds.right()) {
    x = target_display_screen_bounds.right() - size.width();
  }

  // Try positioning the menu below the bar first, if this makes it outside the
  // bounds of the display, then try positioning it above.
  gfx::Rect result{gfx::Point(x, bottom_center.y() + kYOffsetFromLabelWidget),
                   size};
  if (result.bottom() > target_display_screen_bounds.bottom()) {
    result.set_y(capture_label_widget_screen_bounds.y() -
                 kYOffsetFromLabelWidget - size.height());
  }

  CHECK(target_display_screen_bounds.Contains(result))
      << "display bounds (screen) = " << target_display_screen_bounds.ToString()
      << ", recording type menu bounds (screen) = " << result.ToString()
      << ", user region bounds (root) = "
      << CaptureModeController::Get()->user_capture_region().ToString();
  return result;
}

void RecordingTypeMenuView::OnOptionSelected(int option_id) const {
  CaptureModeController::Get()->SetRecordingType(
      static_cast<RecordingType>(option_id));
  on_option_selected_callback_.Run();
}

bool RecordingTypeMenuView::IsOptionChecked(int option_id) const {
  return option_id == ToInt(CaptureModeController::Get()->recording_type());
}

bool RecordingTypeMenuView::IsOptionEnabled(int option_id) const {
  return true;
}

views::View* RecordingTypeMenuView::GetWebMOptionForTesting() {
  return GetOptionForTesting(ToInt(RecordingType::kWebM));  // IN-TEST
}

views::View* RecordingTypeMenuView::GetGifOptionForTesting() {
  return GetOptionForTesting(ToInt(RecordingType::kGif));  // IN-TEST
}

}  // namespace ash