chromium/ash/hud_display/graph_page_view_base.cc

// 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.

#include "ash/hud_display/graph_page_view_base.h"

#include <utility>

#include "ash/hud_display/hud_constants.h"
#include "ash/hud_display/hud_properties.h"
#include "ash/hud_display/legend.h"
#include "ash/hud_display/reference_lines.h"
#include "ash/hud_display/solid_source_background.h"
#include "base/functional/bind.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/window/vector_icons/vector_icons.h"

namespace ash {
namespace hud_display {

namespace {

constexpr int kMinMaxButtonIconSize = 10;
constexpr int kMinMaxButtonBorder = 5;

// ImageButton with underline
class MinMaxButton : public views::ImageButton {
  METADATA_HEADER(MinMaxButton, views::ImageButton)

 public:
  explicit MinMaxButton(views::Button::PressedCallback callback)
      : views::ImageButton(std::move(callback)) {
    SetBorder(views::CreateEmptyBorder(kMinMaxButtonBorder));
    SetBackground(std::make_unique<SolidSourceBackground>(kHUDLegendBackground,
                                                          /*radius=*/0));
    SetProperty(kHUDClickHandler, HTCLIENT);

    SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
  }

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

  ~MinMaxButton() override = default;

 protected:
  // ImageButton
  void PaintButtonContents(gfx::Canvas* canvas) override {
    views::ImageButton::PaintButtonContents(canvas);

    SkPath path;
    path.moveTo(0, height());
    path.lineTo(height(), width());

    cc::PaintFlags flags;
    flags.setAntiAlias(true);
    flags.setBlendMode(SkBlendMode::kSrc);
    flags.setStyle(cc::PaintFlags::kStroke_Style);
    flags.setStrokeWidth(1);
    flags.setColor(kHUDDefaultColor);
    canvas->DrawPath(path, flags);
  }
};

BEGIN_METADATA(MinMaxButton)
END_METADATA

void SetMinimizeIconToButton(views::ImageButton* button) {
  button->SetImageModel(
      views::Button::ButtonState::STATE_NORMAL,
      ui::ImageModel::FromVectorIcon(views::kWindowControlMinimizeIcon,
                                     kHUDDefaultColor, kMinMaxButtonIconSize));
}

void SetRestoreIconToButton(views::ImageButton* button) {
  button->SetImageModel(
      views::Button::ButtonState::STATE_NORMAL,
      ui::ImageModel::FromVectorIcon(views::kWindowControlRestoreIcon,
                                     kHUDDefaultColor, kMinMaxButtonIconSize));
}

}  // namespace

BEGIN_METADATA(GraphPageViewBase)
END_METADATA

GraphPageViewBase::GraphPageViewBase() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);

  // There are two overlaid children: reference lines and legend container.
  SetLayoutManager(std::make_unique<views::FillLayout>());

  // |ReferenceLines| object is added after this object is fully initialized,
  // but it should be located under control elements (or they will never receive
  // events). This way we need to create a separate container for it.
  reference_lines_container_ = AddChildView(std::make_unique<views::View>());
  reference_lines_container_->SetLayoutManager(
      std::make_unique<views::FillLayout>());

  // Legend is floating in its own container. Invisible border of
  // kLegendPositionOffset makes it float on top of the graph.
  constexpr int kLegendPositionOffset = 20;
  legend_container_ = AddChildView(std::make_unique<views::View>());
  legend_container_
      ->SetLayoutManager(std::make_unique<views::BoxLayout>(
          views::BoxLayout::Orientation::kVertical))
      ->set_cross_axis_alignment(views::BoxLayout::CrossAxisAlignment::kStart);
  legend_container_->SetBorder(views::CreateEmptyBorder(kLegendPositionOffset));
  legend_container_->SetVisible(false);

  legend_min_max_button_ = legend_container_->AddChildView(
      std::make_unique<MinMaxButton>(base::BindRepeating(
          &GraphPageViewBase::OnButtonPressed, base::Unretained(this))));

  legend_min_max_button_->SetTooltipText(u"Trigger graph legend");
  SetMinimizeIconToButton(legend_min_max_button_);
}

GraphPageViewBase::~GraphPageViewBase() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
}

void GraphPageViewBase::OnButtonPressed() {
  if (legend_->GetVisible()) {
    legend_->SetVisible(false);
    SetRestoreIconToButton(legend_min_max_button_);
  } else {
    legend_->SetVisible(true);
    SetMinimizeIconToButton(legend_min_max_button_);
  }
}

void GraphPageViewBase::CreateLegend(
    const std::vector<Legend::Entry>& entries) {
  DCHECK(!legend_);
  legend_ = legend_container_->AddChildView(std::make_unique<Legend>(entries));
  legend_container_->SetVisible(true);
}

ReferenceLines* GraphPageViewBase::CreateReferenceLines(
    float left,
    float top,
    float right,
    float bottom,
    const std::u16string& x_unit,
    const std::u16string& y_unit,
    int horizontal_points_number,
    int horizontal_ticks_interval,
    float vertical_ticks_interval) {
  DCHECK(reference_lines_container_->children().empty());
  return reference_lines_container_->AddChildView(
      std::make_unique<ReferenceLines>(
          left, top, right, bottom, x_unit, y_unit, horizontal_points_number,
          horizontal_ticks_interval, vertical_ticks_interval));
}

void GraphPageViewBase::RefreshLegendValues() {
  if (legend_)
    legend_->RefreshValues();
}

}  // namespace hud_display
}  // namespace ash