Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(271)

Side by Side Diff: ui/views/view.cc

Issue 6286013: V2 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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.
4
5 #include "ui/views/view.h"
6
7 #include <algorithm>
8 #include <functional>
9
10 #include "gfx/canvas.h"
11 #include "gfx/point.h"
12 #include "gfx/size.h"
13 #include "ui/base/dragdrop/drag_drop_types.h"
14 #include "ui/views/events/context_menu_controller.h"
15 #include "ui/views/events/drag_controller.h"
16 #include "ui/views/layout/layout_manager.h"
17 #include "ui/views/rendering/border.h"
18 #include "ui/views/widget/widget.h"
19
20 namespace ui {
21
22 namespace {
23
24 // Saves gfx::Canvas state upon construction and automatically restores it when
25 // it goes out of scope.
26 class ScopedCanvasState {
27 public:
28 explicit ScopedCanvasState(gfx::Canvas* canvas) : canvas_(canvas) {
29 canvas_->Save();
30 }
31 ~ScopedCanvasState() {
32 canvas_->Restore();
33 }
34
35 private:
36 gfx::Canvas* canvas_;
37 DISALLOW_COPY_AND_ASSIGN(ScopedCanvasState);
38 };
39
40 bool ExceededDragThreshold(const gfx::Point& press_point,
41 const gfx::Point& event_point) {
42 return true;
sky 2011/02/01 18:56:22 TODO?
43 }
44
45 } // namespace
46
47 ////////////////////////////////////////////////////////////////////////////////
48 // View, public:
49
50 View::View()
51 : parent_(NULL),
52 parent_owned_(true),
53 visible_(true),
54 enabled_(true),
55 id_(-1),
56 group_(-1),
57 focusable_(false),
58 context_menu_controller_(NULL),
59 drag_controller_(NULL) {
60 }
61
62 View::~View() {
63 if (parent_)
64 parent_->RemoveChildView(this);
65
66 ViewVector::const_iterator it = children_.begin();
67 for (; it != children_.end(); ++it) {
68 (*it)->parent_ = NULL;
69 if ((*it)->parent_owned())
70 delete *it;
71 }
72 }
73
74 // Size and disposition --------------------------------------------------------
75
76 void View::SetBounds(int x, int y, int width, int height) {
77 SetBoundsRect(gfx::Rect(x, y, std::max(0, width), std::max(0, height)));
78 }
79
80 void View::SetBoundsRect(const gfx::Rect& bounds) {
81 gfx::Rect old_bounds = bounds_;
82 bounds_ = bounds;
83 if (old_bounds != bounds_)
84 OnBoundsChanged();
85 }
sky 2011/02/01 18:56:22 You sure you don't want the: if (bounds == bounds
86
87 gfx::Rect View::GetVisibleBounds() const {
88 // TODO(beng):
89 return bounds();
90 }
91
92 void View::SetSize(const gfx::Size& size) {
93 SetBounds(x(), y(), size.width(), size.height());
94 }
95
96 void View::SetPosition(const gfx::Point& position) {
97 SetBounds(position.x(), position.y(), width(), height());
98 }
99
100 void View::SetBorder(Border* border) {
101 border_.reset(border);
102 }
103
104 gfx::Rect View::GetContentsBounds() const {
105 if (border_.get()) {
106 return gfx::Rect(
107 border_->insets().left(), border_->insets().top(),
108 width() - border_->insets().right() - border_->insets().left(),
109 height() - border_->insets().bottom() - border_->insets().top());
110 }
111 return gfx::Rect(0, 0, width(), height());
112 }
113
114 gfx::Size View::GetPreferredSize() const {
115 return gfx::Size();
116 }
117
118 gfx::Size View::GetMinimumSize() const {
119 return GetPreferredSize();
120 }
121
122 void View::SetLayoutManager(LayoutManager* layout_manager) {
123 layout_manager_.reset(layout_manager);
124 }
125
126 void View::Layout() {
127 if (layout_manager_.get()) {
128 // Layout Manager handles laying out children.
129 layout_manager_->Layout(this);
130 } else {
131 // We handle laying out our own children.
132 ViewVector::iterator it = children_.begin();
133 for (; it != children_.end(); ++it)
134 (*it)->Layout();
135 }
136 }
sky 2011/02/01 18:56:22 This is missing some trickier bits. Specifically S
137
138 void View::SetVisible(bool visible) {
139 if (visible != visible_) {
140 visible_ = visible;
141 Invalidate();
sky 2011/02/01 18:56:22 Because Invalidate does nothing if !visible_, you
142 }
143 }
144
145 void View::SetEnabled(bool enabled) {
146 if (enabled != enabled_) {
147 enabled_ = enabled;
148 Invalidate();
149 }
150 }
151
152 void View::OnBoundsChanged() {
153 Layout();
154 }
155
156 // Coordinate conversion -------------------------------------------------------
157
158 // static
159 void View::ConvertPointToView(View* source, View* target, gfx::Point* point) {
160 View* inner = NULL;
161 View* outer = NULL;
162 if (source->Contains(target)) {
163 inner = target;
164 outer = source;
165 } else if (target->Contains(source)) {
166 inner = source;
167 outer = target;
168 } // Note that we cannot do a plain "else" here since |source| and |target|
169 // may be in different hierarchies with no relation.
170
171 if (inner && outer) {
172 gfx::Point offset;
173 View* temp = inner;
174 while (temp != outer) {
175 offset.Offset(temp->x(), temp->y());
176 temp = temp->parent();
177 }
178 // When target is contained by source, we need to subtract the offset.
179 // When source is contained by target, we need to add the fofset.
180 int multiplier = inner == target ? -1 : 1;
181 point->Offset(multiplier * offset.x(), multiplier * offset.y());
182 }
183 }
184
185 // static
186 void View::ConvertPointToScreen(View* source, gfx::Point* point) {
187 Widget* widget = source->GetWidget();
188 if (widget) {
189 ConvertPointToWidget(source, point);
190 gfx::Rect r = widget->GetClientAreaScreenBounds();
191 point->Offset(r.x(), r.y());
192 }
193 }
194
195 // static
196 void View::ConvertPointToWidget(View* source, gfx::Point* point) {
197 for (View* v = source; v; v = v->parent())
198 point->Offset(v->x(), v->y());
199 }
200
201 // Tree operations -------------------------------------------------------------
202
203 Widget* View::GetWidget() const {
204 return parent_ ? parent_->GetWidget() : NULL;
205 }
206
207 void View::AddChildView(View* view) {
208 AddChildViewAt(view, children_.size());
209 }
210
211 void View::AddChildViewAt(View* view, size_t index) {
212 CHECK(view != this) << "A view cannot be its own child.";
213
214 // Remove the child from its current parent if any.
215 if (view->parent())
216 view->parent()->RemoveChildView(view);
217
218 children_.insert(children_.begin() + index, view);
219 view->parent_ = this;
220
221 // Notify the hierarchy.
222 NotifyHierarchyChanged(this, view, true);
223
224 // TODO(beng): Notify other objects like tooltip, layout manager, etc.
sky 2011/02/01 18:56:22 and child bounds notification and focus initializa
Ben Goodger (Google) 2011/02/01 20:04:57 I actually want to try and see if I can remove the
225 }
226
227 View* View::RemoveChildView(View* view) {
228 ViewVector::iterator it = find(children_.begin(), children_.end(), view);
229 if (it != children_.end()) {
230 View* old_parent = view->parent_;
231 view->parent_ = NULL;
232 children_.erase(it);
233 NotifyHierarchyChanged(old_parent, view, false);
234 }
235
236 // TODO(beng): Notify other objects like tooltip, layout manager, etc.
237 return view;
238 }
239
240 void View::RemoveAllChildViews(bool delete_children) {
241 // TODO(beng): use for_each.
242 ViewVector::iterator it = children_.begin();
243 while (it != children_.end())
244 RemoveChildView(*it);
sky 2011/02/01 18:56:22 This needs to honor delete_children.
245 }
246
247 View* View::GetChildViewAt(size_t index) {
248 CHECK(index < child_count());
249 return children_[index];
250 }
251
252 bool View::Contains(View* child) {
253 while (child) {
254 if (child == this)
255 return true;
256 child = child->parent();
257 }
258 return false;
259 }
260
261 View* View::GetViewForPoint(const gfx::Point& point) const {
262 ViewVector::const_reverse_iterator it = children_.rbegin();
263 for (; it != children_.rend(); ++it) {
264 View* child = *it;
265 if (!child->visible())
266 continue;
267
268 gfx::Point point_in_child_coords(point);
269 View::ConvertPointToView(const_cast<View*>(this), child,
270 &point_in_child_coords);
271 if (child->HitTest(point_in_child_coords))
272 return child->GetViewForPoint(point_in_child_coords);
273 }
274 return const_cast<View*>(this);
275 }
276
277 bool View::HitTest(const gfx::Point& point) const {
278 // TODO(beng): Hit test mask support.
279 return GetContentsBounds().Contains(point);
sky 2011/02/01 18:56:22 I think the border should be considered part of th
280 }
281
282 View* View::GetViewById(int id) const {
283 if (id_ == id)
284 return const_cast<View*>(this);
285 ViewVector::const_iterator it = children_.begin();
286 for (; it != children_.end(); ++it) {
287 View* view = (*it)->GetViewById(id);
288 if (view)
289 return view;
290 }
291 return NULL;
292 }
293
294 void View::GetViewsWithGroup(int group, ViewVector* vec) const {
295 if (group_ == group)
296 vec->push_back(const_cast<View*>(this));
297 ViewVector::const_iterator it = children_.begin();
298 for (; it != children_.end(); ++it)
299 (*it)->GetViewsWithGroup(group, vec);
300 }
301
302 void View::OnViewAdded(View* parent, View* child) {
303 }
304
305 void View::OnViewRemoved(View* parent, View* child) {
306 }
307
308 void View::OnViewAddedToWidget() {
309 }
310
311 void View::OnViewRemovedFromWidget() {
312 }
313
314 // Accelerators ----------------------------------------------------------------
315
316 void View::AddAccelerator(const Accelerator& accelerator) {
317 }
318
319 void View::RemoveAccelerator(const Accelerator& accelerator) {
320 }
321
322 void View::RemoveAllAccelerators() {
323 }
324
325 bool View::OnAcceleratorPressed(const Accelerator& accelerator) {
326 return false;
327 }
328
329 // Focus -----------------------------------------------------------------------
330
331 FocusManager* View::GetFocusManager() const {
332 return NULL;
333 }
334
335 FocusTraversable* View::GetFocusTraversable() const {
336 return NULL;
337 }
338
339 View* View::GetNextFocusableView() const {
340 return NULL;
341 }
342
343 View* View::GetPreviousFocusableView() const {
344 return NULL;
345 }
346
347 bool View::SkipDefaultKeyEventProcessing(const KeyEvent& event) const {
348 return false;
349 }
350
351 bool View::IsFocusable() const {
352 return false;
353 }
354
355 bool View::HasFocus() const {
356 return false;
357 }
358
359 void View::RequestFocus() {
360 }
361
362 void View::OnFocus(/* const FocusEvent& event */) {
363 }
364
365 void View::OnBlur() {
366 }
367
368 // Input -----------------------------------------------------------------------
369
370 bool View::OnKeyPressed(const KeyEvent& event) {
371 return true;
372 }
373
374 bool View::OnKeyReleased(const KeyEvent& event) {
375 return true;
376 }
377
378 bool View::OnMouseWheel(const MouseWheelEvent& event) {
379 return true;
380 }
381
382 bool View::OnMousePressed(const MouseEvent& event) {
383 return true;
384 }
385
386 bool View::OnMouseDragged(const MouseEvent& event) {
387 return true;
388 }
389
390 void View::OnMouseReleased(const MouseEvent& event) {
391
392 }
393
394 void View::OnMouseCaptureLost() {
395
396 }
397
398 void View::OnMouseMoved(const MouseEvent& event) {
399
400 }
401
402 void View::OnMouseEntered(const MouseEvent& event) {
403
404 }
405
406 void View::OnMouseExited(const MouseEvent& event) {
407
408 }
409
410 gfx::NativeCursor View::GetCursorForPoint(const gfx::Point& point) {
411 return NULL;
412 }
413
414 // Painting --------------------------------------------------------------------
415
416 void View::Invalidate() {
417 InvalidateRect(gfx::Rect(0, 0, width(), height()));
418 }
419
420 void View::InvalidateRect(const gfx::Rect& invalid_rect) {
421 if (!visible_)
422 return;
423
424 if (parent_) {
425 gfx::Rect r = invalid_rect;
426 r.Offset(bounds_.origin());
427 parent_->InvalidateRect(r);
428 }
429 }
430
431 void View::Paint(gfx::Canvas* canvas) {
sky 2011/02/01 18:56:22 Because the canvas is not in the child coordinates
432 // Invisible views are not painted.
433 if (!visible_)
434 return;
435
436 ScopedCanvasState canvas_state(canvas);
437 if (canvas->ClipRectInt(x(), y(), width(), height())) {
438 canvas->TranslateInt(x(), y());
439 // TODO(beng): RTL
440 ScopedCanvasState canvas_state(canvas);
sky 2011/02/01 18:56:22 Don't you need a different name than on line 436?
441 OnPaint(canvas);
442 PaintChildren(canvas);
443 }
444 }
445
446 void View::PaintChildren(gfx::Canvas* canvas) {
447 // TODO(beng): use for_each.
448 // std::for_each(children_.begin(), children_.end(),
449 // std::bind2nd(std::mem_fun_ref(&View::Paint), canvas));
450 ViewVector::iterator it = children_.begin();
451 for (; it != children_.end(); ++it)
452 (*it)->Paint(canvas);
453 }
454
455 void View::OnPaint(gfx::Canvas* canvas) {
456 OnPaintBackground(canvas);
457 OnPaintFocusBorder(canvas);
458 OnPaintBorder(canvas);
459 }
460
461 void View::OnPaintBackground(gfx::Canvas* canvas) {
462 }
463
464 void View::OnPaintBorder(gfx::Canvas* canvas) {
465 if (border_.get())
466 border_->Paint(const_cast<const View*>(this), canvas);
467 }
468
469 void View::OnPaintFocusBorder(gfx::Canvas* canvas) {
470 }
471
472 // Resources -------------------------------------------------------------------
473
474 ThemeProvider* View::GetThemeProvider() const {
475 Widget* widget = GetWidget();
476 return widget ? widget->GetThemeProvider() : NULL;
477 }
478
479 ////////////////////////////////////////////////////////////////////////////////
480 // View, private:
481
482 void View::DragInfo::Reset() {
483 possible_drag = false;
484 press_point = gfx::Point();
485 }
486
487 void View::DragInfo::PossibleDrag(const gfx::Point& point) {
488 possible_drag = true;
489 press_point = point;
490 }
491
492 // Drag & Drop -----------------------------------------------------------------
493
494 int View::GetDragOperations(const gfx::Point& point) {
495 return drag_controller_ ?
496 drag_controller_->GetDragOperations(const_cast<View*>(this), point) :
497 DragDropTypes::DRAG_NONE;
498 }
499
500 void View::WriteDragData(const gfx::Point& point, OSExchangeData* data) {
501 drag_controller_->WriteDragData(this, point, data);
502 }
503
504 void View::StartShellDrag(const MouseEvent& event,
sky 2011/02/01 18:56:22 How come you named this with 'shell'? Why not just
Ben Goodger (Google) 2011/02/01 20:04:57 These methods are for initiating and dealing with
505 const gfx::Point& press_point) {
506 // TODO(beng): system stuff.
507 }
508
509 // RootView API ----------------------------------------------------------------
510
511 bool View::MousePressed(const MouseEvent& event, DragInfo* drag_info) {
512 bool handled = OnMousePressed(event);
513 if (!enabled_)
sky 2011/02/01 18:56:22 This crashes if 'this' is deleted in handling OnMo
Ben Goodger (Google) 2011/02/01 20:04:57 I added a TODO... I do have to wonder if this is a
514 return handled;
515
516 int drag_operations =
517 enabled_ && event.IsOnlyLeftMouseButton() && HitTest(event.location()) ?
518 GetDragOperations(event.location()) : DragDropTypes::DRAG_NONE;
519 if (drag_operations != DragDropTypes::DRAG_NONE) {
520 drag_info->PossibleDrag(event.location());
521 return true;
522 }
523 bool has_context_menu = event.IsRightMouseButton() ?
524 !!context_menu_controller_ : NULL;
525 return has_context_menu || handled;
526 }
527
528 bool View::MouseDragged(const MouseEvent& event, DragInfo* drag_info) {
529 if (drag_info->possible_drag &&
530 ExceededDragThreshold(drag_info->press_point, event.location())) {
531 if (!drag_controller_ ||
532 drag_controller_->CanStartDrag(this, drag_info->press_point,
533 event.location())) {
534 StartShellDrag(event, drag_info->press_point);
535 }
536 } else {
537 if (OnMouseDragged(event))
538 return true;
539 }
540 return !!context_menu_controller_ || drag_info->possible_drag;
sky 2011/02/01 18:56:22 This crashes if 'this' is deleted in handling OnMo
541 }
542
543 void View::MouseReleased(const MouseEvent& event) {
544 OnMouseReleased(event);
545 if (context_menu_controller_ && event.IsOnlyRightMouseButton()) {
sky 2011/02/01 18:56:22 This crashes if 'this' is deleted in handling OnMo
546 gfx::Point location(event.location());
547 if (HitTest(location)) {
548 ConvertPointToScreen(this, &location);
549 context_menu_controller_->ShowContextMenu(this, location, true);
550 }
551 }
552 }
553
554 // Tree operations -------------------------------------------------------------
555
556 void View::NotifyHierarchyChanged(View* parent, View* child, bool is_add) {
557 // Notify the child. Note that we call GetWidget() on the parent, not the
558 // child, since this method is called after the child is already removed from
559 // the hierarchy when |is_add| is false and so child->GetWidget() will always
560 // return NULL.
561 bool has_widget = parent->GetWidget() != NULL;
562 CallViewNotification(child, parent, child, is_add, has_widget);
563
564 // Notify the hierarchy up.
565 NotifyHierarchyChangedUp(parent, child, is_add);
566
567 // Notify the hierarchy down.
568 if (!is_add) {
569 // Because |child| has already been removed from |parent|'s child list, we
570 // need to notify its hierarchy manually.
571 child->NotifyHierarchyChangedDown(parent, child, is_add, has_widget);
572 }
573 NotifyHierarchyChangedDown(parent, child, is_add, has_widget);
574 }
575
576 void View::NotifyHierarchyChangedUp(View* parent, View* child, bool is_add) {
577 for (View* v = parent; v; v = v->parent()) {
578 if (is_add)
579 v->OnViewAdded(parent, child);
580 else
581 v->OnViewRemoved(parent, child);
582 }
583 }
584
585 void View::NotifyHierarchyChangedDown(View* parent, View* child, bool is_add,
586 bool has_widget) {
587 ViewVector::iterator it = children_.begin();
588 for (; it != children_.end(); ++it) {
589 CallViewNotification(*it, parent, child, is_add, has_widget);
590 (*it)->NotifyHierarchyChangedDown(parent, child, is_add, has_widget);
591 }
592 }
593
594 void View::CallViewNotification(View* target,
595 View* parent,
596 View* child,
597 bool is_add,
598 bool has_widget) {
599 if (is_add) {
600 target->OnViewAdded(parent, child);
601 if (has_widget)
602 target->OnViewAddedToWidget();
603 } else {
604 target->OnViewRemoved(parent, child);
605 if (has_widget)
606 target->OnViewRemovedFromWidget();
607 }
608 }
609
610 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698