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 const int WindowSelectorItem::kFadeInMilliseconds = 80;
     77 
     78 // Opacity for dimmed items.
     79 static const float kDimmedItemOpacity = 0.5f;
     80 
     81 WindowSelectorItem::WindowSelectorItem()
     82     : dimmed_(false),
     83       root_window_(NULL),
     84       in_bounds_update_(false),
     85       window_label_view_(NULL) {
     86 }
     87 
     88 WindowSelectorItem::~WindowSelectorItem() {
     89 }
     90 
     91 void WindowSelectorItem::RemoveWindow(const aura::Window* window) {
     92   // If empty WindowSelectorItem will be destroyed immediately after this by
     93   // its owner.
     94   if (empty())
     95     return;
     96   window_label_.reset();
     97   UpdateWindowLabels(target_bounds_, root_window_, false);
     98   UpdateCloseButtonBounds(root_window_, false);
     99 }
    100 
    101 void WindowSelectorItem::SetBounds(aura::Window* root_window,
    102                                    const gfx::Rect& target_bounds,
    103                                    bool animate) {
    104   if (in_bounds_update_)
    105     return;
    106   base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
    107   root_window_ = root_window;
    108   target_bounds_ = target_bounds;
    109 
    110   // Set the bounds of the transparent window handler to cover the entire
    111   // bounding box area.
    112   if (!activate_window_button_) {
    113     activate_window_button_.reset(
    114         new TransparentActivateWindowButton(SelectionWindow()));
    115   }
    116   activate_window_button_->SetBounds(target_bounds);
    117 
    118   UpdateWindowLabels(target_bounds, root_window, animate);
    119 
    120   gfx::Rect inset_bounds(target_bounds);
    121   inset_bounds.Inset(kWindowMargin, kWindowMargin);
    122   SetItemBounds(root_window, inset_bounds, animate);
    123   UpdateCloseButtonBounds(root_window, animate);
    124 }
    125 
    126 void WindowSelectorItem::RecomputeWindowTransforms() {
    127   if (in_bounds_update_ || target_bounds_.IsEmpty())
    128     return;
    129   DCHECK(root_window_);
    130   base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
    131   gfx::Rect inset_bounds(target_bounds_);
    132   inset_bounds.Inset(kWindowMargin, kWindowMargin);
    133   SetItemBounds(root_window_, inset_bounds, false);
    134   UpdateCloseButtonBounds(root_window_, false);
    135 }
    136 
    137 void WindowSelectorItem::SendFocusAlert() const {
    138   activate_window_button_->SendFocusAlert();
    139 }
    140 
    141 void WindowSelectorItem::SetDimmed(bool dimmed) {
    142   dimmed_ = dimmed;
    143   SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
    144 }
    145 
    146 void WindowSelectorItem::ButtonPressed(views::Button* sender,
    147                                        const ui::Event& event) {
    148   views::Widget::GetWidgetForNativeView(SelectionWindow())->Close();
    149 }
    150 
    151 void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
    152   // TODO(flackr): Maybe add the new title to a vector of titles so that we can
    153   // filter any of the titles the window had while in the overview session.
    154   if (window == SelectionWindow())
    155     window_label_view_->SetText(window->title());
    156 }
    157 
    158 void WindowSelectorItem::UpdateCloseButtonBounds(aura::Window* root_window,
    159                                                  bool animate) {
    160   gfx::RectF align_bounds(SelectionWindow()->layer()->bounds());
    161   gfx::Transform window_transform;
    162   window_transform.Translate(align_bounds.x(), align_bounds.y());
    163   window_transform.PreconcatTransform(SelectionWindow()->layer()->
    164                                           GetTargetTransform());
    165   window_transform.Translate(-align_bounds.x(), -align_bounds.y());
    166   window_transform.TransformRect(&align_bounds);
    167   gfx::Rect target_bounds = ToEnclosingRect(align_bounds);
    168 
    169   gfx::Transform close_button_transform;
    170   close_button_transform.Translate(target_bounds.right(), target_bounds.y());
    171 
    172   // If the root window has changed, force the close button to be recreated
    173   // and faded in on the new root window.
    174   if (close_button_ &&
    175       close_button_->GetNativeWindow()->GetRootWindow() != root_window) {
    176     close_button_.reset();
    177   }
    178 
    179   if (!close_button_) {
    180     close_button_.reset(CreateCloseWindowButton(root_window, this));
    181     gfx::Rect close_button_rect(close_button_->GetNativeWindow()->bounds());
    182     // Align the center of the button with position (0, 0) so that the
    183     // translate transform does not need to take the button dimensions into
    184     // account.
    185     close_button_rect.set_x(-close_button_rect.width() / 2);
    186     close_button_rect.set_y(-close_button_rect.height() / 2);
    187     close_button_->GetNativeWindow()->SetBounds(close_button_rect);
    188     close_button_->GetNativeWindow()->SetTransform(close_button_transform);
    189     // The close button is initialized when entering overview, fade the button
    190     // in after the window should be in place.
    191     ui::Layer* layer = close_button_->GetNativeWindow()->layer();
    192     layer->SetOpacity(0);
    193     layer->GetAnimator()->StopAnimating();
    194     layer->GetAnimator()->SchedulePauseForProperties(
    195         base::TimeDelta::FromMilliseconds(
    196             ScopedTransformOverviewWindow::kTransitionMilliseconds),
    197         ui::LayerAnimationElement::OPACITY);
    198     {
    199       ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
    200       settings.SetPreemptionStrategy(
    201           ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
    202       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    203           WindowSelectorItem::kFadeInMilliseconds));
    204       layer->SetOpacity(1);
    205     }
    206   } else {
    207     if (animate) {
    208       ui::ScopedLayerAnimationSettings settings(
    209           close_button_->GetNativeWindow()->layer()->GetAnimator());
    210       settings.SetPreemptionStrategy(
    211           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    212       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    213           ScopedTransformOverviewWindow::kTransitionMilliseconds));
    214       close_button_->GetNativeWindow()->SetTransform(close_button_transform);
    215     } else {
    216       close_button_->GetNativeWindow()->SetTransform(close_button_transform);
    217     }
    218   }
    219 }
    220 
    221 void WindowSelectorItem::SetOpacity(float opacity) {
    222   window_label_->GetNativeWindow()->layer()->SetOpacity(opacity);
    223   close_button_->GetNativeWindow()->layer()->SetOpacity(opacity);
    224 }
    225 
    226 void WindowSelectorItem::UpdateWindowLabels(const gfx::Rect& window_bounds,
    227                                             aura::Window* root_window,
    228                                             bool animate) {
    229   gfx::Rect converted_bounds = ScreenUtil::ConvertRectFromScreen(root_window,
    230                                                                  window_bounds);
    231   gfx::Rect label_bounds(converted_bounds.x(),
    232                          converted_bounds.bottom(),
    233                          converted_bounds.width(),
    234                          0);
    235 
    236   // If the root window has changed, force the window label to be recreated
    237   // and faded in on the new root window.
    238   if (window_label_ &&
    239       window_label_->GetNativeWindow()->GetRootWindow() != root_window) {
    240     window_label_.reset();
    241   }
    242 
    243   if (!window_label_) {
    244     CreateWindowLabel(SelectionWindow()->title());
    245     label_bounds.set_height(window_label_view_->GetPreferredSize().height());
    246     label_bounds.set_y(label_bounds.y() - window_label_view_->
    247                            GetPreferredSize().height());
    248     window_label_->GetNativeWindow()->SetBounds(label_bounds);
    249     ui::Layer* layer = window_label_->GetNativeWindow()->layer();
    250 
    251     layer->SetOpacity(0);
    252     layer->GetAnimator()->StopAnimating();
    253 
    254     layer->GetAnimator()->SchedulePauseForProperties(
    255         base::TimeDelta::FromMilliseconds(
    256             ScopedTransformOverviewWindow::kTransitionMilliseconds),
    257         ui::LayerAnimationElement::OPACITY);
    258 
    259     ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
    260     settings.SetPreemptionStrategy(
    261         ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
    262     settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    263         kFadeInMilliseconds));
    264     layer->SetOpacity(1);
    265   } else {
    266     label_bounds.set_height(window_label_->
    267                                 GetContentsView()->GetPreferredSize().height());
    268     label_bounds.set_y(label_bounds.y() - window_label_->
    269                            GetContentsView()->GetPreferredSize().height());
    270     if (animate) {
    271       ui::ScopedLayerAnimationSettings settings(
    272           window_label_->GetNativeWindow()->layer()->GetAnimator());
    273       settings.SetPreemptionStrategy(
    274           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    275       settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    276           ScopedTransformOverviewWindow::kTransitionMilliseconds));
    277       window_label_->GetNativeWindow()->SetBounds(label_bounds);
    278     } else {
    279       window_label_->GetNativeWindow()->SetBounds(label_bounds);
    280     }
    281   }
    282 }
    283 
    284 void WindowSelectorItem::CreateWindowLabel(const base::string16& title) {
    285   window_label_.reset(new views::Widget);
    286   views::Widget::InitParams params;
    287   params.type = views::Widget::InitParams::TYPE_POPUP;
    288   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    289   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
    290   params.parent =
    291       Shell::GetContainer(root_window_, ash::kShellWindowId_OverlayContainer);
    292   params.accept_events = false;
    293   params.visible_on_all_workspaces = true;
    294   window_label_->set_focus_on_creation(false);
    295   window_label_->Init(params);
    296   window_label_view_ = new views::Label;
    297   window_label_view_->SetEnabledColor(kLabelColor);
    298   window_label_view_->SetBackgroundColor(kLabelBackground);
    299   window_label_view_->SetShadows(gfx::ShadowValues(
    300       1,
    301       gfx::ShadowValue(
    302           gfx::Point(0, kVerticalShadowOffset), kShadowBlur, kLabelShadow)));
    303   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
    304   window_label_view_->SetFontList(
    305       bundle.GetFontList(ui::ResourceBundle::BoldFont));
    306   window_label_view_->SetText(title);
    307   views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kVertical,
    308                                                   0,
    309                                                   kVerticalLabelPadding,
    310                                                   0);
    311   window_label_view_->SetLayoutManager(layout);
    312   window_label_->SetContentsView(window_label_view_);
    313   window_label_->Show();
    314 }
    315 
    316 }  // namespace ash
    317