| // 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/hud_header_view.h" |
| |
| #include "ash/hud_display/hud_constants.h" |
| #include "ash/hud_display/hud_display.h" |
| #include "ash/hud_display/hud_properties.h" |
| #include "ash/hud_display/solid_source_background.h" |
| #include "ash/hud_display/tab_strip.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/gfx/skia_util.h" |
| #include "ui/views/background.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/layout_manager.h" |
| #include "ui/views/metadata/metadata_impl_macros.h" |
| |
| namespace ash { |
| namespace hud_display { |
| namespace { |
| |
| // Draws bottom left rounded background triangle. |
| class BottomLeftOuterBackground : public views::Background { |
| public: |
| // Background will have left bottom rounded corner with |top_rounding_radius|. |
| BottomLeftOuterBackground(SkColor color, SkScalar top_rounding_radius) |
| : inner_radius_(top_rounding_radius) { |
| SetNativeControlColor(color); |
| } |
| |
| BottomLeftOuterBackground(const BottomLeftOuterBackground&) = delete; |
| BottomLeftOuterBackground& operator=(const BottomLeftOuterBackground&) = |
| delete; |
| |
| ~BottomLeftOuterBackground() override = default; |
| |
| // views::Background |
| void Paint(gfx::Canvas* canvas, views::View* view) const override { |
| const SkScalar circle_size = inner_radius_ * 2; |
| const int bottom_edge = view->height(); |
| |
| SkPath path; |
| path.moveTo(0, bottom_edge); |
| /* |false| will draw straight line to the start of the arc */ |
| path.arcTo({0, bottom_edge - circle_size, circle_size, bottom_edge}, 90, 90, |
| false); |
| path.lineTo(0, bottom_edge); |
| |
| cc::PaintFlags flags; |
| flags.setAntiAlias(true); |
| flags.setBlendMode(SkBlendMode::kSrc); |
| flags.setStyle(cc::PaintFlags::kFill_Style); |
| flags.setColor(get_color()); |
| canvas->DrawPath(path, flags); |
| } |
| |
| private: |
| SkScalar inner_radius_; |
| }; |
| |
| // ImageButton with underline |
| class SettingsButton : public views::ImageButton { |
| public: |
| METADATA_HEADER(SettingsButton); |
| |
| explicit SettingsButton(views::Button::PressedCallback callback) |
| : views::ImageButton(callback) { |
| SetImage(views::Button::ButtonState::STATE_NORMAL, |
| gfx::CreateVectorIcon(vector_icons::kSettingsIcon, |
| kHUDSettingsIconSize, kHUDDefaultColor)); |
| SetBorder(views::CreateEmptyBorder(gfx::Insets(kSettingsIconBorder))); |
| SetProperty(kHUDClickHandler, HTCLIENT); |
| |
| SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY); |
| } |
| |
| SettingsButton(const SettingsButton&) = delete; |
| SettingsButton& operator=(const SettingsButton&) = delete; |
| |
| ~SettingsButton() 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(SettingsButton, views::ImageButton) |
| END_METADATA |
| |
| // Basically FillLayout that matches host size to the given data view. |
| // Padding will take the rest of the host view to the right. |
| class HUDHeaderLayout : public views::LayoutManager { |
| public: |
| HUDHeaderLayout(const views::View* data_view, views::View* padding) |
| : data_view_(data_view), padding_(padding) {} |
| |
| HUDHeaderLayout(const HUDHeaderLayout&) = delete; |
| HUDHeaderLayout& operator=(const HUDHeaderLayout&) = delete; |
| |
| ~HUDHeaderLayout() override = default; |
| |
| // views::LayoutManager: |
| void Layout(views::View* host) override; |
| gfx::Size GetPreferredSize(const views::View* host) const override; |
| |
| private: |
| const views::View* data_view_; |
| views::View* padding_; |
| }; |
| |
| gfx::Size HUDHeaderLayout::GetPreferredSize(const views::View* host) const { |
| return data_view_->GetPreferredSize() + padding_->GetPreferredSize(); |
| } |
| |
| void HUDHeaderLayout::Layout(views::View* host) { |
| // Assume there are only 3 child views (data, background and padding). |
| DCHECK_EQ(host->children().size(), 3U); |
| |
| const gfx::Size preferred_size = data_view_->GetPreferredSize(); |
| |
| for (auto* child : host->children()) { |
| if (child != padding_) { |
| child->SetPosition({0, 0}); |
| child->SetSize(preferred_size); |
| } |
| } |
| // Layout padding |
| padding_->SetPosition({preferred_size.width(), 0}); |
| padding_->SetSize({host->width() - preferred_size.width(), host->height()}); |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // HUDHeaderView |
| |
| BEGIN_METADATA(HUDHeaderView, views::View) |
| END_METADATA |
| |
| HUDHeaderView::HUDHeaderView(HUDDisplayView* hud) { |
| // Header is rendered as horizontal container with three children: |
| // background, buttons container and padding. |
| |
| // Header should have background under the buttons area only. |
| // To achieve this we have buttons container view to calculate total width, |
| // and special layout manager that matches size of the background view to the |
| // size of the buttons. |
| // |
| // There is additional "padding" view an the end to draw bottom inner |
| // rounded piece of background. |
| |
| views::View* header_background = |
| AddChildView(std::make_unique<views::View>()); |
| header_background->SetBackground(std::make_unique<SolidSourceBackground>( |
| kHUDBackground, kTabOverlayCornerRadius)); |
| |
| views::View* header_buttons = AddChildView(std::make_unique<views::View>()); |
| header_buttons |
| ->SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kHorizontal)) |
| ->set_cross_axis_alignment( |
| views::BoxLayout::CrossAxisAlignment::kStretch); |
| // Header does not have margin between header and data. |
| // Data has its top margin (kHUDGraphsInset). |
| header_buttons->SetBorder(views::CreateEmptyBorder( |
| gfx::Insets(kHUDInset, kHUDInset, 0, kHUDInset))); |
| |
| // Add buttons and tab strip. |
| header_buttons->AddChildView( |
| std::make_unique<SettingsButton>(base::BindRepeating( |
| &HUDDisplayView::OnSettingsToggle, base::Unretained(hud)))); |
| tab_strip_ = header_buttons->AddChildView(std::make_unique<HUDTabStrip>(hud)); |
| |
| // Padding will take the rest of the header and draw bottom inner left |
| // background padding. |
| views::View* padding = AddChildView(std::make_unique<views::View>()); |
| padding->SetBackground(std::make_unique<BottomLeftOuterBackground>( |
| kHUDBackground, kTabOverlayCornerRadius)); |
| |
| SetLayoutManager(std::make_unique<HUDHeaderLayout>(header_buttons, padding)); |
| } |
| |
| HUDHeaderView::~HUDHeaderView() = default; |
| |
| } // namespace hud_display |
| } // namespace ash |