Home | History | Annotate | Download | only in window
      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.
      4 
      5 #include "ui/views/window/non_client_view.h"
      6 
      7 #include "ui/base/accessibility/accessible_view_state.h"
      8 #include "ui/base/hit_test.h"
      9 #include "ui/gfx/rect_conversions.h"
     10 #include "ui/views/rect_based_targeting_utils.h"
     11 #include "ui/views/widget/root_view.h"
     12 #include "ui/views/widget/widget.h"
     13 #include "ui/views/window/client_view.h"
     14 
     15 namespace views {
     16 
     17 // static
     18 const char NonClientFrameView::kViewClassName[] =
     19     "ui/views/window/NonClientFrameView";
     20 
     21 const char NonClientView::kViewClassName[] =
     22     "ui/views/window/NonClientView";
     23 
     24 // The frame view and the client view are always at these specific indices,
     25 // because the RootView message dispatch sends messages to items higher in the
     26 // z-order first and we always want the client view to have first crack at
     27 // handling mouse messages.
     28 static const int kFrameViewIndex = 0;
     29 static const int kClientViewIndex = 1;
     30 // The overlay view is always on top (index == child_count() - 1).
     31 
     32 ////////////////////////////////////////////////////////////////////////////////
     33 // NonClientView, public:
     34 
     35 NonClientView::NonClientView()
     36     : client_view_(NULL),
     37       overlay_view_(NULL) {
     38 }
     39 
     40 NonClientView::~NonClientView() {
     41   // This value may have been reset before the window hierarchy shuts down,
     42   // so we need to manually remove it.
     43   RemoveChildView(frame_view_.get());
     44 }
     45 
     46 void NonClientView::SetFrameView(NonClientFrameView* frame_view) {
     47   // See comment in header about ownership.
     48   frame_view->set_owned_by_client();
     49   if (frame_view_.get())
     50     RemoveChildView(frame_view_.get());
     51   frame_view_.reset(frame_view);
     52   if (parent())
     53     AddChildViewAt(frame_view_.get(), kFrameViewIndex);
     54 }
     55 
     56 void NonClientView::SetOverlayView(View* view) {
     57   if (overlay_view_)
     58     RemoveChildView(overlay_view_);
     59 
     60   if (!view)
     61     return;
     62 
     63   overlay_view_ = view;
     64   if (parent())
     65     AddChildView(overlay_view_);
     66 }
     67 
     68 bool NonClientView::CanClose() {
     69   return client_view_->CanClose();
     70 }
     71 
     72 void NonClientView::WindowClosing() {
     73   client_view_->WidgetClosing();
     74 }
     75 
     76 void NonClientView::UpdateFrame() {
     77   Widget* widget = GetWidget();
     78   SetFrameView(widget->CreateNonClientFrameView());
     79   widget->ThemeChanged();
     80   Layout();
     81   SchedulePaint();
     82 }
     83 
     84 void NonClientView::SetInactiveRenderingDisabled(bool disable) {
     85   frame_view_->SetInactiveRenderingDisabled(disable);
     86 }
     87 
     88 gfx::Rect NonClientView::GetWindowBoundsForClientBounds(
     89     const gfx::Rect client_bounds) const {
     90   return frame_view_->GetWindowBoundsForClientBounds(client_bounds);
     91 }
     92 
     93 int NonClientView::NonClientHitTest(const gfx::Point& point) {
     94   // The NonClientFrameView is responsible for also asking the ClientView.
     95   return frame_view_->NonClientHitTest(point);
     96 }
     97 
     98 void NonClientView::GetWindowMask(const gfx::Size& size,
     99                                   gfx::Path* window_mask) {
    100   frame_view_->GetWindowMask(size, window_mask);
    101 }
    102 
    103 void NonClientView::ResetWindowControls() {
    104   frame_view_->ResetWindowControls();
    105 }
    106 
    107 void NonClientView::UpdateWindowIcon() {
    108   frame_view_->UpdateWindowIcon();
    109 }
    110 
    111 void NonClientView::UpdateWindowTitle() {
    112   frame_view_->UpdateWindowTitle();
    113 }
    114 
    115 void NonClientView::LayoutFrameView() {
    116   // First layout the NonClientFrameView, which determines the size of the
    117   // ClientView...
    118   frame_view_->SetBounds(0, 0, width(), height());
    119 
    120   // We need to manually call Layout here because layout for the frame view can
    121   // change independently of the bounds changing - e.g. after the initial
    122   // display of the window the metrics of the native window controls can change,
    123   // which does not change the bounds of the window but requires a re-layout to
    124   // trigger a repaint. We override OnBoundsChanged() for the NonClientFrameView
    125   // to do nothing so that SetBounds above doesn't cause Layout to be called
    126   // twice.
    127   frame_view_->Layout();
    128 }
    129 
    130 void NonClientView::SetAccessibleName(const string16& name) {
    131   accessible_name_ = name;
    132 }
    133 
    134 ////////////////////////////////////////////////////////////////////////////////
    135 // NonClientView, View overrides:
    136 
    137 gfx::Size NonClientView::GetPreferredSize() {
    138   // TODO(pkasting): This should probably be made to look similar to
    139   // GetMinimumSize() below.  This will require implementing GetPreferredSize()
    140   // better in the various frame views.
    141   gfx::Rect client_bounds(gfx::Point(), client_view_->GetPreferredSize());
    142   return GetWindowBoundsForClientBounds(client_bounds).size();
    143 }
    144 
    145 gfx::Size NonClientView::GetMinimumSize() {
    146   return frame_view_->GetMinimumSize();
    147 }
    148 
    149 gfx::Size NonClientView::GetMaximumSize() {
    150   return frame_view_->GetMaximumSize();
    151 }
    152 
    153 void NonClientView::Layout() {
    154   LayoutFrameView();
    155 
    156   // Then layout the ClientView, using those bounds.
    157   client_view_->SetBoundsRect(frame_view_->GetBoundsForClientView());
    158 
    159   // We need to manually call Layout on the ClientView as well for the same
    160   // reason as above.
    161   client_view_->Layout();
    162 
    163   if (overlay_view_ && overlay_view_->visible())
    164     overlay_view_->SetBoundsRect(GetLocalBounds());
    165 }
    166 
    167 void NonClientView::ViewHierarchyChanged(
    168     const ViewHierarchyChangedDetails& details) {
    169   // Add our two child views here as we are added to the Widget so that if we
    170   // are subsequently resized all the parent-child relationships are
    171   // established.
    172   if (details.is_add && GetWidget() && details.child == this) {
    173     AddChildViewAt(frame_view_.get(), kFrameViewIndex);
    174     AddChildViewAt(client_view_, kClientViewIndex);
    175     if (overlay_view_)
    176       AddChildView(overlay_view_);
    177   }
    178 }
    179 
    180 void NonClientView::GetAccessibleState(ui::AccessibleViewState* state) {
    181   state->role = ui::AccessibilityTypes::ROLE_CLIENT;
    182   state->name = accessible_name_;
    183 }
    184 
    185 const char* NonClientView::GetClassName() const {
    186   return kViewClassName;
    187 }
    188 
    189 views::View* NonClientView::GetEventHandlerForRect(const gfx::Rect& rect) {
    190   if (!views::UsePointBasedTargeting(rect))
    191     return View::GetEventHandlerForRect(rect);
    192 
    193   // Because of the z-ordering of our child views (the client view is positioned
    194   // over the non-client frame view, if the client view ever overlaps the frame
    195   // view visually (as it does for the browser window), then it will eat
    196   // events for the window controls. We override this method here so that we can
    197   // detect this condition and re-route the events to the non-client frame view.
    198   // The assumption is that the frame view's implementation of HitTest will only
    199   // return true for area not occupied by the client view.
    200   if (frame_view_->parent() == this) {
    201     // During the reset of the frame_view_ it's possible to be in this code
    202     // after it's been removed from the view hierarchy but before it's been
    203     // removed from the NonClientView.
    204     gfx::RectF rect_in_child_coords_f(rect);
    205     View::ConvertRectToTarget(this, frame_view_.get(), &rect_in_child_coords_f);
    206     gfx::Rect rect_in_child_coords = gfx::ToEnclosingRect(
    207         rect_in_child_coords_f);
    208     if (frame_view_->HitTestRect(rect_in_child_coords))
    209       return frame_view_->GetEventHandlerForRect(rect_in_child_coords);
    210   }
    211 
    212   return View::GetEventHandlerForRect(rect);
    213 }
    214 
    215 views::View* NonClientView::GetTooltipHandlerForPoint(const gfx::Point& point) {
    216   // The same logic as for |GetEventHandlerForRect()| applies here.
    217   if (frame_view_->parent() == this) {
    218     // During the reset of the frame_view_ it's possible to be in this code
    219     // after it's been removed from the view hierarchy but before it's been
    220     // removed from the NonClientView.
    221     gfx::Point point_in_child_coords(point);
    222     View::ConvertPointToTarget(this, frame_view_.get(), &point_in_child_coords);
    223     views::View* handler =
    224         frame_view_->GetTooltipHandlerForPoint(point_in_child_coords);
    225     if (handler)
    226       return handler;
    227   }
    228 
    229   return View::GetTooltipHandlerForPoint(point);
    230 }
    231 
    232 ////////////////////////////////////////////////////////////////////////////////
    233 // NonClientFrameView, public:
    234 
    235 void NonClientFrameView::SetInactiveRenderingDisabled(bool disable) {
    236   if (paint_as_active_ == disable)
    237     return;
    238 
    239   paint_as_active_ = disable;
    240   ShouldPaintAsActiveChanged();
    241 }
    242 
    243 int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point,
    244                                                int top_resize_border_height,
    245                                                int resize_border_thickness,
    246                                                int top_resize_corner_height,
    247                                                int resize_corner_width,
    248                                                bool can_resize) {
    249   // Tricky: In XP, native behavior is to return HTTOPLEFT and HTTOPRIGHT for
    250   // a |resize_corner_size|-length strip of both the side and top borders, but
    251   // only to return HTBOTTOMLEFT/HTBOTTOMRIGHT along the bottom border + corner
    252   // (not the side border).  Vista goes further and doesn't return these on any
    253   // of the side borders.  We allow callers to match either behavior.
    254   int component;
    255   if (point.x() < resize_border_thickness) {
    256     if (point.y() < top_resize_corner_height)
    257       component = HTTOPLEFT;
    258     else if (point.y() >= (height() - resize_border_thickness))
    259       component = HTBOTTOMLEFT;
    260     else
    261       component = HTLEFT;
    262   } else if (point.x() >= (width() - resize_border_thickness)) {
    263     if (point.y() < top_resize_corner_height)
    264       component = HTTOPRIGHT;
    265     else if (point.y() >= (height() - resize_border_thickness))
    266       component = HTBOTTOMRIGHT;
    267     else
    268       component = HTRIGHT;
    269   } else if (point.y() < top_resize_border_height) {
    270     if (point.x() < resize_corner_width)
    271       component = HTTOPLEFT;
    272     else if (point.x() >= (width() - resize_corner_width))
    273       component = HTTOPRIGHT;
    274     else
    275       component = HTTOP;
    276   } else if (point.y() >= (height() - resize_border_thickness)) {
    277     if (point.x() < resize_corner_width)
    278       component = HTBOTTOMLEFT;
    279     else if (point.x() >= (width() - resize_corner_width))
    280       component = HTBOTTOMRIGHT;
    281     else
    282       component = HTBOTTOM;
    283   } else {
    284     return HTNOWHERE;
    285   }
    286 
    287   // If the window can't be resized, there are no resize boundaries, just
    288   // window borders.
    289   return can_resize ? component : HTBORDER;
    290 }
    291 
    292 ////////////////////////////////////////////////////////////////////////////////
    293 // NonClientFrameView, View overrides:
    294 
    295 bool NonClientFrameView::HitTestRect(const gfx::Rect& rect) const {
    296   // For the default case, we assume the non-client frame view never overlaps
    297   // the client view.
    298   return !GetWidget()->client_view()->bounds().Intersects(rect);
    299 }
    300 
    301 ////////////////////////////////////////////////////////////////////////////////
    302 // NonClientFrameView, protected:
    303 
    304 bool NonClientFrameView::ShouldPaintAsActive() const {
    305   return GetWidget()->IsActive() || paint_as_active_;
    306 }
    307 
    308 void NonClientFrameView::ShouldPaintAsActiveChanged() {
    309   SchedulePaint();
    310 }
    311 
    312 void NonClientFrameView::GetAccessibleState(ui::AccessibleViewState* state) {
    313   state->role = ui::AccessibilityTypes::ROLE_CLIENT;
    314 }
    315 
    316 const char* NonClientFrameView::GetClassName() const {
    317   return kViewClassName;
    318 }
    319 
    320 void NonClientFrameView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
    321   // Overridden to do nothing. The NonClientView manually calls Layout on the
    322   // FrameView when it is itself laid out, see comment in NonClientView::Layout.
    323 }
    324 
    325 }  // namespace views
    326