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