chromium/ash/shelf/desk_button_widget.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SHELF_DESK_BUTTON_WIDGET_H_
#define ASH_SHELF_DESK_BUTTON_WIDGET_H_

#include "ash/ash_export.h"
#include "ash/shelf/shelf_component.h"
#include "base/memory/raw_ptr.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace ash {

class DeskButtonContainer;
class Shelf;
enum class ShelfAlignment;

// The desk button provides an overview of existing desks and quick access to
// them. The button is only visible in clamshell mode and disappears when in
// overview.
class ASH_EXPORT DeskButtonWidget : public ShelfComponent,
                                    public views::Widget {
 public:
  // Delegate view for laying out the desk button UI. It does not use the
  // default fill layout since the desk button UI has dynamic size, and the
  // widget reserves the maximum possible space for the current shelf alignment
  // and zero state.
  class DelegateView : public views::WidgetDelegateView {
   public:
    DelegateView();
    DelegateView(const DelegateView&) = delete;
    DelegateView& operator=(const DelegateView&) = delete;
    ~DelegateView() override;

    DeskButtonContainer* desk_button_container() const {
      return desk_button_container_;
    }

    // Initializes the view. Must be called before any meaningful UIs can be
    // laid out.
    void Init(DeskButtonWidget* desk_button_widget);

    // views::WidgetDelegateView:
    bool CanActivate() const override;
    void Layout(PassKey) override;

    // views::View:
    bool AcceleratorPressed(const ui::Accelerator& accelerator) override;

   private:
    raw_ptr<DeskButtonContainer> desk_button_container_ = nullptr;
    raw_ptr<DeskButtonWidget> desk_button_widget_ = nullptr;
  };

  explicit DeskButtonWidget(Shelf* shelf);
  DeskButtonWidget(const DeskButtonWidget&) = delete;
  DeskButtonWidget& operator=(const DeskButtonWidget&) = delete;
  ~DeskButtonWidget() override;

  // Returns the max length for the widget for the horizontal or vertical shelf.
  static int GetMaxLength(bool horizontal_shelf);

  DelegateView* delegate_view() const { return delegate_view_; }

  Shelf* shelf() const { return shelf_; }

  // Indicates if the shelf should reserve some space for this widget.
  bool ShouldReserveSpaceFromShelf() const;

  // Whether the desk button should currently be visible.
  bool ShouldBeVisible() const;

  // Updates expanded state and values impacted by shelf alignment change.
  void PrepareForAlignmentChange();

  // ShelfComponent:
  void CalculateTargetBounds() override;
  gfx::Rect GetTargetBounds() const override;
  void UpdateLayout(bool animate) override;
  void UpdateTargetBoundsForGesture(int shelf_position) override;

  // Called when shelf layout manager detects a locale change.
  void HandleLocaleChange();

  // Initializes the widget, sets its contents view and basic properties.
  void Initialize(aura::Window* container);

  DeskButtonContainer* GetDeskButtonContainer() const;

  // Returns true if this widget belongs to a horizontal shelf.
  bool IsHorizontalShelf() const;

  void SetDefaultChildToFocus(views::View* default_child_to_focus);

  // Stores the current focused view for desk button widget.
  void StoreDeskButtonFocus();

  // Restores focus to the stored focused view of desk button widget if there is
  // one.
  void RestoreDeskButtonFocus();

  // Depending on what child view has focus, either focus out of the desk
  // button, or pass the focus to the next view. `reverse` indicates backward
  // focusing, otherwise forward focusing.
  void MaybeFocusOut(bool reverse);

 private:
  // views::Widget:
  bool OnNativeWidgetActivationChanged(bool active) override;

  raw_ptr<DelegateView, DanglingUntriaged> delegate_view_ = nullptr;

  gfx::Rect target_bounds_;

  raw_ptr<Shelf> const shelf_;

  // Default child view to focus when `OnNativeWidgetActivationChanged()`
  // occurs. When it's not null, it should point to the desk button, the
  // previous desk button, or the next desk button.
  raw_ptr<views::View> default_child_to_focus_ = nullptr;

  // Stored focused view for the widget. This is used to restore the focus to
  // the desk button when the desk bar is closed. When it's not null, it should
  // point to the desk button, the previous desk button, or the next desk
  // button.
  raw_ptr<views::View> stored_focused_view_ = nullptr;
};

}  // namespace ash

#endif  // ASH_SHELF_DESK_BUTTON_WIDGET_H_