// 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_display.h" #include "ash/fast_ink/view_tree_host_root_view.h" #include "ash/fast_ink/view_tree_host_widget.h" #include "ash/frame/non_client_frame_view_ash.h" #include "ash/hud_display/graphs_container_view.h" #include "ash/hud_display/hud_constants.h" #include "ash/hud_display/hud_header_view.h" #include "ash/hud_display/hud_properties.h" #include "ash/hud_display/hud_settings_view.h" #include "ash/hud_display/tab_strip.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "base/bind.h" #include "base/strings/utf_string_conversions.h" #include "components/vector_icons/vector_icons.h" #include "ui/aura/window.h" #include "ui/base/hit_test.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/compositor/layer.h" #include "ui/events/base_event_utils.h" #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/widget/native_widget.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" namespace ash { namespace hud_display { namespace { // Header height. constexpr int kHUDHeaderHeight = kHUDSettingsIconSize + 2 * kHUDSettingsIconBorder; // Margin below header. constexpr int kHUDHeaderMargin = 5; // Graph height. constexpr int kHUDGraphHeight = 300; // Graph width/height including bordering reference lines. constexpr int kHUDGraphWidthWithReferenceLines = kHUDGraphWidth + 2 * kHUDGraphReferenceLineWidth; constexpr int kHUDGraphHeightWithReferenceLines = kHUDGraphHeight + 2 * kHUDGraphReferenceLineWidth; // HUD window width. constexpr int kHUDWidth = kHUDGraphWidthWithReferenceLines + 2 * kHUDInset; // Top inset + header + header margin + bottom inset. Used to compute the HUD // window height. Just add the graph height or settings height as appropriate. constexpr int kHUDFrameHeight = kHUDInset + kHUDHeaderHeight + kHUDHeaderMargin + kHUDInset; // HUD window height with graph. constexpr int kHUDHeightWithGraph = kHUDFrameHeight + kHUDGraphHeightWithReferenceLines; views::Widget* g_hud_widget = nullptr; // True if HUD should be initialized as overlay. bool g_hud_overlay_mode = true; // ClientView that return HTNOWHERE by default. A child view can receive event // by setting kHitTestComponentKey property to HTCLIENT. class HTClientView : public views::ClientView { public: METADATA_HEADER(HTClientView); HTClientView(HUDDisplayView* hud_display, views::Widget* widget, views::View* contents_view) : views::ClientView(widget, contents_view), hud_display_(hud_display) {} HTClientView(const HTClientView&) = delete; HTClientView& operator=(const HTClientView&) = delete; ~HTClientView() override = default; // views::ClientView int NonClientHitTest(const gfx::Point& point) override { return hud_display_->NonClientHitTest(point); } HUDDisplayView* GetHUDDisplayViewForTesting() { return hud_display_; } private: HUDDisplayView* hud_display_; }; BEGIN_METADATA(HTClientView, views::ClientView) END_METADATA std::unique_ptr MakeClientView(views::Widget* widget) { auto view = std::make_unique(); auto* weak_view = view.get(); return std::make_unique(weak_view, widget, view.release()); } void InitializeFrameView(views::WidgetDelegate* delegate) { auto* frame_view = static_cast( delegate->GetWidget()->non_client_view()->frame_view()); // TODO(oshima): support component type with TYPE_WINDOW_FLAMELESS widget. if (frame_view) frame_view->SetFrameEnabled(false); } } // namespace //////////////////////////////////////////////////////////////////////////////// // HUDDisplayView, public: BEGIN_METADATA(HUDDisplayView, views::View) END_METADATA // static void HUDDisplayView::Destroy() { delete g_hud_widget; g_hud_widget = nullptr; } // static void HUDDisplayView::Toggle() { if (g_hud_widget) { Destroy(); return; } auto delegate = std::make_unique(); delegate->SetClientViewFactory(base::BindOnce(&MakeClientView)); delegate->RegisterWidgetInitializedCallback( base::BindOnce(&InitializeFrameView, base::Unretained(delegate.get()))); delegate->SetOwnedByWidget(true); views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.delegate = delegate.release(); params.parent = Shell::GetContainer(Shell::GetPrimaryRootWindow(), kShellWindowId_OverlayContainer); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(kHUDWidth, kHUDHeightWithGraph); auto* widget = CreateViewTreeHostWidget(std::move(params)); widget->GetLayer()->SetName("HUDDisplayView"); static_cast(widget->GetRootView()) ->SetIsOverlayCandidate(g_hud_overlay_mode); widget->Show(); g_hud_widget = widget; } // static bool HUDDisplayView::IsShown() { return g_hud_widget; } HUDDisplayView::HUDDisplayView() { DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_); // Layout: // ---------------------- // | Header | // Buttons, tabs, controls // ---------------------- // | | // Data views full-size, z-stacked. // | Data | // | | // ---------------------- // Create two child views for header and data. Vertically stacked. views::BoxLayout* layout_manager = SetLayoutManager(std::make_unique( views::BoxLayout::Orientation::kVertical)); layout_manager->set_cross_axis_alignment( views::BoxLayout::CrossAxisAlignment::kStretch); header_view_ = AddChildView(std::make_unique(this)); views::View* data = AddChildView(std::make_unique()); // Data view takes the rest of the host view. layout_manager->SetFlexForView(data, 1, /*use_min_size=*/false); // Setup header. header_view_->tab_strip()->AddTabButton(HUDDisplayMode::CPU, u"CPU"); header_view_->tab_strip()->AddTabButton(HUDDisplayMode::MEMORY, u"RAM"); header_view_->tab_strip()->AddTabButton(HUDDisplayMode::FPS, u"FPS"); // Setup data. data->SetBackground(views::CreateSolidBackground(kHUDBackground)); data->SetBorder(views::CreateEmptyBorder( gfx::Insets::TLBR(kHUDHeaderMargin, kHUDInset, kHUDInset, kHUDInset))); // We have two child views z-stacked. // The bottom one is GraphsContainerView with all the graph lines. // The top one is settings UI overlay. data->SetLayoutManager(std::make_unique()); graphs_container_ = data->AddChildView(std::make_unique()); settings_view_ = data->AddChildView(std::make_unique(this)); settings_view_->SetVisible(false); // CPU display is active by default. SetDisplayMode(HUDDisplayMode::CPU); } HUDDisplayView::~HUDDisplayView() { DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_); } // There is only one button. void HUDDisplayView::OnSettingsToggle() { gfx::Rect bounds = g_hud_widget->GetWindowBoundsInScreen(); // Here we are checking the settings visibility before we toggle it. We must // keep in mind that it is the opposite of what it will be. bounds.set_height(settings_view_->GetVisible() ? kHUDHeightWithGraph : kHUDFrameHeight + settings_view_->GetPreferredSize().height()); g_hud_widget->SetBounds(bounds); settings_view_->ToggleVisibility(); graphs_container_->SetVisible(!settings_view_->GetVisible()); } bool HUDDisplayView::IsOverlay() { return static_cast(GetWidget()->GetRootView()) ->GetIsOverlayCandidate(); } void HUDDisplayView::ToggleOverlay() { g_hud_overlay_mode = !g_hud_overlay_mode; static_cast(GetWidget()->GetRootView()) ->SetIsOverlayCandidate(g_hud_overlay_mode); } // static HUDDisplayView* HUDDisplayView::GetForTesting() { if (!g_hud_widget) return nullptr; HTClientView* client_view = static_cast(g_hud_widget->client_view()); if (!client_view) return nullptr; return client_view->GetHUDDisplayViewForTesting(); // IN-TEST } HUDSettingsView* HUDDisplayView::GetSettingsViewForTesting() { return settings_view_; } void HUDDisplayView::ToggleSettingsForTesting() { OnSettingsToggle(); } int HUDDisplayView::NonClientHitTest(const gfx::Point& point) { const View* view = GetEventHandlerForPoint(point); if (!view) return HTNOWHERE; return view->GetProperty(kHUDClickHandler); } void HUDDisplayView::SetDisplayMode(HUDDisplayMode display_mode) { graphs_container_->SetMode(display_mode); header_view_->tab_strip()->ActivateTab(display_mode); } } // namespace hud_display } // namespace ash