Home | History | Annotate | Download | only in overview
      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/wm/overview/window_selector_item.h"
      6 
      7 #include "ash/screen_util.h"
      8 #include "ash/shell.h"
      9 #include "ash/shell_window_ids.h"
     10 #include "ash/wm/overview/scoped_transform_overview_window.h"
     11 #include "ash/wm/overview/transparent_activate_window_button.h"
     12 #include "base/auto_reset.h"
     13 #include "grit/ash_resources.h"
     14 #include "ui/aura/window.h"
     15 #include "ui/base/resource/resource_bundle.h"
     16 #include "ui/compositor/scoped_layer_animation_settings.h"
     17 #include "ui/views/controls/button/image_button.h"
     18 #include "ui/views/controls/label.h"
     19 #include "ui/views/layout/box_layout.h"
     20 #include "ui/views/widget/widget.h"
     21 
     22 namespace ash {
     23 
     24 namespace {
     25 
     26 views::Widget* CreateCloseWindowButton(aura::Window* root_window,
     27                                        views::ButtonListener* listener) {
     28   views::Widget* widget = new views::Widget;
     29   views::Widget::InitParams params;
     30   params.type = views::Widget::InitParams::TYPE_POPUP;
     31   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     32   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     33   params.parent =
     34       Shell::GetContainer(root_window, ash::kShellWindowId_OverlayContainer);
     35   widget->set_focus_on_creation(false);
     36   widget->Init(params);
     37   views::ImageButton* button = new views::ImageButton(listener);
     38   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
     39   button->SetImage(views::CustomButton::STATE_NORMAL,
     40                    rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE));
     41   button->SetImage(views::CustomButton::STATE_HOVERED,
     42                    rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_H));
     43   button->SetImage(views::CustomButton::STATE_PRESSED,
     44                    rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE_P));
     45   widget->SetContentsView(button);
     46   widget->SetSize(rb.GetImageSkiaNamed(IDR_AURA_WINDOW_OVERVIEW_CLOSE)->size());
     47   widget->Show();
     48   return widget;
     49 }
     50 
     51 }  // namespace
     52 
     53 // In the conceptual overview table, the window margin is the space reserved
     54 // around the window within the cell. This margin does not overlap so the
     55 // closest distance between adjacent windows will be twice this amount.
     56 static const int kWindowMargin = 30;
     57 
     58 // Foreground label color.
     59 static const SkColor kLabelColor = SK_ColorWHITE;
     60 
     61 // Background label color.
     62 static const SkColor kLabelBackground = SK_ColorTRANSPARENT;
     63 
     64 // Label shadow color.
     65 static const SkColor kLabelShadow = 0xB0000000;
     66 
     67 // Vertical padding for the label, both over and beneath it.
     68 static const int kVerticalLabelPadding = 20;
     69 
     70 // Solid shadow length from the label
     71 static const int kVerticalShadowOffset = 1;
     72 
     73 // Amount of blur applied to the label shadow
     74 static const int kShadowBlur = 10;
     75 
     76 views::Widget* CreateWindowLabel(aura::Window* root_window,
     77                                  const base::string16 title) {
     78   views::Widget* widget = new views::Widget;
     79   views::Widget::InitParams params;
     80   params.type = views::Widget::InitParams::TYPE_POPUP;
     81   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     82   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     83   params.parent =
     84       Shell::GetContainer(root_window, ash::kShellWindowId_OverlayContainer);
     85   params.accept_events = false;
     86   params.visible_on_all_workspaces = true;
     87   widget->set_focus_on_creation(false);
     88   widget->Init(params);
     89   views::Label* label = new views::Label;
     90   label->SetEnabledColor(kLabelColor);
     91   label->SetBackgroundColor(kLabelBackground);
     92   label->set_shadows(gfx::ShadowValues(1, gfx::ShadowValue(
     93       gfx::Point(0, kVerticalShadowOffset), kShadowBlur, kLabelShadow)));
     94   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
     95   label->SetFontList(bundle.GetFontList(ui::ResourceBundle::BoldFont));
     96   label->SetText(title);
     97   views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kVertical,
     98                                                   0,
     99                                                   kVerticalLabelPadding,
    100                                                   0);
    101   label->SetLayoutManager(layout);
    102   widget->SetContentsView(label);
    103   widget->Show();
    104   return widget;
    105 }
    106 
    107 const int WindowSelectorItem::kFadeInMilliseconds = 80;
    108 
    109 WindowSelectorItem::WindowSelectorItem()
    110     : root_window_(NULL),
    111       in_bounds_update_(false) {
    112 }
    113 
    114 WindowSelectorItem::~WindowSelectorItem() {
    115 }
    116 
    117 void WindowSelectorItem::RemoveWindow(const aura::Window* window) {
    118   // If empty WindowSelectorItem will be destroyed immediately after this by
    119   // its owner.
    120   if (empty())
    121     return;
    122   window_label_.reset();
    123   UpdateWindowLabels(target_bounds_, root_window_, false);
    124   UpdateCloseButtonBounds(root_window_, false);
    125 }
    126 
    127 void WindowSelectorItem::SetBounds(aura::Window* root_window,
    128                                    const gfx::Rect& target_bounds,
    129                                    bool animate) {
    130   if (in_bounds_update_)
    131     return;
    132   base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
    133   root_window_ = root_window;
    134   target_bounds_ = target_bounds;
    135 
    136   // Set the bounds of the transparent window handler to cover the entire
    137   // bounding box area.
    138   if (!activate_window_button_) {
    139     activate_window_button_.reset(
    140         new TransparentActivateWindowButton(SelectionWindow()));
    141   }
    142   activate_window_button_->SetBounds(target_bounds);
    143 
    144   // TODO(nsatragno): Handle window title updates.
    145   UpdateWindowLabels(target_bounds, root_window, animate);
    146 
    147   gfx::Rect inset_bounds(target_bounds);
    148   inset_bounds.Inset(kWindowMargin, kWindowMargin);
    149   SetItemBounds(root_window, inset_bounds, animate);
    150   UpdateCloseButtonBounds(root_window, animate);
    151 }
    152 
    153 void WindowSelectorItem::RecomputeWindowTransforms() {
    154   if (in_bounds_update_ || target_bounds_.IsEmpty())
    155     return;
    156   DCHECK(root_window_);
    157   base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
    158   gfx::Rect inset_bounds(target_bounds_);
    159   inset_bounds.Inset(kWindowMargin, kWindowMargin);
    160   SetItemBounds(root_window_, inset_bounds, false);
    161   UpdateCloseButtonBounds(root_window_, false);
    162 }
    163 
    164 void WindowSelectorItem::SendFocusAlert() const {
    165   activate_window_button_->SendFocusAlert();
    166 }
    167 
    168 void WindowSelectorItem::ButtonPressed(views::Button* sender,
    169                                        const ui::Event& event) {
    170   views::Widget::GetWidgetForNativeView(SelectionWindow())->Close();
    171 }
    172 
    173 void WindowSelectorItem::UpdateCloseButtonBounds(aura::Window* root_window,
    174                                                  bool animate) {
    175   gfx::RectF align_bounds(SelectionWindow()->layer()->bounds());
    176   gfx::Transform window_transform;
    177   window_transform.Translate(align_bounds.x(), align_bounds.y());
    178   window_transform.PreconcatTransform(SelectionWindow()->layer()->
    179                                           GetTargetTransform());
    180   window_transform.Translate(-align_bounds.x(), -align_bounds.y());
    181   window_transform.TransformRect(&align_bounds);
    182   gfx::Rect target_bounds = ToEnclosingRect(align_bounds);
    183 
    184   gfx::Transform close_button_transform;
    185   close_button_transform.Translate(target_bounds.right(), target_bounds.y());
    186 
    187   // If the root window has changed, force the close button to be recreated
    188   // and faded in on the new root window.
    189   if (close_button_ &&
    190       close_button_->GetNativeWindow()->GetRootWindow() != root_window) {
    191     close_button_.reset();
    192   }
    193 
    194   if (!close_button_) {
    195     close_button_.reset(CreateCloseWindowButton(root_window, this));
    196     gfx::Rect close_button_rect(close_button_->GetNativeWindow()->bounds());
    197     // Align the center of the button with position (0, 0) so that the
    198     // translate transform does not need to take the button dimensions into
    199     // account.
    200     close_button_rect.set_x(-close_button_rect.width() / 2);
    201     close_button_rect.set_y(-close_button_rect.height() / 2);
    202     close_button_->GetNativeWindow()->SetBounds(close_button_rect);
    203     close_button_->GetNativeWindow()->SetTransform(close_button_transform);
    204     // The close button is initialized when entering overview, fade the button
    205     // in after the window should be in place.
    206     ui::Layer* layer = close_button_->GetNativeWindow()->layer();
    207     layer->SetOpacity(0);
    208     layer->GetAnimator()->StopAnimating();
    209     layer->GetAnimator()->SchedulePauseForProperties(
    210         base::TimeDelta::FromMilliseconds(
    211             ScopedTransformOverviewWindow::kTransitionMilliseconds),
    212         ui::LayerAnimationElement::OPACITY);
    213     {
    214       ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
    215       settings.SetPreemptionStrategy(
    216           ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
    217       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    218           WindowSelectorItem::kFadeInMilliseconds));
    219       layer->SetOpacity(1);
    220     }
    221   } else {
    222     if (animate) {
    223       ui::ScopedLayerAnimationSettings settings(
    224           close_button_->GetNativeWindow()->layer()->GetAnimator());
    225       settings.SetPreemptionStrategy(
    226           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    227       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    228           ScopedTransformOverviewWindow::kTransitionMilliseconds));
    229       close_button_->GetNativeWindow()->SetTransform(close_button_transform);
    230     } else {
    231       close_button_->GetNativeWindow()->SetTransform(close_button_transform);
    232     }
    233   }
    234 }
    235 
    236 void WindowSelectorItem::UpdateWindowLabels(const gfx::Rect& window_bounds,
    237                                             aura::Window* root_window,
    238                                             bool animate) {
    239   gfx::Rect converted_bounds = ScreenUtil::ConvertRectFromScreen(root_window,
    240                                                                  window_bounds);
    241   gfx::Rect label_bounds(converted_bounds.x(),
    242                          converted_bounds.bottom(),
    243                          converted_bounds.width(),
    244                          0);
    245 
    246   // If the root window has changed, force the window label to be recreated
    247   // and faded in on the new root window.
    248   if (window_label_ &&
    249       window_label_->GetNativeWindow()->GetRootWindow() != root_window) {
    250     window_label_.reset();
    251   }
    252 
    253   if (!window_label_) {
    254     window_label_.reset(CreateWindowLabel(root_window,
    255                                           SelectionWindow()->title()));
    256     label_bounds.set_height(window_label_->
    257                                 GetContentsView()->GetPreferredSize().height());
    258     label_bounds.set_y(label_bounds.y() - window_label_->
    259                            GetContentsView()->GetPreferredSize().height());
    260     window_label_->GetNativeWindow()->SetBounds(label_bounds);
    261     ui::Layer* layer = window_label_->GetNativeWindow()->layer();
    262 
    263     layer->SetOpacity(0);
    264     layer->GetAnimator()->StopAnimating();
    265 
    266     layer->GetAnimator()->SchedulePauseForProperties(
    267         base::TimeDelta::FromMilliseconds(
    268             ScopedTransformOverviewWindow::kTransitionMilliseconds),
    269         ui::LayerAnimationElement::OPACITY);
    270 
    271     ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
    272     settings.SetPreemptionStrategy(
    273         ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
    274     settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    275         kFadeInMilliseconds));
    276     layer->SetOpacity(1);
    277   } else {
    278     label_bounds.set_height(window_label_->
    279                                 GetContentsView()->GetPreferredSize().height());
    280     label_bounds.set_y(label_bounds.y() - window_label_->
    281                            GetContentsView()->GetPreferredSize().height());
    282     if (animate) {
    283       ui::ScopedLayerAnimationSettings settings(
    284           window_label_->GetNativeWindow()->layer()->GetAnimator());
    285       settings.SetPreemptionStrategy(
    286           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    287       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    288           ScopedTransformOverviewWindow::kTransitionMilliseconds));
    289       window_label_->GetNativeWindow()->SetBounds(label_bounds);
    290     } else {
    291       window_label_->GetNativeWindow()->SetBounds(label_bounds);
    292     }
    293   }
    294 }
    295 
    296 }  // namespace ash
    297