// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/hud_display/legend.h" #include "ash/hud_display/graph.h" #include "ash/hud_display/hud_constants.h" #include "ash/hud_display/solid_source_background.h" #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/gfx/canvas.h" #include "ui/gfx/text_constants.h" #include "ui/views/border.h" #include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" namespace ash { namespace hud_display { namespace { class LegendEntry : public views::View { public: METADATA_HEADER(LegendEntry); explicit LegendEntry(const Legend::Entry& data); LegendEntry(const LegendEntry&) = delete; LegendEntry& operator=(const LegendEntry&) = delete; ~LegendEntry() override; // views::View: void OnPaint(gfx::Canvas* canvas) override; void SetValueIndex(size_t index); void RefreshValue(); // This is used by parent to match sizes. views::View* value() { return value_; } private: const SkColor color_; const Graph& graph_; size_t value_index_ = 0; Legend::Formatter formatter_; views::Label* value_ = nullptr; }; BEGIN_METADATA(LegendEntry, views::View) END_METADATA LegendEntry::LegendEntry(const Legend::Entry& data) : color_(data.graph.color()), graph_(data.graph), formatter_(data.formatter) { views::BoxLayout* layout_manager = SetLayoutManager(std::make_unique( views::BoxLayout::Orientation::kHorizontal)); layout_manager->set_cross_axis_alignment( views::BoxLayout::CrossAxisAlignment::kCenter); // We need to allocate space for the colorpicker. It should be square with // edge size matching default views::Label height. This is not known until // layout runs, so just hard code it. constexpr int kColorpickerAreaWidth = 20; SetBorder(views::CreateEmptyBorder( gfx::Insets::TLBR(0, kColorpickerAreaWidth, 0, 0))); views::Label* label = AddChildView( std::make_unique(data.label, views::style::CONTEXT_LABEL)); label->SetEnabledColor(kHUDDefaultColor); if (!data.tooltip.empty()) label->SetTooltipText(data.tooltip); constexpr int kLabelToValueSpece = 5; value_ = AddChildView(std::make_unique( std::u16string(), views::style::CONTEXT_LABEL)); layout_manager->SetFlexForView(value_, /*flex=*/1); value_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_RIGHT); value_->SetBorder( views::CreateEmptyBorder(gfx::Insets::TLBR(0, kLabelToValueSpece, 0, 0))); value_->SetEnabledColor(kHUDDefaultColor); } LegendEntry::~LegendEntry() = default; void LegendEntry::OnPaint(gfx::Canvas* canvas) { // Draw 10x10 sold color rectangle in the middle of the left border. // (We used border to allocate space for the colorpicker above.) constexpr int kBoxSize = 10; const gfx::Rect bounds(GetInsets().left(), height()); constexpr int kBoxBorderWidth = 1; gfx::Rect box = bounds; box.ClampToCenteredSize(gfx::Size(kBoxSize, kBoxSize)); const SkRect border = SkRect::MakeXYWH(box.x(), box.y(), box.width(), box.height()); SkPath box_border; box_border.addRect(border); SkPath box_filled; box_filled.addRect(border.makeInset(kBoxBorderWidth, kBoxBorderWidth)); cc::PaintFlags flags; flags.setAntiAlias(true); flags.setBlendMode(SkBlendMode::kSrc); flags.setStyle(cc::PaintFlags::kFill_Style); flags.setColor(color_); canvas->DrawPath(box_filled, flags); flags.setStyle(cc::PaintFlags::kStroke_Style); flags.setStrokeWidth(kBoxBorderWidth); flags.setColor(kHUDDefaultColor); canvas->DrawPath(box_border, flags); views::View::OnPaint(canvas); } void LegendEntry::SetValueIndex(size_t index) { if (index == value_index_) return; value_index_ = index; RefreshValue(); } void LegendEntry::RefreshValue() { if (graph_.IsFilledIndex(value_index_)) { value_->SetText(formatter_.Run(graph_.GetUnscaledValueAt(value_index_))); } else { value_->SetText(std::u16string()); } } } // namespace Legend::Entry::Entry(const Graph& graph, std::u16string label, std::u16string tooltip, Formatter formatter) : graph(graph), label(label), tooltip(tooltip), formatter(formatter) {} Legend::Entry::Entry(const Entry&) = default; Legend::Entry::~Entry() = default; BEGIN_METADATA(Legend, views::View) END_METADATA Legend::Legend(const std::vector& contents) { views::BoxLayout* layout_manager = SetLayoutManager(std::make_unique( views::BoxLayout::Orientation::kVertical)); layout_manager->set_cross_axis_alignment( views::BoxLayout::CrossAxisAlignment::kStretch); SetBackground(std::make_unique(kHUDLegendBackground, /*radius=*/0)); SetBorder(views::CreateEmptyBorder(kHUDInset)); for (const auto& entry : contents) AddChildView(std::make_unique(entry)); } Legend::~Legend() = default; void Legend::Layout() { views::View::Layout(); gfx::Size max_size; bool updated = false; for (auto* view : children()) { if (view->GetClassName() != LegendEntry::kViewClassName) continue; views::View* value = static_cast(view)->value(); max_size.SetToMax(value->GetPreferredSize()); updated |= max_size != value->GetPreferredSize(); } if (updated) { for (auto* view : children()) { if (view->GetClassName() != LegendEntry::kViewClassName) continue; static_cast(view)->value()->SetPreferredSize(max_size); } views::View::Layout(); } } void Legend::SetValuesIndex(size_t index) { for (auto* view : children()) { if (view->GetClassName() != LegendEntry::kViewClassName) continue; static_cast(view)->SetValueIndex(index); } } void Legend::RefreshValues() { for (auto* view : children()) { if (view->GetClassName() != LegendEntry::kViewClassName) continue; static_cast(view)->RefreshValue(); } } } // namespace hud_display } // namespace ash