chromium/ash/style/tab_slider.h

// Copyright 2022 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_STYLE_TAB_SLIDER_H_
#define ASH_STYLE_TAB_SLIDER_H_

#include <cstddef>
#include <utility>

#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/layout/table_layout_view.h"

namespace ash {

class TabSliderButton;

// A tab slider has multiple slider buttons placed horizontally in a button
// container. At any time, only one of the buttons can be selected. Selecting a
// button will deselect all the other buttons. A selector painted with a fully
// rounded rectangle shows behind the selected button. When another button is
// selected, the selector will move from the position of previously selected
// button to the position of currently selected button.
class ASH_EXPORT TabSlider : public views::TableLayoutView {
  METADATA_HEADER(TabSlider, views::TableLayoutView)

 public:
  // The init parameters used to initialize the layout, appearance, and behavior
  // of the tab slider.
  struct InitParams {
    int internal_border_padding;
    int between_buttons_spacing;
    // Indicates whether there is a fully rounded rect background for the tab
    // slider.
    bool has_background;
    // Indicates whether an animation should be shown when the selector moves
    // between buttons.
    bool has_selector_animation;
    // Indicates whether the extra space should be distributed evenly between
    // buttons.
    bool distribute_space_evenly;
  };

  static constexpr InitParams kDefaultParams = {2, 0, true, true, true};

  // `max_tab_num` is the maximum number of tabs in the slider.
  explicit TabSlider(size_t max_tab_num,
                     const InitParams& params = kDefaultParams);
  TabSlider(const TabSlider&) = delete;
  TabSlider& operator=(const TabSlider&) = delete;
  ~TabSlider() override;

  views::View* GetSelectorView();
  TabSliderButton* GetButtonAtIndex(size_t index);

  // Add a button with the button's unique pointer. For example
  // AddButton(std::make_unique<SliderButtonType>(...)).
  template <typename T>
  T* AddButton(std::unique_ptr<T> button) {
    T* button_ptr = button.get();
    AddButtonInternal(button.release());
    return button_ptr;
  }

  // Add a button with the button's ctor arguments. For example
  // AddButton<SliderButtonType>(arg1, arg2, ...).
  template <typename T, typename... Args>
  T* AddButton(Args&&... args) {
    auto button = std::make_unique<T>(std::forward<Args>(args)...);
    return AddButton(std::move(button));
  }

  // Called when a button is selected.
  void OnButtonSelected(TabSliderButton* button);

  // views::View:
  void Layout(PassKey) override;

 private:
  // The view of the selector.
  class SelectorView;

  // Initialize the layout according to the total number of tabs and init
  // parameters.
  void Init();

  // Adds the button as a child of the button container and inserts it into the
  // 'buttons_' list.
  void AddButtonInternal(TabSliderButton* button);

  // Called when the enabled state is changed.
  void OnEnabledStateChanged();

  const size_t max_tab_num_;
  const InitParams params_;

  // Owned by view hierarchy.
  raw_ptr<SelectorView> selector_view_;
  std::vector<raw_ptr<TabSliderButton, VectorExperimental>> buttons_;

  base::CallbackListSubscription enabled_changed_subscription_;
};

}  // namespace ash

#endif  // ASH_STYLE_TAB_SLIDER_H_