| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/aura/window.h" |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| #include "ui/aura/client/stacking_client.h" |
| #include "ui/aura/desktop.h" |
| #include "ui/aura/event.h" |
| #include "ui/aura/event_filter.h" |
| #include "ui/aura/layout_manager.h" |
| #include "ui/aura/window_delegate.h" |
| #include "ui/aura/window_observer.h" |
| #include "ui/aura/window_types.h" |
| #include "ui/base/animation/multi_animation.h" |
| #include "ui/gfx/canvas_skia.h" |
| #include "ui/gfx/compositor/compositor.h" |
| #include "ui/gfx/screen.h" |
| |
| namespace aura { |
| |
| Window::Window(WindowDelegate* delegate) |
| : type_(WINDOW_TYPE_UNKNOWN), |
| delegate_(delegate), |
| parent_(NULL), |
| transient_parent_(NULL), |
| id_(-1), |
| user_data_(NULL), |
| stops_event_propagation_(false) { |
| } |
| |
| Window::~Window() { |
| // Let the delegate know we're in the processing of destroying. |
| if (delegate_) |
| delegate_->OnWindowDestroying(); |
| |
| // Let the root know so that it can remove any references to us. |
| Desktop* desktop = GetDesktop(); |
| if (desktop) |
| desktop->WindowDestroying(this); |
| |
| // Then destroy the children. |
| while (!children_.empty()) { |
| Window* child = children_[0]; |
| delete child; |
| // Deleting the child so remove it from out children_ list. |
| DCHECK(std::find(children_.begin(), children_.end(), child) == |
| children_.end()); |
| } |
| |
| // Removes ourselves from our transient parent. |
| if (transient_parent_) |
| transient_parent_->RemoveTransientChild(this); |
| |
| // Destroy transient children. |
| Windows transient_children(transient_children_); |
| STLDeleteElements(&transient_children); |
| DCHECK(transient_children_.empty()); |
| |
| // And let the delegate do any post cleanup. |
| if (delegate_) |
| delegate_->OnWindowDestroyed(); |
| if (parent_) |
| parent_->RemoveChild(this); |
| |
| FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroyed(this)); |
| } |
| |
| void Window::Init(ui::Layer::LayerType layer_type) { |
| layer_.reset(new ui::Layer(layer_type)); |
| layer_->SetVisible(false); |
| layer_->set_delegate(this); |
| } |
| |
| void Window::SetType(WindowType type) { |
| // Cannot change type after the window is initialized. |
| DCHECK(!layer()); |
| type_ = type; |
| } |
| |
| void Window::Show() { |
| SetVisible(true); |
| } |
| |
| void Window::Hide() { |
| SetVisible(false); |
| ReleaseCapture(); |
| if (Desktop::GetInstance()->active_window() == this || |
| !Desktop::GetInstance()->active_window()) { |
| Desktop::GetInstance()->ActivateTopmostWindow(); |
| } |
| } |
| |
| bool Window::IsVisible() const { |
| return layer_->IsDrawn(); |
| } |
| |
| gfx::Rect Window::GetScreenBounds() const { |
| gfx::Point origin = bounds().origin(); |
| Window::ConvertPointToWindow(parent_, |
| aura::Desktop::GetInstance(), |
| &origin); |
| return gfx::Rect(origin, bounds().size()); |
| } |
| |
| void Window::Activate() { |
| // If we support minimization need to ensure this restores the window first. |
| aura::Desktop::GetInstance()->SetActiveWindow(this, this); |
| } |
| |
| void Window::Deactivate() { |
| aura::Desktop::GetInstance()->Deactivate(this); |
| } |
| |
| bool Window::IsActive() const { |
| return aura::Desktop::GetInstance()->active_window() == this; |
| } |
| |
| void Window::SetTransform(const ui::Transform& transform) { |
| layer()->SetTransform(transform); |
| } |
| |
| void Window::SetLayoutManager(LayoutManager* layout_manager) { |
| layout_manager_.reset(layout_manager); |
| } |
| |
| void Window::SetBounds(const gfx::Rect& new_bounds) { |
| if (parent_ && parent_->layout_manager()) |
| parent_->layout_manager()->SetChildBounds(this, new_bounds); |
| else |
| SetBoundsInternal(new_bounds); |
| } |
| |
| gfx::Rect Window::GetTargetBounds() const { |
| return layer_->GetTargetBounds(); |
| } |
| |
| const gfx::Rect& Window::bounds() const { |
| return layer_->bounds(); |
| } |
| |
| void Window::SchedulePaintInRect(const gfx::Rect& rect) { |
| layer_->SchedulePaint(rect); |
| } |
| |
| void Window::SetCanvas(const SkCanvas& canvas, const gfx::Point& origin) { |
| // TODO: figure out how this is going to work when animating the layer. In |
| // particular if we're animating the size then the underlying Texture is going |
| // to be unhappy if we try to set a texture on a size bigger than the size of |
| // the texture. |
| layer_->SetCanvas(canvas, origin); |
| } |
| |
| void Window::SetParent(Window* parent) { |
| if (parent) |
| parent->AddChild(this); |
| else if (Desktop::GetInstance()->stacking_client()) |
| Desktop::GetInstance()->stacking_client()->AddChildToDefaultParent(this); |
| else |
| NOTREACHED(); |
| } |
| |
| void Window::MoveChildToFront(Window* child) { |
| if (children_.size() <= 1 || child == children_.back()) |
| return; // In the front already. |
| MoveChildAbove(child, children_.back()); |
| } |
| |
| void Window::MoveChildAbove(Window* child, Window* other) { |
| DCHECK_NE(child, other); |
| DCHECK(child); |
| DCHECK(other); |
| DCHECK_EQ(this, child->parent()); |
| DCHECK_EQ(this, other->parent()); |
| |
| size_t child_i = |
| std::find(children_.begin(), children_.end(), child) - children_.begin(); |
| size_t other_i = |
| std::find(children_.begin(), children_.end(), other) - children_.begin(); |
| if (child_i > other_i) |
| return; // Already in front of |other|. |
| |
| // Reorder children. |
| children_.erase(children_.begin() + child_i); |
| children_.insert(children_.begin() + other_i, child); |
| |
| // Reorder the layer. |
| layer()->MoveAbove(child->layer(), other->layer()); |
| |
| // Move any transient children that share the same parent to be in front of |
| // 'child'. |
| Window* last_transient = child; |
| for (Windows::iterator i = child->transient_children_.begin(); |
| i != child->transient_children_.end(); ++i) { |
| Window* transient_child = *i; |
| if (transient_child->parent_ == this) { |
| MoveChildAbove(transient_child, last_transient); |
| last_transient = transient_child; |
| } |
| } |
| } |
| |
| bool Window::CanActivate() const { |
| return IsVisible() && (!delegate_ || delegate_->ShouldActivate(NULL)); |
| } |
| |
| void Window::AddChild(Window* child) { |
| DCHECK(std::find(children_.begin(), children_.end(), child) == |
| children_.end()); |
| if (child->parent()) |
| child->parent()->RemoveChild(child); |
| child->parent_ = this; |
| layer_->Add(child->layer_.get()); |
| children_.push_back(child); |
| if (layout_manager_.get()) |
| layout_manager_->OnWindowAddedToLayout(child); |
| FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowAdded(child)); |
| } |
| |
| void Window::AddTransientChild(Window* child) { |
| if (child->transient_parent_) |
| child->transient_parent_->RemoveTransientChild(child); |
| DCHECK(std::find(transient_children_.begin(), transient_children_.end(), |
| child) == transient_children_.end()); |
| transient_children_.push_back(child); |
| child->transient_parent_ = this; |
| } |
| |
| void Window::RemoveTransientChild(Window* child) { |
| Windows::iterator i = |
| std::find(transient_children_.begin(), transient_children_.end(), child); |
| DCHECK(i != transient_children_.end()); |
| transient_children_.erase(i); |
| if (child->transient_parent_ == this) |
| child->transient_parent_ = NULL; |
| } |
| |
| void Window::RemoveChild(Window* child) { |
| Windows::iterator i = std::find(children_.begin(), children_.end(), child); |
| DCHECK(i != children_.end()); |
| if (layout_manager_.get()) |
| layout_manager_->OnWillRemoveWindowFromLayout(child); |
| FOR_EACH_OBSERVER(WindowObserver, observers_, OnWillRemoveWindow(child)); |
| aura::Window* desktop = child->GetDesktop(); |
| child->parent_ = NULL; |
| if (desktop) |
| desktop->WindowDetachedFromDesktop(child); |
| layer_->Remove(child->layer_.get()); |
| children_.erase(i); |
| } |
| |
| Window* Window::GetChildById(int id) { |
| return const_cast<Window*>(const_cast<const Window*>(this)->GetChildById(id)); |
| } |
| |
| const Window* Window::GetChildById(int id) const { |
| Windows::const_iterator i; |
| for (i = children_.begin(); i != children_.end(); ++i) { |
| if ((*i)->id() == id) |
| return *i; |
| const Window* result = (*i)->GetChildById(id); |
| if (result) |
| return result; |
| } |
| return NULL; |
| } |
| |
| // static |
| void Window::ConvertPointToWindow(const Window* source, |
| const Window* target, |
| gfx::Point* point) { |
| if (!source) |
| return; |
| ui::Layer::ConvertPointToLayer(source->layer(), target->layer(), point); |
| } |
| |
| gfx::NativeCursor Window::GetCursor(const gfx::Point& point) const { |
| return delegate_ ? delegate_->GetCursor(point) : gfx::kNullCursor; |
| } |
| |
| void Window::SetEventFilter(EventFilter* event_filter) { |
| event_filter_.reset(event_filter); |
| } |
| |
| void Window::AddObserver(WindowObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void Window::RemoveObserver(WindowObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| bool Window::ContainsPoint(const gfx::Point& local_point) { |
| gfx::Rect local_bounds(gfx::Point(), bounds().size()); |
| return local_bounds.Contains(local_point); |
| } |
| |
| bool Window::HitTest(const gfx::Point& local_point) { |
| // TODO(beng): hittest masks. |
| return ContainsPoint(local_point); |
| } |
| |
| Window* Window::GetEventHandlerForPoint(const gfx::Point& local_point) { |
| return GetWindowForPoint(local_point, true, true); |
| } |
| |
| Window* Window::GetTopWindowContainingPoint(const gfx::Point& local_point) { |
| return GetWindowForPoint(local_point, false, false); |
| } |
| |
| void Window::Focus() { |
| DCHECK(GetFocusManager()); |
| GetFocusManager()->SetFocusedWindow(this); |
| } |
| |
| void Window::Blur() { |
| DCHECK(GetFocusManager()); |
| GetFocusManager()->SetFocusedWindow(NULL); |
| } |
| |
| bool Window::HasFocus() const { |
| const internal::FocusManager* focus_manager = GetFocusManager(); |
| return focus_manager ? focus_manager->IsFocusedWindow(this) : false; |
| } |
| |
| // For a given window, we determine its focusability by inspecting each sibling |
| // after it (i.e. drawn in front of it in the z-order) to see if it stops |
| // propagation of events that would otherwise be targeted at windows behind it. |
| // We then perform this same check on every window up to the root. |
| bool Window::CanFocus() const { |
| // TODO(beng): Figure out how to consult the delegate wrt. focusability also. |
| if (!IsVisible() || !parent_) |
| return false; |
| |
| Windows::const_iterator i = std::find(parent_->children().begin(), |
| parent_->children().end(), |
| this); |
| for (++i; i != parent_->children().end(); ++i) { |
| if ((*i)->StopsEventPropagation()) |
| return false; |
| } |
| return parent_->CanFocus(); |
| } |
| |
| internal::FocusManager* Window::GetFocusManager() { |
| return const_cast<internal::FocusManager*>( |
| static_cast<const Window*>(this)->GetFocusManager()); |
| } |
| |
| const internal::FocusManager* Window::GetFocusManager() const { |
| return parent_ ? parent_->GetFocusManager() : NULL; |
| } |
| |
| void Window::SetCapture() { |
| if (!IsVisible()) |
| return; |
| |
| Desktop* desktop = GetDesktop(); |
| if (!desktop) |
| return; |
| |
| desktop->SetCapture(this); |
| } |
| |
| void Window::ReleaseCapture() { |
| Desktop* desktop = GetDesktop(); |
| if (!desktop) |
| return; |
| |
| desktop->ReleaseCapture(this); |
| } |
| |
| bool Window::HasCapture() { |
| Desktop* desktop = GetDesktop(); |
| return desktop && desktop->capture_window() == this; |
| } |
| |
| void Window::SetProperty(const char* name, void* value) { |
| void* old = GetProperty(name); |
| if (value) |
| prop_map_[name] = value; |
| else |
| prop_map_.erase(name); |
| FOR_EACH_OBSERVER(WindowObserver, observers_, |
| OnPropertyChanged(this, name, old)); |
| } |
| |
| void Window::SetIntProperty(const char* name, int value) { |
| SetProperty(name, reinterpret_cast<void*>(value)); |
| } |
| |
| void* Window::GetProperty(const char* name) const { |
| std::map<const char*, void*>::const_iterator iter = prop_map_.find(name); |
| if (iter == prop_map_.end()) |
| return NULL; |
| return iter->second; |
| } |
| |
| int Window::GetIntProperty(const char* name) const { |
| return static_cast<int>(reinterpret_cast<intptr_t>( |
| GetProperty(name))); |
| } |
| |
| Desktop* Window::GetDesktop() { |
| return parent_ ? parent_->GetDesktop() : NULL; |
| } |
| |
| void Window::WindowDetachedFromDesktop(aura::Window* window) { |
| } |
| |
| void Window::SetBoundsInternal(const gfx::Rect& new_bounds) { |
| const gfx::Rect old_bounds = layer_->GetTargetBounds(); |
| |
| // Always need to set the layer's bounds -- even if it is to the same thing. |
| // This may cause important side effects such as stopping animation. |
| layer_->SetBounds(new_bounds); |
| |
| // If we're not changing the effective bounds, then we can bail early and skip |
| // notifying our listeners. |
| if (old_bounds == new_bounds) |
| return; |
| |
| if (layout_manager_.get()) |
| layout_manager_->OnWindowResized(); |
| if (delegate_) |
| delegate_->OnBoundsChanged(old_bounds, new_bounds); |
| } |
| |
| void Window::SetVisible(bool visible) { |
| if (visible == layer_->visible()) |
| return; // No change. |
| |
| bool was_visible = IsVisible(); |
| layer_->SetVisible(visible); |
| bool is_visible = IsVisible(); |
| if (was_visible != is_visible) { |
| SchedulePaint(); |
| if (delegate_) |
| delegate_->OnWindowVisibilityChanged(is_visible); |
| } |
| if (parent_ && parent_->layout_manager_.get()) |
| parent_->layout_manager_->OnChildWindowVisibilityChanged(this, visible); |
| FOR_EACH_OBSERVER(WindowObserver, observers_, |
| OnWindowVisibilityChanged(this, visible)); |
| } |
| |
| void Window::SchedulePaint() { |
| SchedulePaintInRect(gfx::Rect(0, 0, bounds().width(), bounds().height())); |
| } |
| |
| bool Window::StopsEventPropagation() const { |
| return stops_event_propagation_ && !children_.empty(); |
| } |
| |
| Window* Window::GetWindowForPoint(const gfx::Point& local_point, |
| bool return_tightest, |
| bool for_event_handling) { |
| if (!IsVisible()) |
| return NULL; |
| |
| if ((for_event_handling && !HitTest(local_point)) || |
| (!for_event_handling && !ContainsPoint(local_point))) |
| return NULL; |
| |
| if (!return_tightest && delegate_) |
| return this; |
| |
| for (Windows::const_reverse_iterator it = children_.rbegin(); |
| it != children_.rend(); ++it) { |
| Window* child = *it; |
| if (!child->IsVisible()) |
| continue; |
| |
| gfx::Point point_in_child_coords(local_point); |
| Window::ConvertPointToWindow(this, child, &point_in_child_coords); |
| Window* match = child->GetWindowForPoint(point_in_child_coords, |
| return_tightest, |
| for_event_handling); |
| if (match) |
| return match; |
| |
| if (for_event_handling && child->StopsEventPropagation()) |
| break; |
| } |
| |
| return delegate_ ? this : NULL; |
| } |
| |
| void Window::OnPaintLayer(gfx::Canvas* canvas) { |
| if (delegate_) |
| delegate_->OnPaint(canvas); |
| } |
| |
| } // namespace aura |