Home | History | Annotate | Download | only in caption_buttons
      1 // Copyright 2013 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 "ash/frame/caption_buttons/frame_caption_button_container_view.h"
      6 
      7 #include <cmath>
      8 #include <map>
      9 
     10 #include "ash/ash_switches.h"
     11 #include "ash/frame/caption_buttons/frame_caption_button.h"
     12 #include "ash/frame/caption_buttons/frame_size_button.h"
     13 #include "ash/metrics/user_metrics_recorder.h"
     14 #include "ash/shell.h"
     15 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
     16 #include "grit/ui_strings.h"  // Accessibility names
     17 #include "ui/base/hit_test.h"
     18 #include "ui/base/l10n/l10n_util.h"
     19 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     20 #include "ui/gfx/canvas.h"
     21 #include "ui/gfx/insets.h"
     22 #include "ui/gfx/point.h"
     23 #include "ui/views/widget/widget.h"
     24 #include "ui/views/widget/widget_delegate.h"
     25 
     26 namespace ash {
     27 
     28 namespace {
     29 
     30 // Converts |point| from |src| to |dst| and hittests against |dst|.
     31 bool ConvertPointToViewAndHitTest(const views::View* src,
     32                                   const views::View* dst,
     33                                   const gfx::Point& point) {
     34   gfx::Point converted(point);
     35   views::View::ConvertPointToTarget(src, dst, &converted);
     36   return dst->HitTestPoint(converted);
     37 }
     38 
     39 }  // namespace
     40 
     41 // static
     42 const char FrameCaptionButtonContainerView::kViewClassName[] =
     43     "FrameCaptionButtonContainerView";
     44 
     45 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
     46     views::Widget* frame,
     47     MinimizeAllowed minimize_allowed)
     48     : frame_(frame),
     49       minimize_button_(NULL),
     50       size_button_(NULL),
     51       close_button_(NULL) {
     52   // Insert the buttons left to right.
     53   minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
     54   minimize_button_->SetAccessibleName(
     55       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
     56   minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED);
     57   AddChildView(minimize_button_);
     58 
     59   size_button_ = new FrameSizeButton(this, frame, this);
     60   size_button_->SetAccessibleName(
     61       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
     62   UpdateSizeButtonVisibility();
     63   AddChildView(size_button_);
     64 
     65   close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE);
     66   close_button_->SetAccessibleName(
     67       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
     68   AddChildView(close_button_);
     69 }
     70 
     71 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {
     72 }
     73 
     74 void FrameCaptionButtonContainerView::SetButtonImages(
     75     CaptionButtonIcon icon,
     76     int icon_image_id,
     77     int inactive_icon_image_id,
     78     int hovered_background_image_id,
     79     int pressed_background_image_id) {
     80   button_icon_id_map_[icon] = ButtonIconIds(icon_image_id,
     81                                             inactive_icon_image_id,
     82                                             hovered_background_image_id,
     83                                             pressed_background_image_id);
     84   FrameCaptionButton* buttons[] = {
     85     minimize_button_, size_button_, close_button_
     86   };
     87   for (size_t i = 0; i < arraysize(buttons); ++i) {
     88     if (buttons[i]->icon() == icon) {
     89       buttons[i]->SetImages(icon,
     90                             FrameCaptionButton::ANIMATE_NO,
     91                             icon_image_id,
     92                             inactive_icon_image_id,
     93                             hovered_background_image_id,
     94                             pressed_background_image_id);
     95     }
     96   }
     97 }
     98 
     99 void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) {
    100   minimize_button_->set_paint_as_active(paint_as_active);
    101   size_button_->set_paint_as_active(paint_as_active);
    102   close_button_->set_paint_as_active(paint_as_active);
    103 }
    104 
    105 void FrameCaptionButtonContainerView::ResetWindowControls() {
    106   SetButtonsToNormal(ANIMATE_NO);
    107 }
    108 
    109 int FrameCaptionButtonContainerView::NonClientHitTest(
    110     const gfx::Point& point) const {
    111   if (close_button_->visible() &&
    112       ConvertPointToViewAndHitTest(this, close_button_, point)) {
    113     return HTCLOSE;
    114   } else if (size_button_->visible() &&
    115              ConvertPointToViewAndHitTest(this, size_button_, point)) {
    116     return HTMAXBUTTON;
    117   } else if (minimize_button_->visible() &&
    118              ConvertPointToViewAndHitTest(this, minimize_button_, point)) {
    119     return HTMINBUTTON;
    120   }
    121   return HTNOWHERE;
    122 }
    123 
    124 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() {
    125   size_button_->SetVisible(
    126       !Shell::GetInstance()->maximize_mode_controller()->
    127           IsMaximizeModeWindowManagerEnabled() &&
    128       frame_->widget_delegate()->CanMaximize());
    129 }
    130 
    131 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const {
    132   int width = 0;
    133   for (int i = 0; i < child_count(); ++i) {
    134     const views::View* child = child_at(i);
    135     if (child->visible())
    136       width += child_at(i)->GetPreferredSize().width();
    137   }
    138   return gfx::Size(width, close_button_->GetPreferredSize().height());
    139 }
    140 
    141 void FrameCaptionButtonContainerView::Layout() {
    142   int x = 0;
    143   for (int i = 0; i < child_count(); ++i) {
    144     views::View* child = child_at(i);
    145     if (!child->visible())
    146       continue;
    147 
    148     gfx::Size size = child->GetPreferredSize();
    149     child->SetBounds(x, 0, size.width(), size.height());
    150     x += size.width();
    151   }
    152 }
    153 
    154 const char* FrameCaptionButtonContainerView::GetClassName() const {
    155   return kViewClassName;
    156 }
    157 
    158 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button,
    159                                                     CaptionButtonIcon icon,
    160                                                     Animate animate) {
    161   // The early return is dependant on |animate| because callers use
    162   // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation
    163   // to the end.
    164   if (button->icon() == icon &&
    165       (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) {
    166     return;
    167   }
    168 
    169   FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ?
    170       FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO;
    171   std::map<CaptionButtonIcon, ButtonIconIds>::const_iterator it =
    172       button_icon_id_map_.find(icon);
    173   if (it != button_icon_id_map_.end()) {
    174     button->SetImages(icon,
    175                       fcb_animate,
    176                       it->second.icon_image_id,
    177                       it->second.inactive_icon_image_id,
    178                       it->second.hovered_background_image_id,
    179                       it->second.pressed_background_image_id);
    180   }
    181 }
    182 
    183 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
    184                                                     const ui::Event& event) {
    185   // When shift-clicking, slow down animations for visual debugging.
    186   // We used to do this via an event filter that looked for the shift key being
    187   // pressed but this interfered with several normal keyboard shortcuts.
    188   scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode;
    189   if (event.IsShiftDown()) {
    190     slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode(
    191         ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
    192   }
    193 
    194   // Abort any animations of the button icons.
    195   SetButtonsToNormal(ANIMATE_NO);
    196 
    197   ash::UserMetricsAction action =
    198       ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
    199   if (sender == minimize_button_) {
    200     frame_->Minimize();
    201   } else if (sender == size_button_) {
    202     if (frame_->IsFullscreen()) {  // Can be clicked in immersive fullscreen.
    203       frame_->SetFullscreen(false);
    204       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
    205     } else if (frame_->IsMaximized()) {
    206       frame_->Restore();
    207       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
    208     } else {
    209       frame_->Maximize();
    210       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
    211     }
    212   } else if (sender == close_button_) {
    213     frame_->Close();
    214     action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
    215   } else {
    216     return;
    217   }
    218   ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action);
    219 }
    220 
    221 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
    222   return minimize_button_->visible();
    223 }
    224 
    225 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
    226   SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
    227       animate);
    228   minimize_button_->SetState(views::Button::STATE_NORMAL);
    229   size_button_->SetState(views::Button::STATE_NORMAL);
    230   close_button_->SetState(views::Button::STATE_NORMAL);
    231 }
    232 
    233 void FrameCaptionButtonContainerView::SetButtonIcons(
    234     CaptionButtonIcon minimize_button_icon,
    235     CaptionButtonIcon close_button_icon,
    236     Animate animate) {
    237   SetButtonIcon(minimize_button_, minimize_button_icon, animate);
    238   SetButtonIcon(close_button_, close_button_icon, animate);
    239 }
    240 
    241 const FrameCaptionButton* FrameCaptionButtonContainerView::GetButtonClosestTo(
    242     const gfx::Point& position_in_screen) const {
    243   // Since the buttons all have the same size, the closest button is the button
    244   // with the center point closest to |position_in_screen|.
    245   // TODO(pkotwicz): Make the caption buttons not overlap.
    246   gfx::Point position(position_in_screen);
    247   views::View::ConvertPointFromScreen(this, &position);
    248 
    249   FrameCaptionButton* buttons[] = {
    250     minimize_button_, size_button_, close_button_
    251   };
    252   int min_squared_distance = INT_MAX;
    253   FrameCaptionButton* closest_button = NULL;
    254   for (size_t i = 0; i < arraysize(buttons); ++i) {
    255     FrameCaptionButton* button = buttons[i];
    256     if (!button->visible())
    257       continue;
    258 
    259     gfx::Point center_point = button->GetLocalBounds().CenterPoint();
    260     views::View::ConvertPointToTarget(button, this, &center_point);
    261     int squared_distance = static_cast<int>(
    262         pow(static_cast<double>(position.x() - center_point.x()), 2) +
    263         pow(static_cast<double>(position.y() - center_point.y()), 2));
    264     if (squared_distance < min_squared_distance) {
    265       min_squared_distance = squared_distance;
    266       closest_button = button;
    267     }
    268   }
    269   return closest_button;
    270 }
    271 
    272 void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
    273     const FrameCaptionButton* to_hover,
    274     const FrameCaptionButton* to_press) {
    275   FrameCaptionButton* buttons[] = {
    276     minimize_button_, size_button_, close_button_
    277   };
    278   for (size_t i = 0; i < arraysize(buttons); ++i) {
    279     FrameCaptionButton* button = buttons[i];
    280     views::Button::ButtonState new_state = views::Button::STATE_NORMAL;
    281     if (button == to_hover)
    282       new_state = views::Button::STATE_HOVERED;
    283     else if (button == to_press)
    284       new_state = views::Button::STATE_PRESSED;
    285     button->SetState(new_state);
    286   }
    287 }
    288 
    289 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
    290     : icon_image_id(-1),
    291       inactive_icon_image_id(-1),
    292       hovered_background_image_id(-1),
    293       pressed_background_image_id(-1) {
    294 }
    295 
    296 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds(
    297     int icon_id,
    298     int inactive_icon_id,
    299     int hovered_background_id,
    300     int pressed_background_id)
    301     : icon_image_id(icon_id),
    302       inactive_icon_image_id(inactive_icon_id),
    303       hovered_background_image_id(hovered_background_id),
    304       pressed_background_image_id(pressed_background_id) {
    305 }
    306 
    307 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() {
    308 }
    309 
    310 }  // namespace ash
    311