chromium/chromeos/ui/frame/multitask_menu/multitask_button.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 "chromeos/ui/frame/multitask_menu/multitask_button.h"

#include "chromeos/ui/frame/multitask_menu/multitask_menu_constants.h"
#include "chromeos/utils/haptics_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/events/devices/haptic_touchpad_effects.h"
#include "ui/gfx/canvas.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/controls/highlight_path_generator.h"

namespace chromeos {

MultitaskButton::MultitaskButton(PressedCallback callback,
                                 Type type,
                                 bool is_portrait_mode,
                                 bool paint_as_active,
                                 const std::u16string& name)
    : views::Button(std::move(callback)),
      type_(type),
      is_portrait_mode_(is_portrait_mode),
      paint_as_active_(paint_as_active) {
  views::InstallRoundRectHighlightPathGenerator(
      this, gfx::Insets(), kMultitaskBaseButtonBorderRadius);
  GetViewAccessibility().SetName(name);
}

void MultitaskButton::StateChanged(views::Button::ButtonState old_state) {
  if (GetState() == views::Button::STATE_HOVERED) {
    haptics_util::PlayHapticTouchpadEffect(
        ui::HapticTouchpadEffect::kSnap,
        ui::HapticTouchpadEffectStrength::kMedium);
  }
}

void MultitaskButton::PaintButtonContents(gfx::Canvas* canvas) {
  cc::PaintFlags fill_flags;
  fill_flags.setAntiAlias(true);
  fill_flags.setStyle(cc::PaintFlags::kFill_Style);

  cc::PaintFlags border_flags;
  border_flags.setAntiAlias(true);
  border_flags.setStyle(cc::PaintFlags::kStroke_Style);

  cc::PaintFlags pattern_flags;
  pattern_flags.setAntiAlias(true);
  pattern_flags.setStyle(cc::PaintFlags::kFill_Style);

  const auto* color_provider = GetColorProvider();
  if (paint_as_active_ || GetState() == Button::STATE_HOVERED ||
      GetState() == Button::STATE_PRESSED) {
    const SkColor primary_color =
        color_provider->GetColor(ui::kColorSysPrimary);
    fill_flags.setColor(
        SkColorSetA(primary_color, kMultitaskHoverBackgroundOpacity));
    border_flags.setColor(primary_color);
    pattern_flags.setColor(primary_color);
  } else if (GetState() == Button::STATE_DISABLED) {
    fill_flags.setColor(SK_ColorTRANSPARENT);
    const SkColor disabled_color =
        SkColorSetA(color_provider->GetColor(ui::kColorSysOnSurface),
                    kMultitaskDisabledButtonOpacity);
    border_flags.setColor(disabled_color);
    pattern_flags.setColor(disabled_color);
  } else {
    fill_flags.setColor(SK_ColorTRANSPARENT);
    const auto default_color =
        SkColorSetA(color_provider->GetColor(ui::kColorSysOnSurface),
                    kMultitaskDefaultButtonOpacity);
    border_flags.setColor(default_color);
    pattern_flags.setColor(default_color);
  }

  const gfx::RectF local_bounds_f(GetLocalBounds());
  canvas->DrawRoundRect(local_bounds_f, kMultitaskBaseButtonBorderRadius,
                        fill_flags);

  // Draw a border on the background circle. Inset by half the stroke width,
  // otherwise half of the stroke will be out of bounds.
  gfx::RectF border_bounds = local_bounds_f;
  border_bounds.Inset(kButtonBorderSize / 2.f);
  border_flags.setStrokeWidth(kButtonBorderSize);
  canvas->DrawRoundRect(border_bounds, kMultitaskBaseButtonBorderRadius,
                        border_flags);

  gfx::RectF pattern_bounds;
  switch (type_) {
    case Type::kFloat: {
      // Float pattern is located at the bottom left or bottom right with a
      // little padding. Default is bottom right, mirrored is bottom left.
      gfx::Rect float_pattern_bounds(GetLocalBounds().bottom_right(),
                                     kFloatPatternSize);
      float_pattern_bounds.Offset(-kFloatPatternSize.width() - kButtonPadding,
                                  -kFloatPatternSize.height() - kButtonPadding);
      float_pattern_bounds = GetMirroredRect(float_pattern_bounds);
      pattern_bounds = gfx::RectF(float_pattern_bounds);
      break;
    }
    case Type::kFull: {
      pattern_bounds = local_bounds_f;
      pattern_bounds.Inset(gfx::InsetsF(kButtonPadding));
      break;
    }
  }

  canvas->DrawRoundRect(pattern_bounds, kButtonCornerRadius, pattern_flags);
}

BEGIN_METADATA(MultitaskButton)
END_METADATA

}  // namespace chromeos