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 "ui/accessibility/ax_view_state.h"
12 #include "ui/base/ime/input_method.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/geometry/rect.h"
18 #include "ui/gfx/geometry/rect_conversions.h"
19 #include "ui/gfx/image/image.h"
20 #include "ui/gfx/skia_util.h"
21 #include "ui/native_theme/native_theme.h"
22 #include "ui/resources/grit/ui_resources.h"
23 #include "ui/views/controls/prefix_selector.h"
24 #include "ui/views/controls/scroll_view.h"
25 #include "ui/views/controls/textfield/textfield.h"
26 #include "ui/views/controls/tree/tree_view_controller.h"
27 #include "ui/views/resources/grit/views_resources.h"
30 using ui::TreeModelNode
;
34 // Insets around the view.
35 static const int kHorizontalInset
= 2;
36 static const int kVerticalInset
= 2;
37 // Padding before/after the image.
38 static const int kImagePadding
= 4;
39 // Size of the arrow region.
40 static const int kArrowRegionSize
= 12;
41 // Padding around the text (on each side).
42 static const int kTextVerticalPadding
= 3;
43 static const int kTextHorizontalPadding
= 2;
44 // How much children are indented from their parent.
45 static const int kIndent
= 20;
48 const char TreeView::kViewClassName
[] = "TreeView";
52 // Returns the color id for the background of selected text. |has_focus|
53 // indicates if the tree has focus.
54 ui::NativeTheme::ColorId
text_background_color_id(bool has_focus
) {
56 ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused
:
57 ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused
;
60 // Returns the color id for text. |has_focus| indicates if the tree has focus
61 // and |is_selected| is true if the item is selected.
62 ui::NativeTheme::ColorId
text_color_id(bool has_focus
, bool is_selected
) {
65 return ui::NativeTheme::kColorId_TreeSelectedText
;
66 return ui::NativeTheme::kColorId_TreeSelectedTextUnfocused
;
68 return ui::NativeTheme::kColorId_TreeText
;
79 auto_expand_children_(false),
83 row_height_(font_list_
.GetHeight() + kTextVerticalPadding
* 2) {
85 closed_icon_
= *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
86 (base::i18n::IsRTL() ? IDR_FOLDER_CLOSED_RTL
87 : IDR_FOLDER_CLOSED
)).ToImageSkia();
88 open_icon_
= *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
89 (base::i18n::IsRTL() ? IDR_FOLDER_OPEN_RTL
90 : IDR_FOLDER_OPEN
)).ToImageSkia();
91 text_offset_
= closed_icon_
.width() + kImagePadding
+ kImagePadding
+
95 TreeView::~TreeView() {
97 model_
->RemoveObserver(this);
99 if (GetInputMethod() && selector_
.get()) {
100 // TreeView should have been blurred before destroy.
101 DCHECK(selector_
.get() != GetInputMethod()->GetTextInputClient());
104 if (focus_manager_
) {
105 focus_manager_
->RemoveFocusChangeListener(this);
106 focus_manager_
= NULL
;
110 View
* TreeView::CreateParentIfNecessary() {
111 ScrollView
* scroll_view
= ScrollView::CreateScrollViewWithBorder();
112 scroll_view
->SetContents(this);
116 void TreeView::SetModel(TreeModel
* model
) {
120 model_
->RemoveObserver(this);
125 selected_node_
= NULL
;
128 model_
->AddObserver(this);
129 model_
->GetIcons(&icons_
);
132 ConfigureInternalNode(model_
->GetRoot(), &root_
);
133 LoadChildren(&root_
);
134 root_
.set_is_expanded(true);
136 selected_node_
= &root_
;
137 else if (root_
.child_count())
138 selected_node_
= root_
.GetChild(0);
143 void TreeView::SetEditable(bool editable
) {
144 if (editable
== editable_
)
146 editable_
= editable
;
150 void TreeView::StartEditing(TreeModelNode
* node
) {
152 // Cancel the current edit.
154 // Make sure all ancestors are expanded.
155 if (model_
->GetParent(node
))
156 Expand(model_
->GetParent(node
));
157 // Select the node, else if the user commits the edit the selection reverts.
158 SetSelectedNode(node
);
159 if (GetSelectedNode() != node
)
160 return; // Selection failed for some reason, don't start editing.
164 editor_
= new Textfield
;
165 // Add the editor immediately as GetPreferredSize returns the wrong thing if
167 AddChildView(editor_
);
168 editor_
->SetFontList(font_list_
);
169 empty_editor_size_
= editor_
->GetPreferredSize();
170 editor_
->set_controller(this);
172 editor_
->SetText(selected_node_
->model_node()->GetTitle());
174 editor_
->SetVisible(true);
175 SchedulePaintForNode(selected_node_
);
176 editor_
->RequestFocus();
177 editor_
->SelectAll(false);
179 // Listen for focus changes so that we can cancel editing.
180 focus_manager_
= GetFocusManager();
182 focus_manager_
->AddFocusChangeListener(this);
184 // Accelerators to commit/cancel edit.
185 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN
, ui::EF_NONE
));
186 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE
, ui::EF_NONE
));
189 void TreeView::CancelEdit() {
193 // WARNING: don't touch |selected_node_|, it may be bogus.
196 if (focus_manager_
) {
197 focus_manager_
->RemoveFocusChangeListener(this);
198 focus_manager_
= NULL
;
200 editor_
->SetVisible(false);
203 RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN
, ui::EF_NONE
));
204 RemoveAccelerator(ui::Accelerator(ui::VKEY_ESCAPE
, ui::EF_NONE
));
207 void TreeView::CommitEdit() {
211 DCHECK(selected_node_
);
212 const bool editor_has_focus
= editor_
->HasFocus();
213 model_
->SetTitle(GetSelectedNode(), editor_
->text());
215 if (editor_has_focus
)
219 TreeModelNode
* TreeView::GetEditingNode() {
220 return editing_
? selected_node_
->model_node() : NULL
;
223 void TreeView::SetSelectedNode(TreeModelNode
* model_node
) {
224 if (editing_
|| model_node
!= selected_node_
)
226 if (model_node
&& model_
->GetParent(model_node
))
227 Expand(model_
->GetParent(model_node
));
228 if (model_node
&& model_node
== root_
.model_node() && !root_shown_
)
229 return; // Ignore requests to select the root when not shown.
230 InternalNode
* node
= model_node
? GetInternalNodeForModelNode(
231 model_node
, CREATE_IF_NOT_LOADED
) : NULL
;
232 bool was_empty_selection
= (selected_node_
== NULL
);
233 bool changed
= (selected_node_
!= node
);
235 SchedulePaintForNode(selected_node_
);
236 selected_node_
= node
;
237 if (selected_node_
== &root_
&& !root_shown_
)
238 selected_node_
= NULL
;
239 if (selected_node_
&& selected_node_
!= &root_
)
240 Expand(model_
->GetParent(selected_node_
->model_node()));
241 SchedulePaintForNode(selected_node_
);
245 ScrollRectToVisible(GetBoundsForNode(selected_node_
));
247 // Notify controller if the old selection was empty to handle the case of
248 // remove explicitly resetting selected_node_ before invoking this.
249 if (controller_
&& (changed
|| was_empty_selection
))
250 controller_
->OnTreeViewSelectionChanged(this);
253 // TODO(dmazzoni): Decide if EVENT_SELECTION_CHANGED is a better choice for
254 // sub-item selection event.
255 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS
, true);
259 TreeModelNode
* TreeView::GetSelectedNode() {
260 return selected_node_
? selected_node_
->model_node() : NULL
;
263 void TreeView::Collapse(ui::TreeModelNode
* model_node
) {
264 // Don't collapse the root if the root isn't shown, otherwise nothing is
266 if (model_node
== root_
.model_node() && !root_shown_
)
269 GetInternalNodeForModelNode(model_node
, DONT_CREATE_IF_NOT_LOADED
);
272 bool was_expanded
= IsExpanded(model_node
);
273 if (node
->is_expanded()) {
274 if (selected_node_
&& selected_node_
->HasAncestor(node
))
275 SetSelectedNode(model_node
);
276 node
->set_is_expanded(false);
282 void TreeView::Expand(TreeModelNode
* node
) {
283 if (ExpandImpl(node
))
285 // TODO: need to support auto_expand_children_.
288 void TreeView::ExpandAll(TreeModelNode
* node
) {
291 bool expanded_at_least_one
= ExpandImpl(node
);
292 // And recursively expand all the children.
293 for (int i
= model_
->GetChildCount(node
) - 1; i
>= 0; --i
) {
294 TreeModelNode
* child
= model_
->GetChild(node
, i
);
295 if (ExpandImpl(child
))
296 expanded_at_least_one
= true;
298 if (expanded_at_least_one
)
302 bool TreeView::IsExpanded(TreeModelNode
* model_node
) {
304 // NULL check primarily for convenience for uses in this class so don't have
305 // to add NULL checks every where we look up the parent.
308 InternalNode
* node
= GetInternalNodeForModelNode(
309 model_node
, DONT_CREATE_IF_NOT_LOADED
);
314 if (!node
->is_expanded())
316 node
= node
->parent();
321 void TreeView::SetRootShown(bool root_shown
) {
322 if (root_shown_
== root_shown
)
324 root_shown_
= root_shown
;
325 if (!root_shown_
&& selected_node_
== &root_
) {
326 if (model_
->GetChildCount(root_
.model_node()))
327 SetSelectedNode(model_
->GetChild(root_
.model_node(), 0));
329 SetSelectedNode(NULL
);
334 ui::TreeModelNode
* TreeView::GetNodeForRow(int row
) {
336 InternalNode
* node
= GetNodeByRow(row
, &depth
);
337 return node
? node
->model_node() : NULL
;
340 int TreeView::GetRowForNode(ui::TreeModelNode
* node
) {
341 InternalNode
* internal_node
=
342 GetInternalNodeForModelNode(node
, DONT_CREATE_IF_NOT_LOADED
);
346 return GetRowForInternalNode(internal_node
, &depth
);
349 void TreeView::Layout() {
350 int width
= preferred_size_
.width();
351 int height
= preferred_size_
.height();
353 width
= std::max(parent()->width(), width
);
354 height
= std::max(parent()->height(), height
);
356 SetBounds(x(), y(), width
, height
);
360 gfx::Size
TreeView::GetPreferredSize() const {
361 return preferred_size_
;
364 bool TreeView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
365 if (accelerator
.key_code() == ui::VKEY_RETURN
) {
368 DCHECK_EQ(ui::VKEY_ESCAPE
, accelerator
.key_code());
375 bool TreeView::OnMousePressed(const ui::MouseEvent
& event
) {
376 return OnClickOrTap(event
);
379 void TreeView::OnGestureEvent(ui::GestureEvent
* event
) {
380 if (event
->type() == ui::ET_GESTURE_TAP
) {
381 if (OnClickOrTap(*event
))
386 void TreeView::ShowContextMenu(const gfx::Point
& p
,
387 ui::MenuSourceType source_type
) {
390 if (source_type
== ui::MENU_SOURCE_MOUSE
) {
391 // Only invoke View's implementation (which notifies the
392 // ContextMenuController) if over a node.
393 gfx::Point
local_point(p
);
394 ConvertPointFromScreen(this, &local_point
);
395 int row
= (local_point
.y() - kVerticalInset
) / row_height_
;
397 InternalNode
* node
= GetNodeByRow(row
, &depth
);
400 gfx::Rect
bounds(GetBoundsForNodeImpl(node
, row
, depth
));
401 if (!bounds
.Contains(local_point
))
404 View::ShowContextMenu(p
, source_type
);
407 void TreeView::GetAccessibleState(ui::AXViewState
* state
) {
408 state
->role
= ui::AX_ROLE_TREE
;
409 state
->AddStateFlag(ui::AX_STATE_READ_ONLY
);
413 // Get selected item info.
414 state
->role
= ui::AX_ROLE_TREE_ITEM
;
415 state
->name
= selected_node_
->model_node()->GetTitle();
418 const char* TreeView::GetClassName() const {
419 return kViewClassName
;
422 void TreeView::TreeNodesAdded(TreeModel
* model
,
423 TreeModelNode
* parent
,
426 InternalNode
* parent_node
=
427 GetInternalNodeForModelNode(parent
, DONT_CREATE_IF_NOT_LOADED
);
428 if (!parent_node
|| !parent_node
->loaded_children())
430 for (int i
= 0; i
< count
; ++i
) {
431 InternalNode
* child
= new InternalNode
;
432 ConfigureInternalNode(model_
->GetChild(parent
, start
+ i
), child
);
433 parent_node
->Add(child
, start
+ i
);
435 if (IsExpanded(parent
))
439 void TreeView::TreeNodesRemoved(TreeModel
* model
,
440 TreeModelNode
* parent
,
443 InternalNode
* parent_node
=
444 GetInternalNodeForModelNode(parent
, DONT_CREATE_IF_NOT_LOADED
);
445 if (!parent_node
|| !parent_node
->loaded_children())
447 bool reset_selection
= false;
448 for (int i
= 0; i
< count
; ++i
) {
449 InternalNode
* child_removing
= parent_node
->GetChild(start
);
450 if (selected_node_
&& selected_node_
->HasAncestor(child_removing
))
451 reset_selection
= true;
452 delete parent_node
->Remove(child_removing
);
454 if (reset_selection
) {
455 // selected_node_ is no longer valid (at the time we enter this function
456 // its model_node() is likely deleted). Explicitly NULL out the field
457 // rather than invoking SetSelectedNode() otherwise, we'll try and use a
459 selected_node_
= NULL
;
460 TreeModelNode
* to_select
= parent
;
461 if (parent
== root_
.model_node() && !root_shown_
) {
462 to_select
= model_
->GetChildCount(parent
) > 0 ?
463 model_
->GetChild(parent
, 0) : NULL
;
465 SetSelectedNode(to_select
);
467 if (IsExpanded(parent
))
471 void TreeView::TreeNodeChanged(TreeModel
* model
, TreeModelNode
* model_node
) {
473 GetInternalNodeForModelNode(model_node
, DONT_CREATE_IF_NOT_LOADED
);
476 int old_width
= node
->text_width();
477 UpdateNodeTextWidth(node
);
478 if (old_width
!= node
->text_width() &&
479 ((node
== &root_
&& root_shown_
) ||
480 (node
!= &root_
&& IsExpanded(node
->parent()->model_node())))) {
485 void TreeView::ContentsChanged(Textfield
* sender
,
486 const base::string16
& new_contents
) {
489 bool TreeView::HandleKeyEvent(Textfield
* sender
,
490 const ui::KeyEvent
& key_event
) {
491 switch (key_event
.key_code()) {
492 case ui::VKEY_RETURN
:
496 case ui::VKEY_ESCAPE
:
506 void TreeView::OnWillChangeFocus(View
* focused_before
, View
* focused_now
) {
509 void TreeView::OnDidChangeFocus(View
* focused_before
, View
* focused_now
) {
513 int TreeView::GetRowCount() {
514 int row_count
= root_
.NumExpandedNodes();
520 int TreeView::GetSelectedRow() {
521 ui::TreeModelNode
* model_node
= GetSelectedNode();
522 return model_node
? GetRowForNode(model_node
) : -1;
525 void TreeView::SetSelectedRow(int row
) {
526 SetSelectedNode(GetNodeForRow(row
));
529 base::string16
TreeView::GetTextForRow(int row
) {
530 return GetNodeForRow(row
)->GetTitle();
533 gfx::Point
TreeView::GetKeyboardContextMenuLocation() {
534 int y
= height() / 2;
535 if (selected_node_
) {
536 gfx::Rect
node_bounds(GetBoundsForNode(selected_node_
));
537 gfx::Rect
vis_bounds(GetVisibleBounds());
538 if (node_bounds
.y() >= vis_bounds
.y() &&
539 node_bounds
.y() < vis_bounds
.bottom()) {
543 gfx::Point
screen_loc(0, y
);
544 if (base::i18n::IsRTL())
545 screen_loc
.set_x(width());
546 ConvertPointToScreen(this, &screen_loc
);
550 bool TreeView::OnKeyPressed(const ui::KeyEvent
& event
) {
554 switch (event
.key_code()) {
557 TreeModelNode
* selected_node
= GetSelectedNode();
558 if (selected_node
&& (!controller_
||
559 controller_
->CanEdit(this, selected_node
))) {
560 StartEditing(selected_node
);
567 IncrementSelection(event
.key_code() == ui::VKEY_UP
?
568 INCREMENT_PREVIOUS
: INCREMENT_NEXT
);
572 if (base::i18n::IsRTL())
573 ExpandOrSelectChild();
575 CollapseOrSelectParent();
579 if (base::i18n::IsRTL())
580 CollapseOrSelectParent();
582 ExpandOrSelectChild();
591 void TreeView::OnPaint(gfx::Canvas
* canvas
) {
592 // Don't invoke View::OnPaint so that we can render our own focus border.
593 canvas
->DrawColor(GetNativeTheme()->GetSystemColor(
594 ui::NativeTheme::kColorId_TreeBackground
));
599 if (canvas
->sk_canvas()->getClipBounds(&sk_clip_rect
)) {
600 // Pixels partially inside the clip rect should be included.
601 gfx::Rect clip_rect
= gfx::ToEnclosingRect(
602 gfx::SkRectToRectF(sk_clip_rect
));
603 min_y
= clip_rect
.y();
604 max_y
= clip_rect
.bottom();
606 gfx::Rect vis_bounds
= GetVisibleBounds();
607 min_y
= vis_bounds
.y();
608 max_y
= vis_bounds
.bottom();
612 int min_row
= std::max(0, (min_y
- kVerticalInset
) / row_height_
);
613 int max_row
= (max_y
- kVerticalInset
) / row_height_
;
614 if ((max_y
- kVerticalInset
) % row_height_
!= 0)
616 int current_row
= root_row();
617 PaintRows(canvas
, min_row
, max_row
, &root_
, root_depth(), ¤t_row
);
620 void TreeView::OnFocus() {
621 if (GetInputMethod())
622 GetInputMethod()->SetFocusedTextInputClient(GetPrefixSelector());
624 SchedulePaintForNode(selected_node_
);
626 // Notify the InputMethod so that it knows to query the TextInputClient.
627 if (GetInputMethod())
628 GetInputMethod()->OnCaretBoundsChanged(GetPrefixSelector());
631 void TreeView::OnBlur() {
632 if (GetInputMethod())
633 GetInputMethod()->DetachTextInputClient(GetPrefixSelector());
634 SchedulePaintForNode(selected_node_
);
636 selector_
->OnViewBlur();
639 bool TreeView::OnClickOrTap(const ui::LocatedEvent
& event
) {
643 int row
= (event
.y() - kVerticalInset
) / row_height_
;
645 InternalNode
* node
= GetNodeByRow(row
, &depth
);
647 gfx::Rect
bounds(GetBoundsForNodeImpl(node
, row
, depth
));
648 if (bounds
.Contains(event
.location())) {
649 int relative_x
= event
.x() - bounds
.x();
650 if (base::i18n::IsRTL())
651 relative_x
= bounds
.width() - relative_x
;
652 if (relative_x
< kArrowRegionSize
&&
653 model_
->GetChildCount(node
->model_node())) {
654 if (node
->is_expanded())
655 Collapse(node
->model_node());
657 Expand(node
->model_node());
658 } else if (relative_x
> kArrowRegionSize
) {
659 SetSelectedNode(node
->model_node());
660 bool should_toggle
= false;
661 if (event
.type() == ui::ET_GESTURE_TAP
) {
662 const ui::GestureEvent
& gesture
=
663 static_cast<const ui::GestureEvent
&>(event
);
664 should_toggle
= gesture
.details().tap_count() == 2;
666 should_toggle
= (event
.flags() & ui::EF_IS_DOUBLE_CLICK
) != 0;
669 if (node
->is_expanded())
670 Collapse(node
->model_node());
672 Expand(node
->model_node());
680 void TreeView::LoadChildren(InternalNode
* node
) {
681 DCHECK_EQ(0, node
->child_count());
682 DCHECK(!node
->loaded_children());
683 node
->set_loaded_children(true);
684 for (int i
= 0, child_count
= model_
->GetChildCount(node
->model_node());
685 i
< child_count
; ++i
) {
686 InternalNode
* child
= new InternalNode
;
687 ConfigureInternalNode(model_
->GetChild(node
->model_node(), i
), child
);
688 node
->Add(child
, node
->child_count());
692 void TreeView::ConfigureInternalNode(TreeModelNode
* model_node
,
693 InternalNode
* node
) {
694 node
->Reset(model_node
);
695 UpdateNodeTextWidth(node
);
698 void TreeView::UpdateNodeTextWidth(InternalNode
* node
) {
699 int width
= 0, height
= 0;
700 gfx::Canvas::SizeStringInt(node
->model_node()->GetTitle(), font_list_
,
701 &width
, &height
, 0, gfx::Canvas::NO_ELLIPSIS
);
702 node
->set_text_width(width
);
705 void TreeView::DrawnNodesChanged() {
706 UpdatePreferredSize();
707 PreferredSizeChanged();
711 void TreeView::UpdatePreferredSize() {
712 preferred_size_
= gfx::Size();
716 preferred_size_
.SetSize(
717 root_
.GetMaxWidth(text_offset_
, root_shown_
? 1 : 0) +
718 kTextHorizontalPadding
* 2,
719 row_height_
* GetRowCount() + kVerticalInset
* 2);
722 void TreeView::LayoutEditor() {
726 DCHECK(selected_node_
);
727 // Position the editor so that its text aligns with the text we drew.
728 gfx::Rect row_bounds
= GetBoundsForNode(selected_node_
);
730 GetMirroredXWithWidthInView(row_bounds
.x(), row_bounds
.width()));
731 row_bounds
.set_x(row_bounds
.x() + text_offset_
);
732 row_bounds
.set_width(row_bounds
.width() - text_offset_
);
733 row_bounds
.Inset(kTextHorizontalPadding
, kTextVerticalPadding
);
734 row_bounds
.Inset(-empty_editor_size_
.width() / 2,
735 -(empty_editor_size_
.height() - font_list_
.GetHeight()) / 2);
736 // Give a little extra space for editing.
737 row_bounds
.set_width(row_bounds
.width() + 50);
738 editor_
->SetBoundsRect(row_bounds
);
742 void TreeView::SchedulePaintForNode(InternalNode
* node
) {
744 return; // Explicitly allow NULL to be passed in.
745 SchedulePaintInRect(GetBoundsForNode(node
));
748 void TreeView::PaintRows(gfx::Canvas
* canvas
,
757 if (*row
>= min_row
&& *row
< max_row
)
758 PaintRow(canvas
, node
, *row
, depth
);
760 if (!node
->is_expanded())
763 for (int i
= 0; i
< node
->child_count() && *row
< max_row
; ++i
)
764 PaintRows(canvas
, min_row
, max_row
, node
->GetChild(i
), depth
, row
);
767 void TreeView::PaintRow(gfx::Canvas
* canvas
,
771 gfx::Rect
bounds(GetBoundsForNodeImpl(node
, row
, depth
));
773 if (model_
->GetChildCount(node
->model_node()))
774 PaintExpandControl(canvas
, bounds
, node
->is_expanded());
778 int icon_index
= model_
->GetIconIndex(node
->model_node());
779 if (icon_index
!= -1)
780 icon
= icons_
[icon_index
];
781 else if (node
== selected_node_
)
785 int icon_x
= kArrowRegionSize
+ kImagePadding
+
786 (open_icon_
.width() - icon
.width()) / 2;
787 if (base::i18n::IsRTL())
788 icon_x
= bounds
.right() - icon_x
- open_icon_
.width();
790 icon_x
+= bounds
.x();
791 canvas
->DrawImageInt(
793 bounds
.y() + (bounds
.height() - icon
.height()) / 2);
795 if (!editing_
|| node
!= selected_node_
) {
796 gfx::Rect
text_bounds(bounds
.x() + text_offset_
, bounds
.y(),
797 bounds
.width() - text_offset_
, bounds
.height());
798 if (base::i18n::IsRTL())
799 text_bounds
.set_x(bounds
.x());
800 if (node
== selected_node_
) {
801 const SkColor bg_color
= GetNativeTheme()->GetSystemColor(
802 text_background_color_id(HasFocus()));
803 canvas
->FillRect(text_bounds
, bg_color
);
805 canvas
->DrawFocusRect(text_bounds
);
807 const ui::NativeTheme::ColorId color_id
=
808 text_color_id(HasFocus(), node
== selected_node_
);
809 const gfx::Rect
internal_bounds(
810 text_bounds
.x() + kTextHorizontalPadding
,
811 text_bounds
.y() + kTextVerticalPadding
,
812 text_bounds
.width() - kTextHorizontalPadding
* 2,
813 text_bounds
.height() - kTextVerticalPadding
* 2);
814 canvas
->DrawStringRect(node
->model_node()->GetTitle(), font_list_
,
815 GetNativeTheme()->GetSystemColor(color_id
),
820 void TreeView::PaintExpandControl(gfx::Canvas
* canvas
,
821 const gfx::Rect
& node_bounds
,
824 if (base::i18n::IsRTL()) {
825 center_x
= node_bounds
.right() - kArrowRegionSize
+
826 (kArrowRegionSize
- 4) / 2;
828 center_x
= node_bounds
.x() + (kArrowRegionSize
- 4) / 2;
830 int center_y
= node_bounds
.y() + node_bounds
.height() / 2;
831 const SkColor arrow_color
= GetNativeTheme()->GetSystemColor(
832 ui::NativeTheme::kColorId_TreeArrow
);
833 // TODO: this should come from an image.
835 int delta
= base::i18n::IsRTL() ? 1 : -1;
836 for (int i
= 0; i
< 4; ++i
) {
837 canvas
->FillRect(gfx::Rect(center_x
+ delta
* (2 - i
),
838 center_y
- (3 - i
), 1, (3 - i
) * 2 + 1),
843 for (int i
= 0; i
< 4; ++i
) {
844 canvas
->FillRect(gfx::Rect(center_x
- (3 - i
), center_y
+ i
,
845 (3 - i
) * 2 + 1, 1), arrow_color
);
850 TreeView::InternalNode
* TreeView::GetInternalNodeForModelNode(
851 ui::TreeModelNode
* model_node
,
852 GetInternalNodeCreateType create_type
) {
853 if (model_node
== root_
.model_node())
855 InternalNode
* parent_internal_node
=
856 GetInternalNodeForModelNode(model_
->GetParent(model_node
), create_type
);
857 if (!parent_internal_node
)
859 if (!parent_internal_node
->loaded_children()) {
860 if (create_type
== DONT_CREATE_IF_NOT_LOADED
)
862 LoadChildren(parent_internal_node
);
864 return parent_internal_node
->GetChild(
865 model_
->GetIndexOf(parent_internal_node
->model_node(), model_node
));
868 gfx::Rect
TreeView::GetBoundsForNode(InternalNode
* node
) {
870 row
= GetRowForInternalNode(node
, &depth
);
871 return GetBoundsForNodeImpl(node
, row
, depth
);
874 gfx::Rect
TreeView::GetBoundsForNodeImpl(InternalNode
* node
,
877 gfx::Rect
rect(depth
* kIndent
+ kHorizontalInset
,
878 row
* row_height_
+ kVerticalInset
,
879 text_offset_
+ node
->text_width() +
880 kTextHorizontalPadding
* 2,
882 rect
.set_x(GetMirroredXWithWidthInView(rect
.x(), rect
.width()));
886 int TreeView::GetRowForInternalNode(InternalNode
* node
, int* depth
) {
887 DCHECK(!node
->parent() || IsExpanded(node
->parent()->model_node()));
890 InternalNode
* tmp_node
= node
;
891 while (tmp_node
->parent()) {
892 int index_in_parent
= tmp_node
->parent()->GetIndexOf(tmp_node
);
895 for (int i
= 0; i
< index_in_parent
; ++i
)
896 row
+= tmp_node
->parent()->GetChild(i
)->NumExpandedNodes();
897 tmp_node
= tmp_node
->parent();
906 TreeView::InternalNode
* TreeView::GetNodeByRow(int row
, int* depth
) {
907 int current_row
= root_row();
909 return GetNodeByRowImpl(&root_
, row
, root_depth(), ¤t_row
, depth
);
912 TreeView::InternalNode
* TreeView::GetNodeByRowImpl(InternalNode
* node
,
917 if (*current_row
== target_row
) {
918 *node_depth
= current_depth
;
922 if (node
->is_expanded()) {
924 for (int i
= 0; i
< node
->child_count(); ++i
) {
925 InternalNode
* result
= GetNodeByRowImpl(
926 node
->GetChild(i
), target_row
, current_depth
, current_row
,
935 void TreeView::IncrementSelection(IncrementType type
) {
939 if (!GetSelectedNode()) {
940 // If nothing is selected select the first or last node.
941 if (!root_
.child_count())
943 if (type
== INCREMENT_PREVIOUS
) {
944 int row_count
= GetRowCount();
947 InternalNode
* node
= GetNodeByRow(row_count
- 1, &depth
);
948 SetSelectedNode(node
->model_node());
949 } else if (root_shown_
) {
950 SetSelectedNode(root_
.model_node());
952 SetSelectedNode(root_
.GetChild(0)->model_node());
958 int delta
= type
== INCREMENT_PREVIOUS
? -1 : 1;
959 int row
= GetRowForInternalNode(selected_node_
, &depth
);
960 int new_row
= std::min(GetRowCount() - 1, std::max(0, row
+ delta
));
962 return; // At the end/beginning.
963 SetSelectedNode(GetNodeByRow(new_row
, &depth
)->model_node());
966 void TreeView::CollapseOrSelectParent() {
967 if (selected_node_
) {
968 if (selected_node_
->is_expanded())
969 Collapse(selected_node_
->model_node());
970 else if (selected_node_
->parent())
971 SetSelectedNode(selected_node_
->parent()->model_node());
975 void TreeView::ExpandOrSelectChild() {
976 if (selected_node_
) {
977 if (!selected_node_
->is_expanded())
978 Expand(selected_node_
->model_node());
979 else if (selected_node_
->child_count())
980 SetSelectedNode(selected_node_
->GetChild(0)->model_node());
984 bool TreeView::ExpandImpl(TreeModelNode
* model_node
) {
985 TreeModelNode
* parent
= model_
->GetParent(model_node
);
987 // Node should be the root.
988 DCHECK_EQ(root_
.model_node(), model_node
);
989 bool was_expanded
= root_
.is_expanded();
990 root_
.set_is_expanded(true);
991 return !was_expanded
;
994 // Expand all the parents.
995 bool return_value
= ExpandImpl(parent
);
996 InternalNode
* internal_node
=
997 GetInternalNodeForModelNode(model_node
, CREATE_IF_NOT_LOADED
);
998 DCHECK(internal_node
);
999 if (!internal_node
->is_expanded()) {
1000 if (!internal_node
->loaded_children())
1001 LoadChildren(internal_node
);
1002 internal_node
->set_is_expanded(true);
1003 return_value
= true;
1005 return return_value
;
1008 PrefixSelector
* TreeView::GetPrefixSelector() {
1010 selector_
.reset(new PrefixSelector(this));
1011 return selector_
.get();
1014 // InternalNode ----------------------------------------------------------------
1016 TreeView::InternalNode::InternalNode()
1017 : model_node_(NULL
),
1018 loaded_children_(false),
1019 is_expanded_(false),
1023 TreeView::InternalNode::~InternalNode() {
1026 void TreeView::InternalNode::Reset(ui::TreeModelNode
* node
) {
1028 loaded_children_
= false;
1029 is_expanded_
= false;
1033 int TreeView::InternalNode::NumExpandedNodes() const {
1034 int result
= 1; // For this.
1037 for (int i
= 0; i
< child_count(); ++i
)
1038 result
+= GetChild(i
)->NumExpandedNodes();
1042 int TreeView::InternalNode::GetMaxWidth(int indent
, int depth
) {
1043 int max_width
= text_width_
+ indent
* depth
;
1046 for (int i
= 0; i
< child_count(); ++i
) {
1047 max_width
= std::max(max_width
,
1048 GetChild(i
)->GetMaxWidth(indent
, depth
+ 1));
1053 } // namespace views