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

#ifndef UI_VIEWS_CONTROLS_TREE_TREE_VIEW_H_
#define UI_VIEWS_CONTROLS_TREE_TREE_VIEW_H_

#include <memory>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "ui/base/models/image_model.h"
#include "ui/base/models/tree_node_model.h"
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/controls/prefix_delegate.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/controls/tree/tree_view_drawing_provider.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/view.h"

namespace ui {

struct AXActionData;

}  // namespace ui

namespace gfx {

class Rect;

}  // namespace gfx

namespace views {

class AXVirtualView;
class PrefixSelector;
class ScrollView;
class Textfield;
class TreeViewController;

// TreeView displays hierarchical data as returned from a TreeModel. The user
// can expand, collapse and edit the items. A Controller may be attached to
// receive notification of selection changes and restrict editing.
//
// In addition to tracking selection, TreeView also tracks the active node,
// which is the item that receives keyboard input when the tree has focus.
// Active/focus is like a pointer for keyboard navigation, and operations such
// as selection are performed at the point of focus. The active node is synced
// to the selected node. When the active node is nullptr, the TreeView itself is
// the target of keyboard input.
//
// Note on implementation. This implementation doesn't scale well. In particular
// it does not store any row information, but instead calculates it as
// necessary. But it's more than adequate for current uses.
class VIEWS_EXPORT TreeView : public View,
                              public ui::TreeModelObserver,
                              public TextfieldController,
                              public FocusChangeListener,
                              public PrefixDelegate {
  METADATA_HEADER(TreeView, View)

 public:
  TreeView();

  TreeView(const TreeView&) = delete;
  TreeView& operator=(const TreeView&) = delete;

  ~TreeView() override;

  // Returns a new ScrollView that contains the given |tree|.
  static std::unique_ptr<ScrollView> CreateScrollViewWithTree(
      std::unique_ptr<TreeView> tree);

  // Sets the model. TreeView does not take ownership of the model.
  void SetModel(ui::TreeModel* model);
  ui::TreeModel* model() const { return model_; }

  // Sets whether to automatically expand children when a parent node is
  // expanded. The default is false. If true, when a node in the tree is
  // expanded for the first time, its children are also automatically expanded.
  // If a node is subsequently collapsed and expanded again, the children
  // will not be automatically expanded.
  void set_auto_expand_children(bool auto_expand_children) {
    auto_expand_children_ = auto_expand_children;
  }

  // Sets whether the user can edit the nodes. The default is true. If true,
  // the Controller is queried to determine if a particular node can be edited.
  void SetEditable(bool editable);

  // Edits the specified node. This cancels the current edit and expands all
  // parents of node.
  void StartEditing(ui::TreeModelNode* node);

  // Cancels the current edit. Does nothing if not editing.
  void CancelEdit();

  // Commits the current edit. Does nothing if not editing.
  void CommitEdit();

  // If the user is editing a node, it is returned. If the user is not
  // editing a node, NULL is returned.
  ui::TreeModelNode* GetEditingNode();

  // Selects the specified node. This expands all the parents of node.
  void SetSelectedNode(ui::TreeModelNode* model_node);

  // Returns the selected node, or nullptr if nothing is selected.
  ui::TreeModelNode* GetSelectedNode() {
    return const_cast<ui::TreeModelNode*>(
        const_cast<const TreeView*>(this)->GetSelectedNode());
  }
  const ui::TreeModelNode* GetSelectedNode() const;

  // Marks the specified node as active, scrolls it into view, and reports a
  // keyboard focus update to ATs. Active node should be synced to the selected
  // node and should be nullptr when the tree is empty.
  // TODO(crbug.com/40691087): Decouple active node from selected node by adding
  // new keyboard affordances.
  void SetActiveNode(ui::TreeModelNode* model_node);

  // Returns the active node, or nullptr if nothing is active.
  ui::TreeModelNode* GetActiveNode() {
    return const_cast<ui::TreeModelNode*>(
        const_cast<const TreeView*>(this)->GetActiveNode());
  }
  const ui::TreeModelNode* GetActiveNode() const;

  // Marks |model_node| as collapsed. This only effects the UI if node and all
  // its parents are expanded (IsExpanded(model_node) returns true).
  void Collapse(ui::TreeModelNode* model_node);

  // Make sure node and all its parents are expanded.
  void Expand(ui::TreeModelNode* node);

  // Invoked from ExpandAll(). Expands the supplied node and recursively
  // invokes itself with all children.
  void ExpandAll(ui::TreeModelNode* node);

  // Returns true if the specified node is expanded.
  bool IsExpanded(ui::TreeModelNode* model_node);

  // Sets whether the root is shown. If true, the root node of the tree is
  // shown, if false only the children of the root are shown. The default is
  // true.
  void SetRootShown(bool root_visible);

  // Sets the controller, which may be null. TreeView does not take ownership
  // of the controller.
  void SetController(TreeViewController* controller) {
    controller_ = controller;
  }

  // Returns the node for the specified row, or NULL for an invalid row index.
  ui::TreeModelNode* GetNodeForRow(int row);

  // Maps a node to a row, returns -1 if node is not valid.
  int GetRowForNode(ui::TreeModelNode* node);

  Textfield* editor() { return editor_; }

  // Replaces this TreeView's TreeViewDrawingProvider with |provider|.
  void SetDrawingProvider(std::unique_ptr<TreeViewDrawingProvider> provider);

  TreeViewDrawingProvider* drawing_provider() {
    return drawing_provider_.get();
  }

  void SetInitialAccessibilityAttributes();

  // View overrides:
  void Layout(PassKey) override;
  gfx::Size CalculatePreferredSize(
      const SizeBounds& /*available_size*/) const override;
  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
  bool OnMousePressed(const ui::MouseEvent& event) override;
  void OnGestureEvent(ui::GestureEvent* event) override;
  void ShowContextMenu(const gfx::Point& p,
                       ui::mojom::MenuSourceType source_type) override;
  bool HandleAccessibleAction(const ui::AXActionData& action_data) override;

  // TreeModelObserver overrides:
  void TreeNodeAdded(ui::TreeModel* model,
                     ui::TreeModelNode* parent,
                     size_t index) override;
  void TreeNodeRemoved(ui::TreeModel* model,
                       ui::TreeModelNode* parent,
                       size_t index) override;
  void TreeNodeChanged(ui::TreeModel* model,
                       ui::TreeModelNode* model_node) override;

  // TextfieldController overrides:
  void ContentsChanged(Textfield* sender,
                       const std::u16string& new_contents) override;
  bool HandleKeyEvent(Textfield* sender,
                      const ui::KeyEvent& key_event) override;

  // FocusChangeListener overrides:
  void OnWillChangeFocus(View* focused_before, View* focused_now) override;
  void OnDidChangeFocus(View* focused_before, View* focused_now) override;

  // PrefixDelegate overrides:
  size_t GetRowCount() override;
  std::optional<size_t> GetSelectedRow() override;
  void SetSelectedRow(std::optional<size_t> row) override;
  std::u16string GetTextForRow(size_t row) override;

 protected:
  // View overrides:
  gfx::Point GetKeyboardContextMenuLocation() override;
  bool OnKeyPressed(const ui::KeyEvent& event) override;
  void OnPaint(gfx::Canvas* canvas) override;
  void OnFocus() override;
  void OnBlur() override;

 private:
  friend class TreeViewTest;

  // Enumeration of possible changes to tree view state when the UI is updated.
  enum class SelectionType {
    // Active state is being set to a tree item.
    kActive,

    // Active and selected states are being set to a tree item.
    kActiveAndSelected,
  };

  // Performs active node and selected node state transitions. Updates states
  // and scrolling before notifying assistive technologies and the controller.
  void UpdateSelection(ui::TreeModelNode* model_node,
                       SelectionType selection_type);

  // Selects, expands or collapses nodes in the tree.  Consistent behavior for
  // tap gesture and click events.
  bool OnClickOrTap(const ui::LocatedEvent& event);

  // InternalNode is used to track information about the set of nodes displayed
  // by TreeViewViews.
  class InternalNode : public ui::TreeNode<InternalNode> {
   public:
    InternalNode();

    InternalNode(const InternalNode&) = delete;
    InternalNode& operator=(const InternalNode&) = delete;

    ~InternalNode() override;

    // Resets the state from |node|.
    void Reset(ui::TreeModelNode* node);

    // The model node this InternalNode represents.
    ui::TreeModelNode* model_node() { return model_node_; }

    // Gets or sets a virtual accessibility view that is used to expose
    // information about this node to assistive software.
    void set_accessibility_view(AXVirtualView* accessibility_view) {
      accessibility_view_ = accessibility_view;
    }
    AXVirtualView* accessibility_view() const { return accessibility_view_; }

    // Whether the node is expanded.
    void set_is_expanded(bool expanded);
    bool is_expanded() const { return is_expanded_; }
    void SetAccessibleIsExpanded(bool expanded);

    // Whether children have been loaded.
    void set_loaded_children(bool value) { loaded_children_ = value; }
    bool loaded_children() const { return loaded_children_; }

    // Width needed to display the string.
    void set_text_width(int width) { text_width_ = width; }
    int text_width() const { return text_width_; }

    // Returns the total number of descendants (including this node).
    size_t NumExpandedNodes() const;

    void UpdateAccessibleName();

    // Returns the max width of all descendants (including this node). |indent|
    // is how many pixels each child is indented and |depth| is the depth of
    // this node from its parent. The tree this node is being placed inside is
    // |tree|.
    int GetMaxWidth(TreeView* tree, int indent, int depth);

   private:
    // The node from the model.
    raw_ptr<ui::TreeModelNode> model_node_ = nullptr;

    // A virtual accessibility view that is used to expose information about
    // this node to assistive software. The view is owned by the Views system.
    raw_ptr<AXVirtualView> accessibility_view_ = nullptr;

    // Whether the children have been loaded.
    bool loaded_children_ = false;

    bool is_expanded_ = false;

    int text_width_ = 0;
  };

  // Used by GetInternalNodeForModelNode.
  enum class CreateType {
    // If an InternalNode hasn't been created yet, create it.
    kCreateIfNotLoaded,

    // Don't create an InternalNode if one hasn't been created yet.
    kDontCreateIfNotLoaded,
  };

  // Used by IncrementSelection.
  enum class IncrementType {
    // Selects the next node.
    kNext,

    // Selects the previous node.
    kPrevious
  };

  // Row of the root node. This varies depending upon whether the root is
  // visible.
  int root_row() const { return root_shown_ ? 0 : -1; }

  // Depth of the root node.
  int root_depth() const { return root_shown_ ? 0 : -1; }

  // Loads the children of the specified node.
  void LoadChildren(InternalNode* node);

  void UpdateAccessiblePositionalProperties(InternalNode* node);
  void UpdateAccessiblePositionalPropertiesForNodeAndChildren(
      InternalNode* node);

  // Configures an InternalNode from a node from the model. This is used
  // when a node changes as well as when loading.
  void ConfigureInternalNode(ui::TreeModelNode* model_node, InternalNode* node);

  // Returns whether the given node is the root.
  bool IsRoot(const InternalNode* node) const;

  // Sets |node|s text_width.
  void UpdateNodeTextWidth(InternalNode* node);

  // Creates a virtual accessibility view that is used to expose information
  // about this node to assistive software.
  //
  // Also attaches the newly created virtual accessibility view to the internal
  // node.
  std::unique_ptr<AXVirtualView> CreateAndSetAccessibilityView(
      InternalNode* node);

  void SetAccessibleSelectionForNode(InternalNode* node, bool selected);

  // Invoked when the set of drawn nodes changes.
  void DrawnNodesChanged();

  // Updates |preferred_size_| from the state of the UI.
  void UpdatePreferredSize();

  // Positions |editor_|.
  void LayoutEditor();

  // Schedules a paint for |node|.
  void SchedulePaintForNode(InternalNode* node);

  // Recursively paints rows from |min_row| to |max_row|. |node| is the node for
  // the row |*row|. |row| is updated as this walks the tree. Depth is the depth
  // of |*row|.
  void PaintRows(gfx::Canvas* canvas,
                 int min_row,
                 int max_row,
                 InternalNode* node,
                 int depth,
                 int* row);

  // Invoked to paint a single node.
  void PaintRow(gfx::Canvas* canvas, InternalNode* node, int row, int depth);

  // Paints the expand control given the specified nodes bounds.
  void PaintExpandControl(gfx::Canvas* canvas,
                          const gfx::Rect& node_bounds,
                          bool expanded);

  // Paints the icon for the specified |node| in |bounds| to |canvas|.
  void PaintNodeIcon(gfx::Canvas* canvas,
                     InternalNode* node,
                     const gfx::Rect& bounds);

  // Returns the InternalNode for a model node. |create_type| indicates whether
  // this should load InternalNode or not.
  InternalNode* GetInternalNodeForModelNode(ui::TreeModelNode* model_node,
                                            CreateType create_type);

  // Returns the InternalNode for a virtual view.
  InternalNode* GetInternalNodeForVirtualView(AXVirtualView* ax_view);

  // Returns the bounds for a node. This rectangle contains the node's icon,
  // text, arrow, and auxiliary text (if any). All of the other bounding
  // rectangles computed by the functions below lie inside this rectangle.
  gfx::Rect GetBoundsForNode(InternalNode* node);

  // Returns the bounds for a node's background.
  gfx::Rect GetBackgroundBoundsForNode(InternalNode* node);

  // Return the bounds for a node's foreground, which is the part containing the
  // expand/collapse symbol (if any), the icon (if any), and the text label.
  gfx::Rect GetForegroundBoundsForNode(InternalNode* node);

  // Returns the bounds for a node's text label.
  gfx::Rect GetTextBoundsForNode(InternalNode* node);

  // Returns the bounds of a node's auxiliary text label.
  gfx::Rect GetAuxiliaryTextBoundsForNode(InternalNode* node);

  // Implementation of GetTextBoundsForNode. Separated out as some callers
  // already know the row/depth.
  gfx::Rect GetForegroundBoundsForNodeImpl(InternalNode* node,
                                           int row,
                                           int depth);

  // Returns the row and depth of a node.
  int GetRowForInternalNode(InternalNode* node, int* depth);

  // Returns the InternalNode (if any) whose foreground bounds contain |point|.
  // If no node's foreground contains |point|, this function returns nullptr.
  InternalNode* GetNodeAtPoint(const gfx::Point& point);

  // Returns the row and depth of the specified node.
  InternalNode* GetNodeByRow(int row, int* depth);

  // Implementation of GetNodeByRow. |current_row| is updated as we iterate.
  InternalNode* GetNodeByRowImpl(InternalNode* node,
                                 int target_row,
                                 int current_depth,
                                 int* current_row,
                                 int* node_depth);

  // Increments the selection. Invoked in response to up/down arrow.
  void IncrementSelection(IncrementType type);

  // If the current node is expanded, it's collapsed, otherwise selection is
  // moved to the parent.
  void CollapseOrSelectParent();

  // If the selected node is collapsed, it's expanded. Otherwise the first child
  // is selected.
  void ExpandOrSelectChild();

  // Implementation of Expand(). Returns true if at least one node was expanded
  // that previously wasn't.
  bool ExpandImpl(ui::TreeModelNode* model_node);

  PrefixSelector* GetPrefixSelector();

  // Returns whether |point| is in the bounds of |node|'s expand/collapse
  // control.
  bool IsPointInExpandControl(InternalNode* node, const gfx::Point& point);

  // Sets whether a focus indicator is visible on this control or not.
  void SetHasFocusIndicator(bool);

  // The model, may be null.
  raw_ptr<ui::TreeModel> model_ = nullptr;

  // Default folder icon.
  ui::ImageModel folder_icon_;

  // Icons from the model.
  std::vector<ui::ImageModel> icons_;

  // The root node.
  InternalNode root_;

  // The selected node, may be null.
  raw_ptr<InternalNode> selected_node_ = nullptr;

  // The current active node, may be null.
  raw_ptr<InternalNode> active_node_ = nullptr;

  bool editing_ = false;

  // The editor; lazily created and never destroyed (well, until TreeView is
  // destroyed). Hidden when no longer editing. We do this to avoid destruction
  // problems.
  raw_ptr<Textfield> editor_ = nullptr;

  // Preferred size of |editor_| with no content.
  gfx::Size empty_editor_size_;

  // If non-NULL we've attached a listener to this focus manager. Used to know
  // when focus is changing to another view so that we can cancel the edit.
  raw_ptr<FocusManager> focus_manager_ = nullptr;

  // Whether to automatically expand children when a parent node is expanded.
  bool auto_expand_children_ = false;

  // Whether the user can edit the items.
  bool editable_ = true;

  // The controller.
  raw_ptr<TreeViewController> controller_ = nullptr;

  // Whether or not the root is shown in the tree.
  bool root_shown_ = true;

  // Cached preferred size.
  gfx::Size preferred_size_;

  // Font list used to display text.
  gfx::FontList font_list_;

  // Height of each row. Based on font and some padding.
  int row_height_;

  // Offset the text is drawn at. This accounts for the size of the expand
  // control, icon and offsets.
  int text_offset_;

  std::unique_ptr<PrefixSelector> selector_;

  // The current drawing provider for this TreeView.
  std::unique_ptr<TreeViewDrawingProvider> drawing_provider_;

  base::ScopedObservation<ui::TreeModel, ui::TreeModelObserver>
      tree_model_observation_{this};
};

}  // namespace views

#endif  // UI_VIEWS_CONTROLS_TREE_TREE_VIEW_H_
