OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 "chrome/browser/views/fullscreen_exit_bubble.h" |
| 6 |
| 7 #include "chrome/app/chrome_dll_resource.h" |
| 8 #include "chrome/common/l10n_util.h" |
| 9 #include "chrome/common/resource_bundle.h" |
| 10 #include "chrome/views/root_view.h" |
| 11 #include "grit/generated_resources.h" |
| 12 |
| 13 |
| 14 // FullscreenExitView ---------------------------------------------------------- |
| 15 |
| 16 class FullscreenExitBubble::FullscreenExitView : public views::View { |
| 17 public: |
| 18 FullscreenExitView(FullscreenExitBubble* bubble, |
| 19 views::WidgetWin* popup, |
| 20 const std::wstring& accelerator); |
| 21 virtual ~FullscreenExitView(); |
| 22 |
| 23 // views::View |
| 24 virtual gfx::Size GetPreferredSize(); |
| 25 |
| 26 private: |
| 27 static const int kPaddingPixels; // Number of pixels around all sides of link |
| 28 |
| 29 // views::View |
| 30 virtual void Layout(); |
| 31 virtual void Paint(ChromeCanvas* canvas); |
| 32 |
| 33 // Handle to the HWND that contains us. |
| 34 views::WidgetWin* popup_; |
| 35 |
| 36 // Clickable hint text to show in the bubble. |
| 37 views::Link link_; |
| 38 }; |
| 39 |
| 40 const int FullscreenExitBubble::FullscreenExitView::kPaddingPixels = 8; |
| 41 |
| 42 FullscreenExitBubble::FullscreenExitView::FullscreenExitView( |
| 43 FullscreenExitBubble* bubble, |
| 44 views::WidgetWin* popup, |
| 45 const std::wstring& accelerator) |
| 46 : popup_(popup) { |
| 47 AddChildView(&link_); |
| 48 link_.SetParentOwned(false); |
| 49 link_.SetText(l10n_util::GetStringF(IDS_EXIT_FULLSCREEN_MODE, accelerator)); |
| 50 link_.SetController(bubble); |
| 51 link_.SetFont(ResourceBundle::GetSharedInstance().GetFont( |
| 52 ResourceBundle::LargeFont)); |
| 53 link_.SetNormalColor(SK_ColorWHITE); |
| 54 link_.SetHighlightedColor(SK_ColorWHITE); |
| 55 } |
| 56 |
| 57 FullscreenExitBubble::FullscreenExitView::~FullscreenExitView() { |
| 58 } |
| 59 |
| 60 gfx::Size FullscreenExitBubble::FullscreenExitView::GetPreferredSize() { |
| 61 gfx::Size preferred_size(link_.GetPreferredSize()); |
| 62 preferred_size.Enlarge(kPaddingPixels * 2, kPaddingPixels * 2); |
| 63 return preferred_size; |
| 64 } |
| 65 |
| 66 void FullscreenExitBubble::FullscreenExitView::Layout() { |
| 67 gfx::Size link_preferred_size(link_.GetPreferredSize()); |
| 68 link_.SetBounds(kPaddingPixels, |
| 69 height() - kPaddingPixels - link_preferred_size.height(), |
| 70 link_preferred_size.width(), link_preferred_size.height()); |
| 71 } |
| 72 |
| 73 void FullscreenExitBubble::FullscreenExitView::Paint(ChromeCanvas* canvas) { |
| 74 // Create a round-bottomed rect to fill the whole View. |
| 75 CRect parent_rect; |
| 76 SkRect rect; |
| 77 SkScalar padding = SkIntToScalar(kPaddingPixels); |
| 78 // The "-padding" top coordinate ensures that the rect is always tall enough |
| 79 // to contain the complete rounded corner radius. If we set this to 0, as the |
| 80 // popup slides offscreen (in reality, squishes to 0 height), the corners will |
| 81 // flatten out as the height becomes less than the corner radius. |
| 82 rect.set(0, -padding, SkIntToScalar(width()), SkIntToScalar(height())); |
| 83 SkScalar rad[8] = { 0, 0, 0, 0, padding, padding, padding, padding }; |
| 84 SkPath path; |
| 85 path.addRoundRect(rect, rad, SkPath::kCW_Direction); |
| 86 |
| 87 // Fill it black. |
| 88 SkPaint paint; |
| 89 paint.setStyle(SkPaint::kFill_Style); |
| 90 paint.setFlags(SkPaint::kAntiAlias_Flag); |
| 91 paint.setColor(SK_ColorBLACK); |
| 92 canvas->drawPath(path, paint); |
| 93 } |
| 94 |
| 95 |
| 96 // FullscreenExitBubble -------------------------------------------------------- |
| 97 |
| 98 const double FullscreenExitBubble::kOpacity = 0.7; |
| 99 const int FullscreenExitBubble::kInitialDelayMs = 2300; |
| 100 const int FullscreenExitBubble::kPositionCheckHz = 10; |
| 101 const int FullscreenExitBubble::kSlideInRegionHeightPx = 4; |
| 102 const int FullscreenExitBubble::kSlideInDurationMs = 350; |
| 103 const int FullscreenExitBubble::kSlideOutDurationMs = 700; |
| 104 |
| 105 FullscreenExitBubble::FullscreenExitBubble( |
| 106 views::Widget* frame, |
| 107 CommandUpdater::CommandUpdaterDelegate* delegate) |
| 108 : root_view_(frame->GetRootView()), |
| 109 delegate_(delegate), |
| 110 popup_(new views::WidgetWin()), |
| 111 size_animation_(new SlideAnimation(this)) { |
| 112 size_animation_->Reset(1); |
| 113 |
| 114 // Create the contents view. |
| 115 views::Accelerator accelerator(0, false, false, false); |
| 116 bool got_accelerator = frame->GetAccelerator(IDC_FULLSCREEN, &accelerator); |
| 117 DCHECK(got_accelerator); |
| 118 view_ = new FullscreenExitView(this, popup_, accelerator.GetShortcutText()); |
| 119 |
| 120 // Initialize the popup. |
| 121 popup_->set_delete_on_destroy(false); |
| 122 popup_->set_window_style(WS_POPUP); |
| 123 popup_->set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW | |
| 124 l10n_util::GetExtendedTooltipStyles()); |
| 125 popup_->SetLayeredAlpha(static_cast<int>(0xff * kOpacity)); |
| 126 popup_->Init(frame->GetHWND(), GetPopupRect(false), false); |
| 127 popup_->SetContentsView(view_); |
| 128 popup_->Show(); |
| 129 |
| 130 // Start the initial delay timer. |
| 131 initial_delay_.Start(base::TimeDelta::FromMilliseconds(kInitialDelayMs), this, |
| 132 &FullscreenExitBubble::AfterInitialDelay); |
| 133 } |
| 134 |
| 135 FullscreenExitBubble::~FullscreenExitBubble() { |
| 136 // This is tricky. We may be in an ATL message handler stack, in which case |
| 137 // the popup cannot be deleted yet. We also can't blindly use |
| 138 // set_delete_on_destroy(true) on the popup to delete it when it closes, |
| 139 // because if the user closed the last tab while in fullscreen mode, Windows |
| 140 // has already destroyed the popup HWND by the time we get here, and thus |
| 141 // either the popup will already have been deleted (if we set this in our |
| 142 // constructor) or the popup will never get another OnFinalMessage() call (if |
| 143 // not, as currently). So instead, we tell the popup to synchronously hide, |
| 144 // and then asynchronously close and delete itself. |
| 145 popup_->Close(); |
| 146 MessageLoop::current()->DeleteSoon(FROM_HERE, popup_); |
| 147 } |
| 148 |
| 149 void FullscreenExitBubble::LinkActivated(views::Link* source, int event_flags) { |
| 150 delegate_->ExecuteCommand(IDC_FULLSCREEN); |
| 151 } |
| 152 |
| 153 void FullscreenExitBubble::AnimationProgressed( |
| 154 const Animation* animation) { |
| 155 gfx::Rect popup_rect(GetPopupRect(false)); |
| 156 if (popup_rect.IsEmpty()) { |
| 157 popup_->Hide(); |
| 158 } else { |
| 159 popup_->MoveWindow(popup_rect.x(), popup_rect.y(), popup_rect.width(), |
| 160 popup_rect.height()); |
| 161 popup_->Show(); |
| 162 } |
| 163 } |
| 164 void FullscreenExitBubble::AnimationEnded( |
| 165 const Animation* animation) { |
| 166 AnimationProgressed(animation); |
| 167 } |
| 168 |
| 169 void FullscreenExitBubble::AfterInitialDelay() { |
| 170 // Check the mouse position immediately and every 50 ms afterwards. |
| 171 CheckMousePosition(); |
| 172 mouse_position_checker_.Start( |
| 173 base::TimeDelta::FromMilliseconds(1000 / kPositionCheckHz), this, |
| 174 &FullscreenExitBubble::CheckMousePosition); |
| 175 } |
| 176 |
| 177 void FullscreenExitBubble::CheckMousePosition() { |
| 178 // Desired behavior: |
| 179 // |
| 180 // +------------+-----------------------------+------------+ |
| 181 // | _ _ _ _ | Exit full screen mode (F11) | _ _ _ _ | Slide-in region |
| 182 // | _ _ _ _ \_____________________________/ _ _ _ _ | Neutral region |
| 183 // | | Slide-out region |
| 184 // : : |
| 185 // |
| 186 // * If the mouse is in the slide-in region, we show the popup. |
| 187 // * If the mouse is in the slide-out region, we hide the popup. |
| 188 // * If the mouse is in the neutral region, we do nothing, except if the popup |
| 189 // is currently sliding out, in which case we show it again. This |
| 190 // facilitates users correcting us if they try to mouse horizontally towards |
| 191 // the popup and unintentionally drop too low. |
| 192 |
| 193 POINT cursor_pos; |
| 194 GetCursorPos(&cursor_pos); |
| 195 gfx::Point transformed_pos(cursor_pos); |
| 196 views::View::ConvertPointToView(NULL, root_view_, &transformed_pos); |
| 197 gfx::Rect trigger_rect(GetPopupRect(true)); |
| 198 if (!root_view_->HitTest(transformed_pos) || |
| 199 (cursor_pos.y >= trigger_rect.bottom())) { |
| 200 size_animation_->SetSlideDuration(kSlideOutDurationMs); |
| 201 size_animation_->Hide(); |
| 202 } else if ((cursor_pos.y < kSlideInRegionHeightPx) || |
| 203 (size_animation_->GetCurrentValue() != 0)) { |
| 204 size_animation_->SetSlideDuration(kSlideInDurationMs); |
| 205 size_animation_->Show(); |
| 206 } |
| 207 } |
| 208 |
| 209 gfx::Rect FullscreenExitBubble::GetPopupRect( |
| 210 bool ignore_animation_state) const { |
| 211 gfx::Size size(view_->GetPreferredSize()); |
| 212 if (!ignore_animation_state) { |
| 213 size.set_height(static_cast<int>(static_cast<double>(size.height()) * |
| 214 size_animation_->GetCurrentValue())); |
| 215 } |
| 216 gfx::Point origin((root_view_->width() - size.width()) / 2, 0); |
| 217 views::View::ConvertPointToScreen(root_view_, &origin); |
| 218 return gfx::Rect(origin, size); |
| 219 } |
OLD | NEW |