OLD | NEW |
---|---|
(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 | |
OLD | NEW |