1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/controls/tree/tree_view.h"
9 #include "base/i18n/rtl.h"
10 #include "base/message_loop/message_loop.h"
11 #include "grit/ui_resources.h"
12 #include "ui/base/accessibility/accessible_view_state.h"
13 #include "ui/base/resource/resource_bundle.h"
14 #include "ui/events/event.h"
15 #include "ui/events/keycodes/keyboard_codes.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/gfx/rect_conversions.h"
19 #include "ui/gfx/skia_util.h"
20 #include "ui/native_theme/native_theme.h"
21 #include "ui/views/controls/prefix_selector.h"
22 #include "ui/views/controls/scroll_view.h"
23 #include "ui/views/controls/textfield/textfield.h"
24 #include "ui/views/controls/tree/tree_view_controller.h"
25 #include "ui/views/ime/input_method.h"
28 using ui::TreeModelNode
;
32 // Insets around the view.
33 static const int kHorizontalInset
= 2;
34 static const int kVerticalInset
= 2;
35 // Padding before/after the image.
36 static const int kImagePadding
= 4;
37 // Size of the arrow region.
38 static const int kArrowRegionSize
= 12;
39 // Padding around the text (on each side).
40 static const int kTextVerticalPadding
= 3;
41 static const int kTextHorizontalPadding
= 2;
42 // How much children are indented from their parent.
43 static const int kIndent
= 20;
46 const char TreeView::kViewClassName
[] = "TreeView";
50 // Returns the color id for the background of selected text. |has_focus|
51 // indicates if the tree has focus.
52 ui::NativeTheme::ColorId
text_background_color_id(bool has_focus
) {
54 ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused
:
55 ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused
;
58 // Returns the color id for text. |has_focus| indicates if the tree has focus
59 // and |is_selected| is true if the item is selected.
60 ui::NativeTheme::ColorId
text_color_id(bool has_focus
, bool is_selected
) {
63 return ui::NativeTheme::kColorId_TreeSelectedText
;
64 return ui::NativeTheme::kColorId_TreeSelectedTextUnfocused
;
66 return ui::NativeTheme::kColorId_TreeText
;
77 auto_expand_children_(false),
81 has_custom_icons_(false),
82 row_height_(font_
.GetHeight() + kTextVerticalPadding
* 2) {
84 closed_icon_
= *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
85 (base::i18n::IsRTL() ? IDR_FOLDER_CLOSED_RTL
86 : IDR_FOLDER_CLOSED
)).ToImageSkia();
87 open_icon_
= *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
88 (base::i18n::IsRTL() ? IDR_FOLDER_OPEN_RTL
89 : IDR_FOLDER_OPEN
)).ToImageSkia();
90 text_offset_
= closed_icon_
.width() + kImagePadding
+ kImagePadding
+
94 TreeView::~TreeView() {
96 model_
->RemoveObserver(this);
98 focus_manager_
->RemoveFocusChangeListener(this);
99 focus_manager_
= NULL
;
103 View
* TreeView::CreateParentIfNecessary() {
104 ScrollView
* scroll_view
= ScrollView::CreateScrollViewWithBorder();
105 scroll_view
->SetContents(this);
109 void TreeView::SetModel(TreeModel
* model
) {
113 model_
->RemoveObserver(this);
118 selected_node_
= NULL
;
121 model_
->AddObserver(this);
122 model_
->GetIcons(&icons_
);
125 ConfigureInternalNode(model_
->GetRoot(), &root_
);
126 LoadChildren(&root_
);
127 root_
.set_is_expanded(true);
129 selected_node_
= &root_
;
130 else if (root_
.child_count())
131 selected_node_
= root_
.GetChild(0);
136 void TreeView::SetEditable(bool editable
) {
137 if (editable
== editable_
)
139 editable_
= editable
;
143 void TreeView::StartEditing(TreeModelNode
* node
) {
145 // Cancel the current edit.
147 // Make sure all ancestors are expanded.
148 if (model_
->GetParent(node
))
149 Expand(model_
->GetParent(node
));
150 // Select the node, else if the user commits the edit the selection reverts.
151 SetSelectedNode(node
);
152 if (GetSelectedNode() != node
)
153 return; // Selection failed for some reason, don't start editing.
157 editor_
= new Textfield
;
158 // Add the editor immediately as GetPreferredSize returns the wrong thing if
160 AddChildView(editor_
);
161 editor_
->SetFont(font_
);
162 empty_editor_size_
= editor_
->GetPreferredSize();
163 editor_
->SetController(this);
165 editor_
->SetText(selected_node_
->model_node()->GetTitle());
167 editor_
->SetVisible(true);
168 SchedulePaintForNode(selected_node_
);
169 editor_
->RequestFocus();
170 editor_
->SelectAll(false);
172 // Listen for focus changes so that we can cancel editing.
173 focus_manager_
= GetFocusManager();
175 focus_manager_
->AddFocusChangeListener(this);
177 // Accelerators to commit/cancel edit.
178 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN
, ui::EF_NONE
));
179 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE
, ui::EF_NONE
));
182 void TreeView::CancelEdit() {
186 // WARNING: don't touch |selected_node_|, it may be bogus.
189 if (focus_manager_
) {
190 focus_manager_
->RemoveFocusChangeListener(this);
191 focus_manager_
= NULL
;
193 editor_
->SetVisible(false);
196 RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN
, ui::EF_NONE
));
197 RemoveAccelerator(ui::Accelerator(ui::VKEY_ESCAPE
, ui::EF_NONE
));
200 void TreeView::CommitEdit() {
204 DCHECK(selected_node_
);
205 const bool editor_has_focus
= editor_
->HasFocus();
206 model_
->SetTitle(GetSelectedNode(), editor_
->text());
208 if (editor_has_focus
)
212 TreeModelNode
* TreeView::GetEditingNode() {
213 return editing_
? selected_node_
->model_node() : NULL
;
216 void TreeView::SetSelectedNode(TreeModelNode
* model_node
) {
217 if (editing_
|| model_node
!= selected_node_
)
219 if (model_node
&& model_
->GetParent(model_node
))
220 Expand(model_
->GetParent(model_node
));
221 if (model_node
&& model_node
== root_
.model_node() && !root_shown_
)
222 return; // Ignore requests to select the root when not shown.
223 InternalNode
* node
= model_node
? GetInternalNodeForModelNode(
224 model_node
, CREATE_IF_NOT_LOADED
) : NULL
;
225 bool was_empty_selection
= (selected_node_
== NULL
);
226 bool changed
= (selected_node_
!= node
);
228 SchedulePaintForNode(selected_node_
);
229 selected_node_
= node
;
230 if (selected_node_
== &root_
&& !root_shown_
)
231 selected_node_
= NULL
;
232 if (selected_node_
&& selected_node_
!= &root_
)
233 Expand(model_
->GetParent(selected_node_
->model_node()));
234 SchedulePaintForNode(selected_node_
);
238 ScrollRectToVisible(GetBoundsForNode(selected_node_
));
240 // Notify controller if the old selection was empty to handle the case of
241 // remove explicitly resetting selected_node_ before invoking this.
242 if (controller_
&& (changed
|| was_empty_selection
))
243 controller_
->OnTreeViewSelectionChanged(this);
246 // TODO(dmazzoni): Decide if EVENT_SELECTION_CHANGED is a better choice for
247 // sub-item selection event.
248 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS
, true);
252 TreeModelNode
* TreeView::GetSelectedNode() {
253 return selected_node_
? selected_node_
->model_node() : NULL
;
256 void TreeView::Collapse(ui::TreeModelNode
* model_node
) {
257 // Don't collapse the root if the root isn't shown, otherwise nothing is
259 if (model_node
== root_
.model_node() && !root_shown_
)
262 GetInternalNodeForModelNode(model_node
, DONT_CREATE_IF_NOT_LOADED
);
265 bool was_expanded
= IsExpanded(model_node
);
266 if (node
->is_expanded()) {
267 if (selected_node_
&& selected_node_
->HasAncestor(node
))
268 SetSelectedNode(model_node
);
269 node
->set_is_expanded(false);
275 void TreeView::Expand(TreeModelNode
* node
) {
276 if (ExpandImpl(node
))
278 // TODO: need to support auto_expand_children_.
281 void TreeView::ExpandAll(TreeModelNode
* node
) {
284 bool expanded_at_least_one
= ExpandImpl(node
);
285 // And recursively expand all the children.
286 for (int i
= model_
->GetChildCount(node
) - 1; i
>= 0; --i
) {
287 TreeModelNode
* child
= model_
->GetChild(node
, i
);
288 if (ExpandImpl(child
))
289 expanded_at_least_one
= true;
291 if (expanded_at_least_one
)
295 bool TreeView::IsExpanded(TreeModelNode
* model_node
) {
297 // NULL check primarily for convenience for uses in this class so don't have
298 // to add NULL checks every where we look up the parent.
301 InternalNode
* node
= GetInternalNodeForModelNode(
302 model_node
, DONT_CREATE_IF_NOT_LOADED
);
307 if (!node
->is_expanded())
309 node
= node
->parent();
314 void TreeView::SetRootShown(bool root_shown
) {
315 if (root_shown_
== root_shown
)
317 root_shown_
= root_shown
;
318 if (!root_shown_
&& selected_node_
== &root_
) {
319 if (model_
->GetChildCount(root_
.model_node()))
320 SetSelectedNode(model_
->GetChild(root_
.model_node(), 0));
322 SetSelectedNode(NULL
);
327 ui::TreeModelNode
* TreeView::GetNodeForRow(int row
) {
329 InternalNode
* node
= GetNodeByRow(row
, &depth
);
330 return node
? node
->model_node() : NULL
;
333 int TreeView::GetRowForNode(ui::TreeModelNode
* node
) {
334 InternalNode
* internal_node
=
335 GetInternalNodeForModelNode(node
, DONT_CREATE_IF_NOT_LOADED
);
339 return GetRowForInternalNode(internal_node
, &depth
);
342 void TreeView::Layout() {
343 int width
= preferred_size_
.width();
344 int height
= preferred_size_
.height();
346 width
= std::max(parent()->width(), width
);
347 height
= std::max(parent()->height(), height
);
349 SetBounds(x(), y(), width
, height
);
353 gfx::Size
TreeView::GetPreferredSize() {
354 return preferred_size_
;
357 bool TreeView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
358 if (accelerator
.key_code() == ui::VKEY_RETURN
) {
361 DCHECK_EQ(ui::VKEY_ESCAPE
, accelerator
.key_code());
368 bool TreeView::OnMousePressed(const ui::MouseEvent
& event
) {
369 return OnClickOrTap(event
);
372 ui::TextInputClient
* TreeView::GetTextInputClient() {
374 selector_
.reset(new PrefixSelector(this));
375 return selector_
.get();
378 void TreeView::OnGestureEvent(ui::GestureEvent
* event
) {
379 if (event
->type() == ui::ET_GESTURE_TAP
) {
380 if (OnClickOrTap(*event
))
385 void TreeView::ShowContextMenu(const gfx::Point
& p
,
386 ui::MenuSourceType source_type
) {
389 if (source_type
== ui::MENU_SOURCE_MOUSE
) {
390 // Only invoke View's implementation (which notifies the
391 // ContextMenuController) if over a node.
392 gfx::Point
local_point(p
);
393 ConvertPointFromScreen(this, &local_point
);
394 int row
= (local_point
.y() - kVerticalInset
) / row_height_
;
396 InternalNode
* node
= GetNodeByRow(row
, &depth
);
399 gfx::Rect
bounds(GetBoundsForNodeImpl(node
, row
, depth
));
400 if (!bounds
.Contains(local_point
))
403 View::ShowContextMenu(p
, source_type
);
406 void TreeView::GetAccessibleState(ui::AccessibleViewState
* state
) {
407 state
->role
= ui::AccessibilityTypes::ROLE_OUTLINE
;
408 state
->state
= ui::AccessibilityTypes::STATE_READONLY
;
412 // Get selected item info.
413 state
->role
= ui::AccessibilityTypes::ROLE_OUTLINEITEM
;
414 state
->name
= selected_node_
->model_node()->GetTitle();
417 const char* TreeView::GetClassName() const {
418 return kViewClassName
;
421 void TreeView::TreeNodesAdded(TreeModel
* model
,
422 TreeModelNode
* parent
,
425 InternalNode
* parent_node
=
426 GetInternalNodeForModelNode(parent
, DONT_CREATE_IF_NOT_LOADED
);
427 if (!parent_node
|| !parent_node
->loaded_children())
429 for (int i
= 0; i
< count
; ++i
) {
430 InternalNode
* child
= new InternalNode
;
431 ConfigureInternalNode(model_
->GetChild(parent
, start
+ i
), child
);
432 parent_node
->Add(child
, start
+ i
);
434 if (IsExpanded(parent
))
438 void TreeView::TreeNodesRemoved(TreeModel
* model
,
439 TreeModelNode
* parent
,
442 InternalNode
* parent_node
=
443 GetInternalNodeForModelNode(parent
, DONT_CREATE_IF_NOT_LOADED
);
444 if (!parent_node
|| !parent_node
->loaded_children())
446 bool reset_selection
= false;
447 for (int i
= 0; i
< count
; ++i
) {
448 InternalNode
* child_removing
= parent_node
->GetChild(start
);
449 if (selected_node_
&& selected_node_
->HasAncestor(child_removing
))
450 reset_selection
= true;
451 delete parent_node
->Remove(child_removing
);
453 if (reset_selection
) {
454 // selected_node_ is no longer valid (at the time we enter this function
455 // its model_node() is likely deleted). Explicitly NULL out the field
456 // rather than invoking SetSelectedNode() otherwise, we'll try and use a
458 selected_node_
= NULL
;
459 TreeModelNode
* to_select
= parent
;
460 if (parent
== root_
.model_node() && !root_shown_
) {
461 to_select
= model_
->GetChildCount(parent
) > 0 ?
462 model_
->GetChild(parent
, 0) : NULL
;
464 SetSelectedNode(to_select
);
466 if (IsExpanded(parent
))
470 void TreeView::TreeNodeChanged(TreeModel
* model
, TreeModelNode
* model_node
) {
472 GetInternalNodeForModelNode(model_node
, DONT_CREATE_IF_NOT_LOADED
);
475 int old_width
= node
->text_width();
476 UpdateNodeTextWidth(node
);
477 if (old_width
!= node
->text_width() &&
478 ((node
== &root_
&& root_shown_
) ||
479 (node
!= &root_
&& IsExpanded(node
->parent()->model_node())))) {
484 void TreeView::ContentsChanged(Textfield
* sender
,
485 const base::string16
& new_contents
) {
488 bool TreeView::HandleKeyEvent(Textfield
* sender
,
489 const ui::KeyEvent
& key_event
) {
490 switch (key_event
.key_code()) {
491 case ui::VKEY_RETURN
:
495 case ui::VKEY_ESCAPE
:
505 void TreeView::OnWillChangeFocus(View
* focused_before
, View
* focused_now
) {
508 void TreeView::OnDidChangeFocus(View
* focused_before
, View
* focused_now
) {
512 int TreeView::GetRowCount() {
513 int row_count
= root_
.NumExpandedNodes();
519 int TreeView::GetSelectedRow() {
520 ui::TreeModelNode
* model_node
= GetSelectedNode();
521 return model_node
? GetRowForNode(model_node
) : -1;
524 void TreeView::SetSelectedRow(int row
) {
525 SetSelectedNode(GetNodeForRow(row
));
528 base::string16
TreeView::GetTextForRow(int row
) {
529 return GetNodeForRow(row
)->GetTitle();
532 gfx::Point
TreeView::GetKeyboardContextMenuLocation() {
533 int y
= height() / 2;
534 if (selected_node_
) {
535 gfx::Rect
node_bounds(GetBoundsForNode(selected_node_
));
536 gfx::Rect
vis_bounds(GetVisibleBounds());
537 if (node_bounds
.y() >= vis_bounds
.y() &&
538 node_bounds
.y() < vis_bounds
.bottom()) {
542 gfx::Point
screen_loc(0, y
);
543 if (base::i18n::IsRTL())
544 screen_loc
.set_x(width());
545 ConvertPointToScreen(this, &screen_loc
);
549 bool TreeView::OnKeyPressed(const ui::KeyEvent
& event
) {
553 switch (event
.key_code()) {
556 TreeModelNode
* selected_node
= GetSelectedNode();
557 if (selected_node
&& (!controller_
||
558 controller_
->CanEdit(this, selected_node
))) {
559 StartEditing(selected_node
);
566 IncrementSelection(event
.key_code() == ui::VKEY_UP
?
567 INCREMENT_PREVIOUS
: INCREMENT_NEXT
);
571 if (base::i18n::IsRTL())
572 ExpandOrSelectChild();
574 CollapseOrSelectParent();
578 if (base::i18n::IsRTL())
579 CollapseOrSelectParent();
581 ExpandOrSelectChild();
590 void TreeView::OnPaint(gfx::Canvas
* canvas
) {
591 // Don't invoke View::OnPaint so that we can render our own focus border.
592 canvas
->DrawColor(GetNativeTheme()->GetSystemColor(
593 ui::NativeTheme::kColorId_TreeBackground
));
598 if (canvas
->sk_canvas()->getClipBounds(&sk_clip_rect
)) {
599 // Pixels partially inside the clip rect should be included.
600 gfx::Rect clip_rect
= gfx::ToEnclosingRect(
601 gfx::SkRectToRectF(sk_clip_rect
));
602 min_y
= clip_rect
.y();
603 max_y
= clip_rect
.bottom();
605 gfx::Rect vis_bounds
= GetVisibleBounds();
606 min_y
= vis_bounds
.y();
607 max_y
= vis_bounds
.bottom();
611 int min_row
= std::max(0, (min_y
- kVerticalInset
) / row_height_
);
612 int max_row
= (max_y
- kVerticalInset
) / row_height_
;
613 if ((max_y
- kVerticalInset
) % row_height_
!= 0)
615 int current_row
= root_row();
616 PaintRows(canvas
, min_row
, max_row
, &root_
, root_depth(), ¤t_row
);
619 void TreeView::OnFocus() {
620 GetInputMethod()->OnFocus();
622 SchedulePaintForNode(selected_node_
);
624 // Notify the InputMethod so that it knows to query the TextInputClient.
625 if (GetInputMethod())
626 GetInputMethod()->OnCaretBoundsChanged(this);
629 void TreeView::OnBlur() {
630 GetInputMethod()->OnBlur();
631 SchedulePaintForNode(selected_node_
);
633 selector_
->OnViewBlur();
636 bool TreeView::OnClickOrTap(const ui::LocatedEvent
& event
) {
640 int row
= (event
.y() - kVerticalInset
) / row_height_
;
642 InternalNode
* node
= GetNodeByRow(row
, &depth
);
644 gfx::Rect
bounds(GetBoundsForNodeImpl(node
, row
, depth
));
645 if (bounds
.Contains(event
.location())) {
646 int relative_x
= event
.x() - bounds
.x();
647 if (base::i18n::IsRTL())
648 relative_x
= bounds
.width() - relative_x
;
649 if (relative_x
< kArrowRegionSize
&&
650 model_
->GetChildCount(node
->model_node())) {
651 if (node
->is_expanded())
652 Collapse(node
->model_node());
654 Expand(node
->model_node());
655 } else if (relative_x
> kArrowRegionSize
) {
656 SetSelectedNode(node
->model_node());
657 bool should_toggle
= false;
658 if (event
.type() == ui::ET_GESTURE_TAP
) {
659 const ui::GestureEvent
& gesture
=
660 static_cast<const ui::GestureEvent
&>(event
);
661 should_toggle
= gesture
.details().tap_count() == 2;
663 should_toggle
= (event
.flags() & ui::EF_IS_DOUBLE_CLICK
) != 0;
666 if (node
->is_expanded())
667 Collapse(node
->model_node());
669 Expand(node
->model_node());
677 void TreeView::LoadChildren(InternalNode
* node
) {
678 DCHECK_EQ(0, node
->child_count());
679 DCHECK(!node
->loaded_children());
680 node
->set_loaded_children(true);
681 for (int i
= 0, child_count
= model_
->GetChildCount(node
->model_node());
682 i
< child_count
; ++i
) {
683 InternalNode
* child
= new InternalNode
;
684 ConfigureInternalNode(model_
->GetChild(node
->model_node(), i
), child
);
685 node
->Add(child
, node
->child_count());
689 void TreeView::ConfigureInternalNode(TreeModelNode
* model_node
,
690 InternalNode
* node
) {
691 node
->Reset(model_node
);
692 UpdateNodeTextWidth(node
);
695 void TreeView::UpdateNodeTextWidth(InternalNode
* node
) {
696 int width
= 0, height
= 0;
697 gfx::Canvas::SizeStringInt(node
->model_node()->GetTitle(), font_
,
698 &width
, &height
, 0, gfx::Canvas::NO_ELLIPSIS
);
699 node
->set_text_width(width
);
702 void TreeView::DrawnNodesChanged() {
703 UpdatePreferredSize();
704 PreferredSizeChanged();
708 void TreeView::UpdatePreferredSize() {
709 preferred_size_
= gfx::Size();
713 preferred_size_
.SetSize(
714 root_
.GetMaxWidth(text_offset_
, root_shown_
? 1 : 0) +
715 kTextHorizontalPadding
* 2,
716 row_height_
* GetRowCount() + kVerticalInset
* 2);
719 void TreeView::LayoutEditor() {
723 DCHECK(selected_node_
);
724 // Position the editor so that its text aligns with the text we drew.
725 gfx::Rect row_bounds
= GetBoundsForNode(selected_node_
);
727 GetMirroredXWithWidthInView(row_bounds
.x(), row_bounds
.width()));
728 row_bounds
.set_x(row_bounds
.x() + text_offset_
);
729 row_bounds
.set_width(row_bounds
.width() - text_offset_
);
730 row_bounds
.Inset(kTextHorizontalPadding
, kTextVerticalPadding
);
731 row_bounds
.Inset(-empty_editor_size_
.width() / 2,
732 -(empty_editor_size_
.height() - font_
.GetHeight()) / 2);
733 // Give a little extra space for editing.
734 row_bounds
.set_width(row_bounds
.width() + 50);
735 editor_
->SetBoundsRect(row_bounds
);
739 void TreeView::SchedulePaintForNode(InternalNode
* node
) {
741 return; // Explicitly allow NULL to be passed in.
742 SchedulePaintInRect(GetBoundsForNode(node
));
745 void TreeView::PaintRows(gfx::Canvas
* canvas
,
754 if (*row
>= min_row
&& *row
< max_row
)
755 PaintRow(canvas
, node
, *row
, depth
);
757 if (!node
->is_expanded())
760 for (int i
= 0; i
< node
->child_count() && *row
< max_row
; ++i
)
761 PaintRows(canvas
, min_row
, max_row
, node
->GetChild(i
), depth
, row
);
764 void TreeView::PaintRow(gfx::Canvas
* canvas
,
768 gfx::Rect
bounds(GetBoundsForNodeImpl(node
, row
, depth
));
770 if (model_
->GetChildCount(node
->model_node()))
771 PaintExpandControl(canvas
, bounds
, node
->is_expanded());
775 int icon_index
= model_
->GetIconIndex(node
->model_node());
776 if (icon_index
!= -1)
777 icon
= icons_
[icon_index
];
778 else if (node
== selected_node_
)
782 int icon_x
= kArrowRegionSize
+ kImagePadding
+
783 (open_icon_
.width() - icon
.width()) / 2;
784 if (base::i18n::IsRTL())
785 icon_x
= bounds
.right() - icon_x
- open_icon_
.width();
787 icon_x
+= bounds
.x();
788 canvas
->DrawImageInt(
790 bounds
.y() + (bounds
.height() - icon
.height()) / 2);
792 if (!editing_
|| node
!= selected_node_
) {
793 gfx::Rect
text_bounds(bounds
.x() + text_offset_
, bounds
.y(),
794 bounds
.width() - text_offset_
, bounds
.height());
795 if (base::i18n::IsRTL())
796 text_bounds
.set_x(bounds
.x());
797 if (node
== selected_node_
) {
798 const SkColor bg_color
= GetNativeTheme()->GetSystemColor(
799 text_background_color_id(HasFocus()));
800 canvas
->FillRect(text_bounds
, bg_color
);
802 canvas
->DrawFocusRect(text_bounds
);
804 const ui::NativeTheme::ColorId color_id
=
805 text_color_id(HasFocus(), node
== selected_node_
);
806 canvas
->DrawStringInt(node
->model_node()->GetTitle(), font_
,
807 GetNativeTheme()->GetSystemColor(color_id
),
808 text_bounds
.x() + kTextHorizontalPadding
,
809 text_bounds
.y() + kTextVerticalPadding
,
810 text_bounds
.width() - kTextHorizontalPadding
* 2,
811 text_bounds
.height() - kTextVerticalPadding
* 2);
815 void TreeView::PaintExpandControl(gfx::Canvas
* canvas
,
816 const gfx::Rect
& node_bounds
,
819 if (base::i18n::IsRTL()) {
820 center_x
= node_bounds
.right() - kArrowRegionSize
+
821 (kArrowRegionSize
- 4) / 2;
823 center_x
= node_bounds
.x() + (kArrowRegionSize
- 4) / 2;
825 int center_y
= node_bounds
.y() + node_bounds
.height() / 2;
826 const SkColor arrow_color
= GetNativeTheme()->GetSystemColor(
827 ui::NativeTheme::kColorId_TreeArrow
);
828 // TODO: this should come from an image.
830 int delta
= base::i18n::IsRTL() ? 1 : -1;
831 for (int i
= 0; i
< 4; ++i
) {
832 canvas
->FillRect(gfx::Rect(center_x
+ delta
* (2 - i
),
833 center_y
- (3 - i
), 1, (3 - i
) * 2 + 1),
838 for (int i
= 0; i
< 4; ++i
) {
839 canvas
->FillRect(gfx::Rect(center_x
- (3 - i
), center_y
+ i
,
840 (3 - i
) * 2 + 1, 1), arrow_color
);
845 TreeView::InternalNode
* TreeView::GetInternalNodeForModelNode(
846 ui::TreeModelNode
* model_node
,
847 GetInternalNodeCreateType create_type
) {
848 if (model_node
== root_
.model_node())
850 InternalNode
* parent_internal_node
=
851 GetInternalNodeForModelNode(model_
->GetParent(model_node
), create_type
);
852 if (!parent_internal_node
)
854 if (!parent_internal_node
->loaded_children()) {
855 if (create_type
== DONT_CREATE_IF_NOT_LOADED
)
857 LoadChildren(parent_internal_node
);
859 return parent_internal_node
->GetChild(
860 model_
->GetIndexOf(parent_internal_node
->model_node(), model_node
));
863 gfx::Rect
TreeView::GetBoundsForNode(InternalNode
* node
) {
865 row
= GetRowForInternalNode(node
, &depth
);
866 return GetBoundsForNodeImpl(node
, row
, depth
);
869 gfx::Rect
TreeView::GetBoundsForNodeImpl(InternalNode
* node
,
872 gfx::Rect
rect(depth
* kIndent
+ kHorizontalInset
,
873 row
* row_height_
+ kVerticalInset
,
874 text_offset_
+ node
->text_width() +
875 kTextHorizontalPadding
* 2,
877 rect
.set_x(GetMirroredXWithWidthInView(rect
.x(), rect
.width()));
881 int TreeView::GetRowForInternalNode(InternalNode
* node
, int* depth
) {
882 DCHECK(!node
->parent() || IsExpanded(node
->parent()->model_node()));
885 InternalNode
* tmp_node
= node
;
886 while (tmp_node
->parent()) {
887 int index_in_parent
= tmp_node
->parent()->GetIndexOf(tmp_node
);
890 for (int i
= 0; i
< index_in_parent
; ++i
)
891 row
+= tmp_node
->parent()->GetChild(i
)->NumExpandedNodes();
892 tmp_node
= tmp_node
->parent();
901 TreeView::InternalNode
* TreeView::GetNodeByRow(int row
, int* depth
) {
902 int current_row
= root_row();
904 return GetNodeByRowImpl(&root_
, row
, root_depth(), ¤t_row
, depth
);
907 TreeView::InternalNode
* TreeView::GetNodeByRowImpl(InternalNode
* node
,
912 if (*current_row
== target_row
) {
913 *node_depth
= current_depth
;
917 if (node
->is_expanded()) {
919 for (int i
= 0; i
< node
->child_count(); ++i
) {
920 InternalNode
* result
= GetNodeByRowImpl(
921 node
->GetChild(i
), target_row
, current_depth
, current_row
,
930 void TreeView::IncrementSelection(IncrementType type
) {
934 if (!GetSelectedNode()) {
935 // If nothing is selected select the first or last node.
936 if (!root_
.child_count())
938 if (type
== INCREMENT_PREVIOUS
) {
939 int row_count
= GetRowCount();
942 InternalNode
* node
= GetNodeByRow(row_count
- 1, &depth
);
943 SetSelectedNode(node
->model_node());
944 } else if (root_shown_
) {
945 SetSelectedNode(root_
.model_node());
947 SetSelectedNode(root_
.GetChild(0)->model_node());
953 int delta
= type
== INCREMENT_PREVIOUS
? -1 : 1;
954 int row
= GetRowForInternalNode(selected_node_
, &depth
);
955 int new_row
= std::min(GetRowCount() - 1, std::max(0, row
+ delta
));
957 return; // At the end/beginning.
958 SetSelectedNode(GetNodeByRow(new_row
, &depth
)->model_node());
961 void TreeView::CollapseOrSelectParent() {
962 if (selected_node_
) {
963 if (selected_node_
->is_expanded())
964 Collapse(selected_node_
->model_node());
965 else if (selected_node_
->parent())
966 SetSelectedNode(selected_node_
->parent()->model_node());
970 void TreeView::ExpandOrSelectChild() {
971 if (selected_node_
) {
972 if (!selected_node_
->is_expanded())
973 Expand(selected_node_
->model_node());
974 else if (selected_node_
->child_count())
975 SetSelectedNode(selected_node_
->GetChild(0)->model_node());
979 bool TreeView::ExpandImpl(TreeModelNode
* model_node
) {
980 TreeModelNode
* parent
= model_
->GetParent(model_node
);
982 // Node should be the root.
983 DCHECK_EQ(root_
.model_node(), model_node
);
984 bool was_expanded
= root_
.is_expanded();
985 root_
.set_is_expanded(true);
986 return !was_expanded
;
989 // Expand all the parents.
990 bool return_value
= ExpandImpl(parent
);
991 InternalNode
* internal_node
=
992 GetInternalNodeForModelNode(model_node
, CREATE_IF_NOT_LOADED
);
993 DCHECK(internal_node
);
994 if (!internal_node
->is_expanded()) {
995 if (!internal_node
->loaded_children())
996 LoadChildren(internal_node
);
997 internal_node
->set_is_expanded(true);
1000 return return_value
;
1003 // InternalNode ----------------------------------------------------------------
1005 TreeView::InternalNode::InternalNode()
1006 : model_node_(NULL
),
1007 loaded_children_(false),
1008 is_expanded_(false),
1012 TreeView::InternalNode::~InternalNode() {
1015 void TreeView::InternalNode::Reset(ui::TreeModelNode
* node
) {
1017 loaded_children_
= false;
1018 is_expanded_
= false;
1022 int TreeView::InternalNode::NumExpandedNodes() const {
1023 int result
= 1; // For this.
1026 for (int i
= 0; i
< child_count(); ++i
)
1027 result
+= GetChild(i
)->NumExpandedNodes();
1031 int TreeView::InternalNode::GetMaxWidth(int indent
, int depth
) {
1032 int max_width
= text_width_
+ indent
* depth
;
1035 for (int i
= 0; i
< child_count(); ++i
) {
1036 max_width
= std::max(max_width
,
1037 GetChild(i
)->GetMaxWidth(indent
, depth
+ 1));
1042 } // namespace views