OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h" | 5 #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h" |
6 | 6 |
7 #include "base/i18n/rtl.h" | 7 #include "base/i18n/rtl.h" |
8 #include "base/strings/stringprintf.h" | 8 #include "base/strings/stringprintf.h" |
9 #include "chrome/browser/chrome_notification_types.h" | 9 #include "chrome/browser/chrome_notification_types.h" |
10 #include "chrome/browser/chrome_page_zoom.h" | 10 #include "chrome/browser/chrome_page_zoom.h" |
(...skipping 14 matching lines...) Expand all Loading... |
25 #include "ui/base/l10n/l10n_util.h" | 25 #include "ui/base/l10n/l10n_util.h" |
26 #include "ui/base/resource/resource_bundle.h" | 26 #include "ui/base/resource/resource_bundle.h" |
27 #include "ui/gfx/favicon_size.h" | 27 #include "ui/gfx/favicon_size.h" |
28 #include "ui/views/controls/button/image_button.h" | 28 #include "ui/views/controls/button/image_button.h" |
29 #include "ui/views/controls/button/label_button.h" | 29 #include "ui/views/controls/button/label_button.h" |
30 #include "ui/views/controls/separator.h" | 30 #include "ui/views/controls/separator.h" |
31 #include "ui/views/layout/grid_layout.h" | 31 #include "ui/views/layout/grid_layout.h" |
32 #include "ui/views/layout/layout_constants.h" | 32 #include "ui/views/layout/layout_constants.h" |
33 #include "ui/views/widget/widget.h" | 33 #include "ui/views/widget/widget.h" |
34 | 34 |
35 namespace { | |
36 | |
37 // The number of milliseconds the bubble should stay on the screen if it will | |
38 // close automatically. | |
39 const int kBubbleCloseDelay = 1500; | |
40 | |
41 // The bubble's padding from the screen edge, used in fullscreen. | |
42 const int kFullscreenPaddingEnd = 20; | |
43 | |
44 } // namespace | |
45 | |
46 // static | 35 // static |
47 ZoomBubbleView* ZoomBubbleView::zoom_bubble_ = NULL; | 36 ZoomBubbleView* ZoomBubbleView::zoom_bubble_ = NULL; |
48 | 37 |
49 // static | 38 // static |
50 void ZoomBubbleView::ShowBubble(content::WebContents* web_contents, | 39 void ZoomBubbleView::ShowBubble(content::WebContents* web_contents, |
51 bool auto_close) { | 40 bool auto_close) { |
52 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); | 41 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
53 DCHECK(browser && browser->window() && browser->fullscreen_controller()); | 42 DCHECK(browser && browser->window() && browser->fullscreen_controller()); |
54 | 43 |
55 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); | 44 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 const ZoomBubbleView* ZoomBubbleView::GetZoomBubbleForTest() { | 113 const ZoomBubbleView* ZoomBubbleView::GetZoomBubbleForTest() { |
125 return zoom_bubble_; | 114 return zoom_bubble_; |
126 } | 115 } |
127 | 116 |
128 ZoomBubbleView::ZoomBubbleView( | 117 ZoomBubbleView::ZoomBubbleView( |
129 views::View* anchor_view, | 118 views::View* anchor_view, |
130 content::WebContents* web_contents, | 119 content::WebContents* web_contents, |
131 bool auto_close, | 120 bool auto_close, |
132 ImmersiveModeController* immersive_mode_controller, | 121 ImmersiveModeController* immersive_mode_controller, |
133 FullscreenController* fullscreen_controller) | 122 FullscreenController* fullscreen_controller) |
134 : BubbleDelegateView(anchor_view, anchor_view ? | 123 : ManagedFullScreenBubbleDelegateView(anchor_view, web_contents), |
135 views::BubbleBorder::TOP_RIGHT : views::BubbleBorder::NONE), | |
136 image_button_(NULL), | 124 image_button_(NULL), |
137 label_(NULL), | 125 label_(NULL), |
138 web_contents_(web_contents), | 126 web_contents_(web_contents), |
139 auto_close_(auto_close), | 127 auto_close_(auto_close), |
140 immersive_mode_controller_(immersive_mode_controller) { | 128 immersive_mode_controller_(immersive_mode_controller) { |
141 // Compensate for built-in vertical padding in the anchor view's image. | 129 // Compensate for built-in vertical padding in the anchor view's image. |
142 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0)); | 130 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0)); |
143 set_notify_enter_exit_on_child(true); | 131 set_notify_enter_exit_on_child(true); |
144 | |
145 // Add observers to close the bubble if the fullscreen state or immersive | |
146 // fullscreen revealed state changes. | |
147 registrar_.Add(this, | |
148 chrome::NOTIFICATION_FULLSCREEN_CHANGED, | |
149 content::Source<FullscreenController>(fullscreen_controller)); | |
150 immersive_mode_controller_->AddObserver(this); | 132 immersive_mode_controller_->AddObserver(this); |
151 } | 133 } |
152 | 134 |
153 ZoomBubbleView::~ZoomBubbleView() { | 135 ZoomBubbleView::~ZoomBubbleView() { |
154 if (immersive_mode_controller_) | 136 if (immersive_mode_controller_) |
155 immersive_mode_controller_->RemoveObserver(this); | 137 immersive_mode_controller_->RemoveObserver(this); |
156 } | 138 } |
157 | 139 |
158 void ZoomBubbleView::AdjustForFullscreen(const gfx::Rect& screen_bounds) { | 140 void ZoomBubbleView::OnGestureEvent(ui::GestureEvent* event) { |
159 if (GetAnchorView()) | 141 if (!zoom_bubble_ || !zoom_bubble_->auto_close_ || |
| 142 event->type() != ui::ET_GESTURE_TAP) { |
160 return; | 143 return; |
| 144 } |
161 | 145 |
162 // TODO(dbeam): should RTL logic be done in views::BubbleDelegateView? | 146 auto_close_ = false; |
163 const size_t bubble_half_width = width() / 2; | 147 StopTimer(); |
164 const int x_pos = base::i18n::IsRTL() ? | 148 event->SetHandled(); |
165 screen_bounds.x() + bubble_half_width + kFullscreenPaddingEnd : | 149 } |
166 screen_bounds.right() - bubble_half_width - kFullscreenPaddingEnd; | 150 |
167 SetAnchorRect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0)); | 151 void ZoomBubbleView::OnMouseEntered(const ui::MouseEvent& event) { |
| 152 StopTimer(); |
| 153 } |
| 154 |
| 155 void ZoomBubbleView::OnMouseExited(const ui::MouseEvent& event) { |
| 156 StartTimerIfNecessary(); |
| 157 } |
| 158 |
| 159 void ZoomBubbleView::Init() { |
| 160 // Set up the layout of the zoom bubble. A grid layout is used because |
| 161 // sometimes an extension icon is shown next to the zoom label. |
| 162 views::GridLayout* grid_layout = new views::GridLayout(this); |
| 163 SetLayoutManager(grid_layout); |
| 164 views::ColumnSet* columns = grid_layout->AddColumnSet(0); |
| 165 // First row. |
| 166 if (extension_info_.icon_image) { |
| 167 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 2, |
| 168 views::GridLayout::USE_PREF, 0, 0); |
| 169 } |
| 170 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| 171 views::GridLayout::USE_PREF, 0, 0); |
| 172 grid_layout->StartRow(0, 0); |
| 173 |
| 174 // If this zoom change was initiated by an extension, that extension will be |
| 175 // attributed by showing its icon in the zoom bubble. |
| 176 if (extension_info_.icon_image) { |
| 177 image_button_ = new views::ImageButton(this); |
| 178 image_button_->SetTooltipText( |
| 179 l10n_util::GetStringFUTF16(IDS_TOOLTIP_ZOOM_EXTENSION_ICON, |
| 180 base::UTF8ToUTF16(extension_info_.name))); |
| 181 image_button_->SetImage(views::Button::STATE_NORMAL, |
| 182 &extension_info_.icon_image->image_skia()); |
| 183 grid_layout->AddView(image_button_); |
| 184 } |
| 185 |
| 186 // Add zoom label with the new zoom percent. |
| 187 ui_zoom::ZoomController* zoom_controller = |
| 188 ui_zoom::ZoomController::FromWebContents(web_contents_); |
| 189 int zoom_percent = zoom_controller->GetZoomPercent(); |
| 190 label_ = new views::Label( |
| 191 l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent)); |
| 192 label_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( |
| 193 ui::ResourceBundle::MediumFont)); |
| 194 grid_layout->AddView(label_); |
| 195 |
| 196 // Second row. |
| 197 grid_layout->AddPaddingRow(0, 8); |
| 198 columns = grid_layout->AddColumnSet(1); |
| 199 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| 200 views::GridLayout::USE_PREF, 0, 0); |
| 201 grid_layout->StartRow(0, 1); |
| 202 |
| 203 // Add "Reset to Default" button. |
| 204 views::LabelButton* set_default_button = new views::LabelButton( |
| 205 this, l10n_util::GetStringUTF16(IDS_ZOOM_SET_DEFAULT)); |
| 206 set_default_button->SetStyle(views::Button::STYLE_BUTTON); |
| 207 set_default_button->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| 208 grid_layout->AddView(set_default_button); |
| 209 |
| 210 StartTimerIfNecessary(); |
| 211 } |
| 212 |
| 213 void ZoomBubbleView::WindowClosing() { |
| 214 // |zoom_bubble_| can be a new bubble by this point (as Close(); doesn't |
| 215 // call this right away). Only set to NULL when it's this bubble. |
| 216 if (zoom_bubble_ == this) |
| 217 zoom_bubble_ = NULL; |
| 218 } |
| 219 |
| 220 void ZoomBubbleView::Close() { |
| 221 // Widget's Close() is async, but we don't want to use zoom_bubble_ after |
| 222 // this. Additionally web_contents_ may have been destroyed. |
| 223 zoom_bubble_ = NULL; |
| 224 web_contents_ = NULL; |
| 225 ManagedFullScreenBubbleDelegateView::Close(); |
| 226 } |
| 227 |
| 228 void ZoomBubbleView::ButtonPressed(views::Button* sender, |
| 229 const ui::Event& event) { |
| 230 if (sender == image_button_) { |
| 231 DCHECK(extension_info_.icon_image) << "Invalid button press."; |
| 232 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); |
| 233 chrome::AddSelectedTabWithURL( |
| 234 browser, GURL(base::StringPrintf("chrome://extensions?id=%s", |
| 235 extension_info_.id.c_str())), |
| 236 ui::PAGE_TRANSITION_FROM_API); |
| 237 } else { |
| 238 chrome_page_zoom::Zoom(web_contents_, content::PAGE_ZOOM_RESET); |
| 239 } |
| 240 } |
| 241 |
| 242 void ZoomBubbleView::OnImmersiveRevealStarted() { |
| 243 CloseBubble(); |
| 244 } |
| 245 |
| 246 void ZoomBubbleView::OnImmersiveModeControllerDestroyed() { |
| 247 immersive_mode_controller_ = NULL; |
| 248 } |
| 249 |
| 250 void ZoomBubbleView::OnExtensionIconImageChanged( |
| 251 extensions::IconImage* /* image */) { |
| 252 image_button_->SetImage(views::Button::STATE_NORMAL, |
| 253 &extension_info_.icon_image->image_skia()); |
| 254 image_button_->SchedulePaint(); |
168 } | 255 } |
169 | 256 |
170 void ZoomBubbleView::Refresh() { | 257 void ZoomBubbleView::Refresh() { |
171 ui_zoom::ZoomController* zoom_controller = | 258 ui_zoom::ZoomController* zoom_controller = |
172 ui_zoom::ZoomController::FromWebContents(web_contents_); | 259 ui_zoom::ZoomController::FromWebContents(web_contents_); |
173 int zoom_percent = zoom_controller->GetZoomPercent(); | 260 int zoom_percent = zoom_controller->GetZoomPercent(); |
174 label_->SetText( | 261 label_->SetText( |
175 l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent)); | 262 l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent)); |
176 StartTimerIfNecessary(); | 263 StartTimerIfNecessary(); |
177 } | 264 } |
178 | 265 |
179 void ZoomBubbleView::Close() { | |
180 // Widget's Close() is async, but we don't want to use zoom_bubble_ after | |
181 // this. Additionally web_contents_ may have been destroyed. | |
182 zoom_bubble_ = NULL; | |
183 web_contents_ = NULL; | |
184 GetWidget()->Close(); | |
185 } | |
186 | |
187 void ZoomBubbleView::SetExtensionInfo(const extensions::Extension* extension) { | 266 void ZoomBubbleView::SetExtensionInfo(const extensions::Extension* extension) { |
188 DCHECK(extension); | 267 DCHECK(extension); |
189 extension_info_.id = extension->id(); | 268 extension_info_.id = extension->id(); |
190 extension_info_.name = extension->name(); | 269 extension_info_.name = extension->name(); |
191 | 270 |
192 ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 271 ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
193 const gfx::ImageSkia& default_extension_icon_image = | 272 const gfx::ImageSkia& default_extension_icon_image = |
194 *rb.GetImageSkiaNamed(IDR_EXTENSIONS_FAVICON); | 273 *rb.GetImageSkiaNamed(IDR_EXTENSIONS_FAVICON); |
195 int icon_size = gfx::kFaviconSize; | 274 int icon_size = gfx::kFaviconSize; |
196 | 275 |
(...skipping 29 matching lines...) Expand all Loading... |
226 icon_size, | 305 icon_size, |
227 default_extension_icon_image, | 306 default_extension_icon_image, |
228 this)); | 307 this)); |
229 } | 308 } |
230 | 309 |
231 void ZoomBubbleView::StartTimerIfNecessary() { | 310 void ZoomBubbleView::StartTimerIfNecessary() { |
232 if (auto_close_) { | 311 if (auto_close_) { |
233 if (timer_.IsRunning()) { | 312 if (timer_.IsRunning()) { |
234 timer_.Reset(); | 313 timer_.Reset(); |
235 } else { | 314 } else { |
| 315 // The number of milliseconds the bubble should stay on the screen if it |
| 316 // will close automatically. |
| 317 const int kBubbleCloseDelay = 1500; |
236 timer_.Start( | 318 timer_.Start( |
237 FROM_HERE, | 319 FROM_HERE, |
238 base::TimeDelta::FromMilliseconds(kBubbleCloseDelay), | 320 base::TimeDelta::FromMilliseconds(kBubbleCloseDelay), |
239 this, | 321 this, |
240 &ZoomBubbleView::Close); | 322 &ZoomBubbleView::Close); |
241 } | 323 } |
242 } | 324 } |
243 } | 325 } |
244 | 326 |
245 void ZoomBubbleView::StopTimer() { | 327 void ZoomBubbleView::StopTimer() { |
246 timer_.Stop(); | 328 timer_.Stop(); |
247 } | 329 } |
248 | 330 |
249 void ZoomBubbleView::OnExtensionIconImageChanged( | |
250 extensions::IconImage* /* image */) { | |
251 image_button_->SetImage(views::Button::STATE_NORMAL, | |
252 &extension_info_.icon_image->image_skia()); | |
253 image_button_->SchedulePaint(); | |
254 } | |
255 | |
256 void ZoomBubbleView::OnMouseEntered(const ui::MouseEvent& event) { | |
257 StopTimer(); | |
258 } | |
259 | |
260 void ZoomBubbleView::OnMouseExited(const ui::MouseEvent& event) { | |
261 StartTimerIfNecessary(); | |
262 } | |
263 | |
264 void ZoomBubbleView::OnGestureEvent(ui::GestureEvent* event) { | |
265 if (!zoom_bubble_ || !zoom_bubble_->auto_close_ || | |
266 event->type() != ui::ET_GESTURE_TAP) { | |
267 return; | |
268 } | |
269 | |
270 auto_close_ = false; | |
271 StopTimer(); | |
272 event->SetHandled(); | |
273 } | |
274 | |
275 void ZoomBubbleView::ButtonPressed(views::Button* sender, | |
276 const ui::Event& event) { | |
277 if (sender == image_button_) { | |
278 DCHECK(extension_info_.icon_image) << "Invalid button press."; | |
279 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); | |
280 chrome::AddSelectedTabWithURL( | |
281 browser, | |
282 GURL(base::StringPrintf("chrome://extensions?id=%s", | |
283 extension_info_.id.c_str())), | |
284 ui::PAGE_TRANSITION_FROM_API); | |
285 } else { | |
286 chrome_page_zoom::Zoom(web_contents_, content::PAGE_ZOOM_RESET); | |
287 } | |
288 } | |
289 | |
290 void ZoomBubbleView::Init() { | |
291 // Set up the layout of the zoom bubble. A grid layout is used because | |
292 // sometimes an extension icon is shown next to the zoom label. | |
293 views::GridLayout* grid_layout = new views::GridLayout(this); | |
294 SetLayoutManager(grid_layout); | |
295 views::ColumnSet* columns = grid_layout->AddColumnSet(0); | |
296 // First row. | |
297 if (extension_info_.icon_image) { | |
298 columns->AddColumn(views::GridLayout::CENTER,views::GridLayout::CENTER, 2, | |
299 views::GridLayout::USE_PREF, 0, 0); | |
300 } | |
301 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, | |
302 views::GridLayout::USE_PREF, 0, 0); | |
303 grid_layout->StartRow(0, 0); | |
304 | |
305 // If this zoom change was initiated by an extension, that extension will be | |
306 // attributed by showing its icon in the zoom bubble. | |
307 if (extension_info_.icon_image) { | |
308 image_button_ = new views::ImageButton(this); | |
309 image_button_->SetTooltipText(l10n_util::GetStringFUTF16( | |
310 IDS_TOOLTIP_ZOOM_EXTENSION_ICON, | |
311 base::UTF8ToUTF16(extension_info_.name))); | |
312 image_button_->SetImage(views::Button::STATE_NORMAL, | |
313 &extension_info_.icon_image->image_skia()); | |
314 grid_layout->AddView(image_button_); | |
315 } | |
316 | |
317 // Add zoom label with the new zoom percent. | |
318 ui_zoom::ZoomController* zoom_controller = | |
319 ui_zoom::ZoomController::FromWebContents(web_contents_); | |
320 int zoom_percent = zoom_controller->GetZoomPercent(); | |
321 label_ = new views::Label( | |
322 l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent)); | |
323 label_->SetFontList( | |
324 ui::ResourceBundle::GetSharedInstance().GetFontList( | |
325 ui::ResourceBundle::MediumFont)); | |
326 grid_layout->AddView(label_); | |
327 | |
328 // Second row. | |
329 grid_layout->AddPaddingRow(0, 8); | |
330 columns = grid_layout->AddColumnSet(1); | |
331 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, | |
332 views::GridLayout::USE_PREF, 0, 0); | |
333 grid_layout->StartRow(0, 1); | |
334 | |
335 // Add "Reset to Default" button. | |
336 views::LabelButton* set_default_button = new views::LabelButton( | |
337 this, l10n_util::GetStringUTF16(IDS_ZOOM_SET_DEFAULT)); | |
338 set_default_button->SetStyle(views::Button::STYLE_BUTTON); | |
339 set_default_button->SetHorizontalAlignment(gfx::ALIGN_CENTER); | |
340 grid_layout->AddView(set_default_button); | |
341 | |
342 StartTimerIfNecessary(); | |
343 } | |
344 | |
345 void ZoomBubbleView::Observe(int type, | |
346 const content::NotificationSource& source, | |
347 const content::NotificationDetails& details) { | |
348 DCHECK_EQ(type, chrome::NOTIFICATION_FULLSCREEN_CHANGED); | |
349 CloseBubble(); | |
350 } | |
351 | |
352 void ZoomBubbleView::OnImmersiveRevealStarted() { | |
353 CloseBubble(); | |
354 } | |
355 | |
356 void ZoomBubbleView::OnImmersiveModeControllerDestroyed() { | |
357 immersive_mode_controller_ = NULL; | |
358 } | |
359 | |
360 void ZoomBubbleView::WindowClosing() { | |
361 // |zoom_bubble_| can be a new bubble by this point (as Close(); doesn't | |
362 // call this right away). Only set to NULL when it's this bubble. | |
363 if (zoom_bubble_ == this) | |
364 zoom_bubble_ = NULL; | |
365 } | |
366 | |
367 ZoomBubbleView::ZoomBubbleExtensionInfo::ZoomBubbleExtensionInfo() {} | 331 ZoomBubbleView::ZoomBubbleExtensionInfo::ZoomBubbleExtensionInfo() {} |
368 | 332 |
369 ZoomBubbleView::ZoomBubbleExtensionInfo::~ZoomBubbleExtensionInfo() {} | 333 ZoomBubbleView::ZoomBubbleExtensionInfo::~ZoomBubbleExtensionInfo() {} |
OLD | NEW |