| // 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/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/functional/bind.h" |
| #include "base/memory/raw_ptr.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 { |
| METADATA_HEADER(HTClientView, views::ClientView) |
| |
| public: |
| 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: |
| raw_ptr<HUDDisplayView> hud_display_; |
| }; |
| |
| BEGIN_METADATA(HTClientView) |
| END_METADATA |
| |
| std::unique_ptr<views::ClientView> MakeClientView(views::Widget* widget) { |
| auto view = std::make_unique<HUDDisplayView>(); |
| auto* weak_view = view.get(); |
| return std::make_unique<HTClientView>(weak_view, widget, view.release()); |
| } |
| |
| void InitializeFrameView(views::WidgetDelegate* delegate) { |
| auto* frame_view = static_cast<NonClientFrameViewAsh*>( |
| 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) |
| 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<views::WidgetDelegate>(); |
| 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"); |
| |
| ViewTreeHostRootView* root_view = |
| static_cast<ViewTreeHostRootView*>(widget->GetRootView()); |
| root_view->SetIsOverlayCandidate(g_hud_overlay_mode); |
| root_view->Init(widget->GetNativeView()); |
| 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>( |
| views::BoxLayout::Orientation::kVertical)); |
| layout_manager->set_cross_axis_alignment( |
| views::BoxLayout::CrossAxisAlignment::kStretch); |
| |
| header_view_ = AddChildView(std::make_unique<HUDHeaderView>(this)); |
| views::View* data = AddChildView(std::make_unique<views::View>()); |
| |
| // 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<views::FillLayout>()); |
| graphs_container_ = |
| data->AddChildView(std::make_unique<GraphsContainerView>()); |
| settings_view_ = data->AddChildView(std::make_unique<HUDSettingsView>(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<ViewTreeHostRootView*>(GetWidget()->GetRootView()) |
| ->GetIsOverlayCandidate(); |
| } |
| |
| void HUDDisplayView::ToggleOverlay() { |
| g_hud_overlay_mode = !g_hud_overlay_mode; |
| static_cast<ViewTreeHostRootView*>(GetWidget()->GetRootView()) |
| ->SetIsOverlayCandidate(g_hud_overlay_mode); |
| } |
| |
| // static |
| HUDDisplayView* HUDDisplayView::GetForTesting() { |
| if (!g_hud_widget) |
| return nullptr; |
| |
| HTClientView* client_view = |
| static_cast<HTClientView*>(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 |