blob: 5f6f425d199b85869b84724709472c5ea310fdd9 [file] [log] [blame]
// 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 "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_;
};
} // namespace views
#endif // UI_VIEWS_CONTROLS_TREE_TREE_VIEW_H_