Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/first_run_bubble.h" | 5 #include "chrome/browser/ui/views/first_run_bubble.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/utf_string_conversions.h" | 8 #include "base/utf_string_conversions.h" |
| 9 #include "chrome/browser/first_run/first_run.h" | 9 #include "chrome/browser/first_run/first_run.h" |
| 10 #include "chrome/browser/search_engines/util.h" | 10 #include "chrome/browser/search_engines/util.h" |
| 11 #include "chrome/browser/ui/browser.h" | 11 #include "chrome/browser/ui/browser.h" |
| 12 #include "chrome/browser/ui/browser_list.h" | 12 #include "chrome/browser/ui/browser_list.h" |
| 13 #include "chrome/browser/ui/browser_window.h" | 13 #include "chrome/browser/ui/browser_window.h" |
| 14 #include "content/browser/user_metrics.h" | 14 #include "content/browser/user_metrics.h" |
| 15 #include "grit/chromium_strings.h" | 15 #include "grit/chromium_strings.h" |
| 16 #include "grit/generated_resources.h" | 16 #include "grit/generated_resources.h" |
| 17 #include "grit/locale_settings.h" | 17 #include "grit/locale_settings.h" |
| 18 #include "grit/theme_resources_standard.h" | 18 #include "grit/theme_resources_standard.h" |
| 19 #include "ui/base/l10n/l10n_font_util.h" | 19 #include "ui/base/l10n/l10n_font_util.h" |
| 20 #include "ui/base/l10n/l10n_util.h" | 20 #include "ui/base/l10n/l10n_util.h" |
| 21 #include "ui/base/resource/resource_bundle.h" | 21 #include "ui/base/resource/resource_bundle.h" |
| 22 #include "views/controls/button/image_button.h" | 22 #include "views/controls/button/image_button.h" |
| 23 #include "views/controls/button/text_button.h" | |
| 24 #include "views/controls/label.h" | 23 #include "views/controls/label.h" |
| 25 #include "views/events/event.h" | 24 #include "views/events/event.h" |
| 26 #include "views/focus/focus_manager.h" | 25 #include "views/layout/grid_layout.h" |
| 27 #include "views/layout/layout_constants.h" | 26 #include "views/layout/layout_constants.h" |
| 28 #include "views/widget/native_widget_win.h" | |
| 29 #include "views/widget/widget.h" | 27 #include "views/widget/widget.h" |
| 30 | 28 |
| 31 namespace { | 29 namespace { |
| 32 | 30 const int kAnchorVerticalOffset = -4; |
| 33 // How much extra padding to put around our content over what the Bubble | 31 const int kLayoutTopInset = 1; |
| 34 // provides. | 32 const int kLayoutLeftInset = 2; |
| 35 const int kBubblePadding = 4; | 33 const int kLayoutBottomInset = 3; |
| 36 | 34 const int kLayoutRightInset = 2; |
| 37 // How much extra padding to put around our content over what the Bubble | |
| 38 // provides in alternative OEM bubble. | |
| 39 const int kOEMBubblePadding = 4; | |
| 40 | |
| 41 // Padding between parts of strings on the same line (for instance, | |
| 42 // "New!" and "Search from the address bar!" | |
| 43 const int kStringSeparationPadding = 2; | |
| 44 | |
| 45 // Margin around close button. | |
| 46 const int kMarginRightOfCloseButton = 7; | |
| 47 | |
| 48 // The bubble's HWND is actually owned by the border widget, and it's the border | |
| 49 // widget that's owned by the frame window the bubble is anchored to. This | |
| 50 // function makes the two leaps necessary to go from the bubble contents HWND | |
| 51 // to the frame HWND. | |
| 52 HWND GetLogicalBubbleOwner(HWND bubble_hwnd) { | |
| 53 HWND border_widget_hwnd = GetWindow(bubble_hwnd, GW_OWNER); | |
| 54 return GetWindow(border_widget_hwnd, GW_OWNER); | |
| 55 } | 35 } |
| 56 | 36 |
| 57 } // namespace | |
| 58 | |
| 59 // Base class for implementations of the client view which appears inside the | |
| 60 // first run bubble. It is a dialog-ish view, but is not a true dialog. | |
| 61 class FirstRunBubbleViewBase : public views::View, | |
| 62 public views::ButtonListener, | |
| 63 public views::FocusChangeListener { | |
| 64 public: | |
| 65 // Called by FirstRunBubble::Show to request focus for the proper button | |
| 66 // in the FirstRunBubbleView when it is shown. | |
| 67 virtual void BubbleShown() = 0; | |
| 68 virtual void OnDidChangeFocus(View* focused_before, View* focused_now) {} | |
| 69 }; | |
| 70 | |
| 71 // FirstRunBubbleView --------------------------------------------------------- | |
| 72 | |
| 73 class FirstRunBubbleView : public FirstRunBubbleViewBase { | |
| 74 public: | |
| 75 FirstRunBubbleView(FirstRunBubble* bubble_window, Profile* profile); | |
| 76 | |
| 77 private: | |
| 78 virtual ~FirstRunBubbleView() {} | |
| 79 | |
| 80 // FirstRunBubbleViewBase: | |
| 81 virtual void BubbleShown(); | |
| 82 | |
| 83 // Overridden from View: | |
| 84 virtual void ButtonPressed(views::Button* sender, const views::Event& event); | |
| 85 virtual void Layout(); | |
| 86 virtual gfx::Size GetPreferredSize(); | |
| 87 | |
| 88 // FocusChangeListener: | |
| 89 virtual void OnWillChangeFocus(View* focused_before, View* focused_now); | |
| 90 | |
| 91 FirstRunBubble* bubble_window_; | |
| 92 views::Label* label1_; | |
| 93 views::Label* label2_; | |
| 94 views::Label* label3_; | |
| 95 views::NativeTextButton* change_button_; | |
| 96 views::NativeTextButton* keep_button_; | |
| 97 Profile* profile_; | |
| 98 | |
| 99 DISALLOW_COPY_AND_ASSIGN(FirstRunBubbleView); | |
| 100 }; | |
| 101 | |
| 102 FirstRunBubbleView::FirstRunBubbleView(FirstRunBubble* bubble_window, | |
| 103 Profile* profile) | |
| 104 : bubble_window_(bubble_window), | |
| 105 label1_(NULL), | |
| 106 label2_(NULL), | |
| 107 label3_(NULL), | |
| 108 keep_button_(NULL), | |
| 109 change_button_(NULL), | |
| 110 profile_(profile) { | |
| 111 const gfx::Font& font = | |
| 112 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont); | |
| 113 | |
| 114 label1_ = new views::Label(l10n_util::GetStringUTF16(IDS_FR_BUBBLE_TITLE)); | |
| 115 label1_->SetFont(font.DeriveFont(3, gfx::Font::BOLD)); | |
| 116 label1_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 117 label1_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 118 AddChildView(label1_); | |
| 119 | |
| 120 gfx::Size ps = GetPreferredSize(); | |
| 121 | |
| 122 label2_ = new views::Label(l10n_util::GetStringUTF16(IDS_FR_BUBBLE_SUBTEXT)); | |
| 123 label2_->SetMultiLine(true); | |
| 124 label2_->SetFont(font); | |
| 125 label2_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 126 label2_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 127 label2_->SizeToFit(ps.width() - kBubblePadding * 2); | |
| 128 AddChildView(label2_); | |
| 129 | |
| 130 string16 question_str = l10n_util::GetStringFUTF16( | |
| 131 IDS_FR_BUBBLE_QUESTION, | |
| 132 GetDefaultSearchEngineName(profile)); | |
| 133 label3_ = new views::Label(question_str); | |
| 134 label3_->SetMultiLine(true); | |
| 135 label3_->SetFont(font); | |
| 136 label3_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 137 label3_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 138 label3_->SizeToFit(ps.width() - kBubblePadding * 2); | |
| 139 AddChildView(label3_); | |
| 140 | |
| 141 std::wstring keep_str = UTF16ToWide(l10n_util::GetStringFUTF16( | |
| 142 IDS_FR_BUBBLE_OK, | |
| 143 GetDefaultSearchEngineName(profile))); | |
| 144 keep_button_ = new views::NativeTextButton(this, keep_str); | |
| 145 keep_button_->SetIsDefault(true); | |
| 146 AddChildView(keep_button_); | |
| 147 | |
| 148 std::wstring change_str = | |
| 149 UTF16ToWide(l10n_util::GetStringUTF16(IDS_FR_BUBBLE_CHANGE)); | |
| 150 change_button_ = new views::NativeTextButton(this, change_str); | |
| 151 AddChildView(change_button_); | |
| 152 } | |
| 153 | |
| 154 void FirstRunBubbleView::BubbleShown() { | |
| 155 keep_button_->RequestFocus(); | |
| 156 } | |
| 157 | |
| 158 void FirstRunBubbleView::ButtonPressed(views::Button* sender, | |
| 159 const views::Event& event) { | |
| 160 UserMetrics::RecordAction(UserMetricsAction("FirstRunBubbleView_Clicked")); | |
| 161 bubble_window_->set_fade_away_on_close(true); | |
| 162 bubble_window_->Close(); | |
| 163 if (change_button_ == sender) { | |
| 164 UserMetrics::RecordAction( | |
| 165 UserMetricsAction("FirstRunBubbleView_ChangeButton")); | |
| 166 | |
| 167 Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); | |
| 168 if (browser) { | |
| 169 browser->OpenSearchEngineOptionsDialog(); | |
| 170 } | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 void FirstRunBubbleView::Layout() { | |
| 175 gfx::Size canvas = GetPreferredSize(); | |
| 176 | |
| 177 // The multiline business that follows is dirty hacks to get around | |
| 178 // bug 1325257. | |
| 179 label1_->SetMultiLine(false); | |
| 180 gfx::Size pref_size = label1_->GetPreferredSize(); | |
| 181 label1_->SetMultiLine(true); | |
| 182 label1_->SizeToFit(canvas.width() - kBubblePadding * 2); | |
| 183 label1_->SetBounds(kBubblePadding, kBubblePadding, | |
| 184 canvas.width() - kBubblePadding * 2, | |
| 185 pref_size.height()); | |
| 186 | |
| 187 int next_v_space = label1_->y() + pref_size.height() + | |
| 188 views::kRelatedControlSmallVerticalSpacing; | |
| 189 | |
| 190 pref_size = label2_->GetPreferredSize(); | |
| 191 label2_->SetBounds(kBubblePadding, next_v_space, | |
| 192 canvas.width() - kBubblePadding * 2, | |
| 193 pref_size.height()); | |
| 194 | |
| 195 next_v_space = label2_->y() + label2_->height() + | |
| 196 views::kPanelSubVerticalSpacing; | |
| 197 | |
| 198 pref_size = label3_->GetPreferredSize(); | |
| 199 label3_->SetBounds(kBubblePadding, next_v_space, | |
| 200 canvas.width() - kBubblePadding * 2, | |
| 201 pref_size.height()); | |
| 202 | |
| 203 pref_size = change_button_->GetPreferredSize(); | |
| 204 change_button_->SetBounds( | |
| 205 canvas.width() - pref_size.width() - kBubblePadding, | |
| 206 canvas.height() - pref_size.height() - views::kButtonVEdgeMargin, | |
| 207 pref_size.width(), pref_size.height()); | |
| 208 | |
| 209 pref_size = keep_button_->GetPreferredSize(); | |
| 210 keep_button_->SetBounds(change_button_->x() - pref_size.width() - | |
| 211 views::kRelatedButtonHSpacing, change_button_->y(), | |
| 212 pref_size.width(), pref_size.height()); | |
| 213 } | |
| 214 | |
| 215 gfx::Size FirstRunBubbleView::GetPreferredSize() { | |
| 216 return gfx::Size(views::Widget::GetLocalizedContentsSize( | |
| 217 IDS_FIRSTRUNBUBBLE_DIALOG_WIDTH_CHARS, | |
| 218 IDS_FIRSTRUNBUBBLE_DIALOG_HEIGHT_LINES)); | |
| 219 } | |
| 220 | |
| 221 void FirstRunBubbleView::OnWillChangeFocus(View* focused_before, | |
| 222 View* focused_now) { | |
| 223 if (focused_before && | |
| 224 (focused_before->GetClassName() == | |
| 225 views::NativeTextButton::kViewClassName)) { | |
| 226 views::NativeTextButton* before = | |
| 227 static_cast<views::NativeTextButton*>(focused_before); | |
| 228 before->SetIsDefault(false); | |
| 229 } | |
| 230 if (focused_now && | |
| 231 (focused_now->GetClassName() == | |
| 232 views::NativeTextButton::kViewClassName)) { | |
| 233 views::NativeTextButton* after = | |
| 234 static_cast<views::NativeTextButton*>(focused_now); | |
| 235 after->SetIsDefault(true); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 // FirstRunOEMBubbleView ------------------------------------------------------ | |
| 240 | |
| 241 class FirstRunOEMBubbleView : public FirstRunBubbleViewBase { | |
| 242 public: | |
| 243 FirstRunOEMBubbleView(FirstRunBubble* bubble_window, Profile* profile); | |
| 244 | |
| 245 private: | |
| 246 virtual ~FirstRunOEMBubbleView() { } | |
| 247 | |
| 248 // FirstRunBubbleViewBase: | |
| 249 virtual void BubbleShown(); | |
| 250 | |
| 251 // Overridden from View: | |
| 252 virtual void ButtonPressed(views::Button* sender, const views::Event& event); | |
| 253 virtual void Layout(); | |
| 254 virtual gfx::Size GetPreferredSize(); | |
| 255 | |
| 256 // FocusChangeListener: | |
| 257 virtual void OnWillChangeFocus(View* focused_before, View* focused_now); | |
| 258 | |
| 259 FirstRunBubble* bubble_window_; | |
| 260 views::Label* label1_; | |
| 261 views::Label* label2_; | |
| 262 views::Label* label3_; | |
| 263 views::ImageButton* close_button_; | |
| 264 Profile* profile_; | |
| 265 | |
| 266 DISALLOW_COPY_AND_ASSIGN(FirstRunOEMBubbleView); | |
| 267 }; | |
| 268 | |
| 269 FirstRunOEMBubbleView::FirstRunOEMBubbleView(FirstRunBubble* bubble_window, | |
| 270 Profile* profile) | |
| 271 : bubble_window_(bubble_window), | |
| 272 label1_(NULL), | |
| 273 label2_(NULL), | |
| 274 label3_(NULL), | |
| 275 close_button_(NULL), | |
| 276 profile_(profile) { | |
| 277 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 278 const gfx::Font& font = rb.GetFont(ResourceBundle::MediumFont); | |
| 279 | |
| 280 label1_ = new views::Label( | |
| 281 l10n_util::GetStringUTF16(IDS_FR_OEM_BUBBLE_TITLE_1)); | |
| 282 label1_->SetFont(font.DeriveFont(3, gfx::Font::BOLD)); | |
| 283 label1_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 284 label1_->SetEnabledColor(SK_ColorRED); | |
| 285 label1_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 286 AddChildView(label1_); | |
| 287 | |
| 288 label2_ = new views::Label( | |
| 289 l10n_util::GetStringUTF16(IDS_FR_OEM_BUBBLE_TITLE_2)); | |
| 290 label2_->SetFont(font.DeriveFont(3, gfx::Font::BOLD)); | |
| 291 label2_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 292 label2_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 293 AddChildView(label2_); | |
| 294 | |
| 295 gfx::Size ps = GetPreferredSize(); | |
| 296 | |
| 297 label3_ = new views::Label( | |
| 298 l10n_util::GetStringUTF16(IDS_FR_OEM_BUBBLE_SUBTEXT)); | |
| 299 label3_->SetMultiLine(true); | |
| 300 label3_->SetFont(font); | |
| 301 label3_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 302 label3_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 303 label3_->SizeToFit(ps.width() - kOEMBubblePadding * 2); | |
| 304 AddChildView(label3_); | |
| 305 | |
| 306 close_button_ = new views::ImageButton(this); | |
| 307 close_button_->SetImage(views::CustomButton::BS_NORMAL, | |
| 308 rb.GetBitmapNamed(IDR_CLOSE_BAR)); | |
| 309 close_button_->SetImage(views::CustomButton::BS_HOT, | |
| 310 rb.GetBitmapNamed(IDR_CLOSE_BAR_H)); | |
| 311 close_button_->SetImage(views::CustomButton::BS_PUSHED, | |
| 312 rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); | |
| 313 | |
| 314 AddChildView(close_button_); | |
| 315 } | |
| 316 | |
| 317 void FirstRunOEMBubbleView::BubbleShown() { | |
| 318 RequestFocus(); | |
| 319 // No button in oem_bubble to request focus. | |
| 320 } | |
| 321 | |
| 322 void FirstRunOEMBubbleView::ButtonPressed(views::Button* sender, | |
| 323 const views::Event& event) { | |
| 324 UserMetrics::RecordAction( | |
| 325 UserMetricsAction("FirstRunOEMBubbleView_Clicked")); | |
| 326 bubble_window_->set_fade_away_on_close(true); | |
| 327 bubble_window_->Close(); | |
| 328 } | |
| 329 | |
| 330 void FirstRunOEMBubbleView::Layout() { | |
| 331 gfx::Size canvas = GetPreferredSize(); | |
| 332 | |
| 333 // First, draw the close button on the far right. | |
| 334 gfx::Size sz = close_button_->GetPreferredSize(); | |
| 335 close_button_->SetBounds( | |
| 336 canvas.width() - sz.width() - kMarginRightOfCloseButton, | |
| 337 kOEMBubblePadding, sz.width(), sz.height()); | |
| 338 | |
| 339 gfx::Size pref_size = label1_->GetPreferredSize(); | |
| 340 label1_->SetBounds(kOEMBubblePadding, kOEMBubblePadding, | |
| 341 pref_size.width() + kOEMBubblePadding * 2, | |
| 342 pref_size.height()); | |
| 343 | |
| 344 pref_size = label2_->GetPreferredSize(); | |
| 345 label2_->SetBounds( | |
| 346 kOEMBubblePadding * 2 + label1_->GetPreferredSize().width(), | |
| 347 kOEMBubblePadding, canvas.width() - kOEMBubblePadding * 2, | |
| 348 pref_size.height()); | |
| 349 | |
| 350 int next_v_space = | |
| 351 label1_->y() + pref_size.height() + | |
| 352 views::kRelatedControlSmallVerticalSpacing; | |
| 353 | |
| 354 pref_size = label3_->GetPreferredSize(); | |
| 355 label3_->SetBounds(kOEMBubblePadding, next_v_space, | |
| 356 canvas.width() - kOEMBubblePadding * 2, | |
| 357 pref_size.height()); | |
| 358 } | |
| 359 | |
| 360 gfx::Size FirstRunOEMBubbleView::GetPreferredSize() { | |
| 361 // Calculate width based on font and text. | |
| 362 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 363 const gfx::Font& font = rb.GetFont( | |
| 364 ResourceBundle::MediumFont).DeriveFont(3, gfx::Font::BOLD); | |
| 365 gfx::Size size = gfx::Size( | |
| 366 ui::GetLocalizedContentsWidthForFont( | |
| 367 IDS_FIRSTRUNOEMBUBBLE_DIALOG_WIDTH_CHARS, font), | |
| 368 ui::GetLocalizedContentsHeightForFont( | |
| 369 IDS_FIRSTRUNOEMBUBBLE_DIALOG_HEIGHT_LINES, font)); | |
| 370 | |
| 371 // WARNING: HACK. Vista and XP calculate font size differently; this means | |
| 372 // that a dialog box correctly proportioned for XP will appear too large in | |
| 373 // Vista. The correct thing to do is to change font size calculations in | |
| 374 // XP or Vista so that the length of a string is calculated properly. For | |
| 375 // now, we force Vista to show a correctly-sized box by taking account of | |
| 376 // the difference in font size calculation. The coefficient should not be | |
| 377 // stored in a variable because it's a hack and should go away. | |
| 378 if (views::NativeWidgetWin::IsAeroGlassEnabled()) { | |
| 379 size.set_width(static_cast<int>(size.width() * 0.85)); | |
| 380 size.set_height(static_cast<int>(size.height() * 0.85)); | |
| 381 } | |
| 382 return size; | |
| 383 } | |
| 384 | |
| 385 void FirstRunOEMBubbleView::OnWillChangeFocus(View* focused_before, | |
| 386 View* focused_now) { | |
| 387 // No buttons in oem_bubble to register focus changes. | |
| 388 } | |
| 389 | |
| 390 // FirstRunMinimalBubbleView -------------------------------------------------- | |
| 391 // TODO(mirandac): combine FRBubbles more elegantly. http://crbug.com/41353 | |
| 392 | |
| 393 class FirstRunMinimalBubbleView : public FirstRunBubbleViewBase { | |
| 394 public: | |
| 395 FirstRunMinimalBubbleView(FirstRunBubble* bubble_window, Profile* profile); | |
| 396 | |
| 397 private: | |
| 398 virtual ~FirstRunMinimalBubbleView() { } | |
| 399 | |
| 400 // FirstRunBubbleViewBase: | |
| 401 virtual void BubbleShown(); | |
| 402 | |
| 403 // Overridden from View: | |
| 404 virtual void ButtonPressed(views::Button* sender, | |
| 405 const views::Event& event) { } | |
| 406 virtual void Layout(); | |
| 407 virtual gfx::Size GetPreferredSize(); | |
| 408 | |
| 409 // FocusChangeListener: | |
| 410 virtual void OnWillChangeFocus(View* focused_before, View* focused_now); | |
| 411 | |
| 412 FirstRunBubble* bubble_window_; | |
| 413 Profile* profile_; | |
| 414 views::Label* label1_; | |
| 415 views::Label* label2_; | |
| 416 | |
| 417 DISALLOW_COPY_AND_ASSIGN(FirstRunMinimalBubbleView); | |
| 418 }; | |
| 419 | |
| 420 FirstRunMinimalBubbleView::FirstRunMinimalBubbleView( | |
| 421 FirstRunBubble* bubble_window, | |
| 422 Profile* profile) | |
| 423 : bubble_window_(bubble_window), | |
| 424 profile_(profile), | |
| 425 label1_(NULL), | |
| 426 label2_(NULL) { | |
| 427 const gfx::Font& font = | |
| 428 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont); | |
| 429 | |
| 430 label1_ = new views::Label(l10n_util::GetStringFUTF16( | |
| 431 IDS_FR_SE_BUBBLE_TITLE, | |
| 432 GetDefaultSearchEngineName(profile_))); | |
| 433 label1_->SetFont(font.DeriveFont(3, gfx::Font::BOLD)); | |
| 434 label1_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 435 label1_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 436 AddChildView(label1_); | |
| 437 | |
| 438 gfx::Size ps = GetPreferredSize(); | |
| 439 | |
| 440 label2_ = new views::Label( | |
| 441 l10n_util::GetStringUTF16(IDS_FR_BUBBLE_SUBTEXT)); | |
| 442 label2_->SetMultiLine(true); | |
| 443 label2_->SetFont(font); | |
| 444 label2_->SetBackgroundColor(Bubble::kBackgroundColor); | |
| 445 label2_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 446 label2_->SizeToFit(ps.width() - kBubblePadding * 2); | |
| 447 AddChildView(label2_); | |
| 448 } | |
| 449 | |
| 450 void FirstRunMinimalBubbleView::BubbleShown() { | |
| 451 RequestFocus(); | |
| 452 } | |
| 453 | |
| 454 void FirstRunMinimalBubbleView::Layout() { | |
| 455 gfx::Size canvas = GetPreferredSize(); | |
| 456 | |
| 457 // See comments in FirstRunOEMBubbleView::Layout explaining this hack. | |
| 458 label1_->SetMultiLine(false); | |
| 459 gfx::Size pref_size = label1_->GetPreferredSize(); | |
| 460 label1_->SetMultiLine(true); | |
| 461 label1_->SizeToFit(canvas.width() - kBubblePadding * 2); | |
| 462 label1_->SetBounds(kBubblePadding, kBubblePadding, | |
| 463 canvas.width() - kBubblePadding * 2, | |
| 464 pref_size.height()); | |
| 465 | |
| 466 int next_v_space = label1_->y() + pref_size.height() + | |
| 467 views::kRelatedControlSmallVerticalSpacing; | |
| 468 | |
| 469 pref_size = label2_->GetPreferredSize(); | |
| 470 label2_->SetBounds(kBubblePadding, next_v_space, | |
| 471 canvas.width() - kBubblePadding * 2, | |
| 472 pref_size.height()); | |
| 473 } | |
| 474 | |
| 475 gfx::Size FirstRunMinimalBubbleView::GetPreferredSize() { | |
| 476 return gfx::Size(views::Widget::GetLocalizedContentsSize( | |
| 477 IDS_FIRSTRUN_MINIMAL_BUBBLE_DIALOG_WIDTH_CHARS, | |
| 478 IDS_FIRSTRUN_MINIMAL_BUBBLE_DIALOG_HEIGHT_LINES)); | |
| 479 } | |
| 480 | |
| 481 void FirstRunMinimalBubbleView::OnWillChangeFocus(View* focused_before, | |
| 482 View* focused_now) { | |
| 483 // No buttons in minimal bubble to register focus changes. | |
| 484 } | |
| 485 | |
| 486 | |
| 487 // FirstRunBubble ------------------------------------------------------------- | |
| 488 | |
| 489 // static | 37 // static |
| 490 FirstRunBubble* FirstRunBubble::Show( | 38 FirstRunBubble* FirstRunBubble::Show( |
| 491 Profile* profile, | 39 Profile* profile, |
| 492 views::Widget* parent, | 40 views::View* anchor_view, |
| 493 const gfx::Rect& position_relative_to, | |
| 494 views::BubbleBorder::ArrowLocation arrow_location, | 41 views::BubbleBorder::ArrowLocation arrow_location, |
| 495 FirstRun::BubbleType bubble_type) { | 42 FirstRun::BubbleType bubble_type) { |
| 496 FirstRunBubble* bubble = new FirstRunBubble(); | 43 FirstRunBubble* delegate = |
| 497 FirstRunBubbleViewBase* view = NULL; | 44 new FirstRunBubble(profile, |
| 498 | 45 anchor_view, |
| 499 switch (bubble_type) { | 46 arrow_location, |
| 500 case FirstRun::OEM_BUBBLE: | 47 bubble_type); |
| 501 view = new FirstRunOEMBubbleView(bubble, profile); | 48 views::BubbleDelegateView::CreateBubble(delegate); |
| 502 break; | 49 delegate->StartFade(true); |
| 503 case FirstRun::LARGE_BUBBLE: | 50 return delegate; |
| 504 view = new FirstRunBubbleView(bubble, profile); | |
| 505 break; | |
| 506 case FirstRun::MINIMAL_BUBBLE: | |
| 507 view = new FirstRunMinimalBubbleView(bubble, profile); | |
| 508 break; | |
| 509 default: | |
| 510 NOTREACHED(); | |
| 511 } | |
| 512 bubble->set_view(view); | |
| 513 bubble->InitBubble( | |
| 514 parent, position_relative_to, arrow_location, | |
| 515 views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, view, bubble); | |
| 516 bubble->GetWidget()->GetFocusManager()->AddFocusChangeListener(view); | |
| 517 view->BubbleShown(); | |
| 518 return bubble; | |
| 519 } | 51 } |
| 520 | 52 |
| 521 FirstRunBubble::FirstRunBubble() | 53 void FirstRunBubble::Init() { |
| 522 : has_been_activated_(false), | 54 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 523 ALLOW_THIS_IN_INITIALIZER_LIST(enable_window_method_factory_(this)), | 55 const gfx::Font& original_font = rb.GetFont(ResourceBundle::MediumFont); |
| 524 view_(NULL) { | 56 const gfx::Font& derived_font = original_font.DeriveFont(2, gfx::Font::BOLD); |
| 57 | |
| 58 views::Label* label1 = new views::Label(l10n_util::GetStringFUTF16( | |
| 59 IDS_FR_SE_BUBBLE_TITLE, | |
| 60 GetDefaultSearchEngineName(profile_))); | |
| 61 label1->SetFont(derived_font); | |
| 62 | |
| 63 views::Label* label2 = | |
| 64 new views::Label(l10n_util::GetStringUTF16(IDS_FR_BUBBLE_SUBTEXT)); | |
| 65 label2->SetFont(original_font); | |
| 66 | |
| 67 views::ImageButton* close_button = new views::ImageButton(this); | |
| 68 close_button->SetImage(views::CustomButton::BS_NORMAL, | |
| 69 rb.GetBitmapNamed(IDR_CLOSE_BAR)); | |
|
msw
2011/11/12 03:35:55
outdent this line by one space.
alicet1
2011/11/15 02:09:50
Done.
| |
| 70 close_button->SetImage(views::CustomButton::BS_HOT, | |
| 71 rb.GetBitmapNamed(IDR_CLOSE_BAR_H)); | |
|
msw
2011/11/12 03:35:55
outdent this line by one space.
alicet1
2011/11/15 02:09:50
Done.
| |
| 72 close_button->SetImage(views::CustomButton::BS_PUSHED, | |
| 73 rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); | |
|
msw
2011/11/12 03:35:55
outdent this line by one space.
alicet1
2011/11/15 02:09:50
Done.
| |
| 74 | |
| 75 views::GridLayout* layout = views::GridLayout::CreatePanel(this); | |
| 76 SetLayoutManager(layout); | |
| 77 layout->SetInsets(kLayoutTopInset, | |
| 78 kLayoutLeftInset, | |
| 79 kLayoutBottomInset, | |
| 80 kLayoutRightInset); | |
| 81 | |
| 82 views::ColumnSet* column_set = layout->AddColumnSet(0); | |
| 83 column_set->AddColumn(views::GridLayout::LEADING, | |
| 84 views::GridLayout::LEADING, 1, | |
| 85 views::GridLayout::USE_PREF, 0, 0); | |
| 86 column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 0, | |
| 87 views::GridLayout::USE_PREF, 0, 0); | |
| 88 | |
| 89 layout->StartRow(0, 0); | |
| 90 layout->AddView(label1); | |
| 91 layout->AddView(close_button, 1, 1, views::GridLayout::TRAILING, | |
| 92 views::GridLayout::LEADING); | |
| 93 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); | |
| 94 | |
| 95 layout->StartRow(0, 0); | |
| 96 layout->AddView(label2); | |
| 97 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); | |
|
msw
2011/11/12 03:35:55
You can remove this padding row, and merge this ex
alicet1
2011/11/15 02:09:50
Done.
| |
| 98 } | |
| 99 | |
| 100 gfx::Point FirstRunBubble::GetAnchorPoint() { | |
| 101 // Compensate for padding in anchor. | |
| 102 return BubbleDelegateView::GetAnchorPoint().Add( | |
| 103 gfx::Point(0, anchor_view() ? kAnchorVerticalOffset : 0)); | |
| 104 } | |
| 105 | |
| 106 FirstRunBubble::FirstRunBubble( | |
| 107 Profile* profile, | |
| 108 views::View* anchor_view, | |
| 109 views::BubbleBorder::ArrowLocation arrow_location, | |
| 110 FirstRun::BubbleType bubble_type) | |
| 111 : views::BubbleDelegateView(anchor_view, | |
| 112 arrow_location, | |
| 113 SK_ColorWHITE), | |
| 114 profile_(profile), | |
| 115 bubble_type_(bubble_type) { | |
| 525 } | 116 } |
| 526 | 117 |
| 527 FirstRunBubble::~FirstRunBubble() { | 118 FirstRunBubble::~FirstRunBubble() { |
| 528 enable_window_method_factory_.InvalidateWeakPtrs(); | |
| 529 GetWidget()->GetFocusManager()->RemoveFocusChangeListener(view_); | |
| 530 } | 119 } |
| 531 | 120 |
| 532 void FirstRunBubble::EnableParent() { | 121 void FirstRunBubble::ButtonPressed(views::Button* sender, |
| 533 ::EnableWindow(GetParent(), true); | 122 const views::Event& event) { |
| 534 // The EnableWindow() call above causes the parent to become active, which | 123 if (bubble_type_ == FirstRun::OEM_BUBBLE) { |
| 535 // resets the flag set by Bubble's call to DisableInactiveRendering(), so we | 124 UserMetrics::RecordAction( |
| 536 // have to call it again before activating the bubble to prevent the parent | 125 UserMetricsAction("FirstRunOEMBubbleView_Clicked")); |
| 537 // window from rendering inactive. | 126 } |
| 538 // TODO(beng): this only works in custom-frame mode, not glass-frame mode. | 127 GetWidget()->Close(); |
| 539 HWND bubble_owner = GetLogicalBubbleOwner(GetNativeView()); | |
| 540 views::Widget* parent = views::Widget::GetWidgetForNativeView(bubble_owner); | |
| 541 if (parent) | |
| 542 parent->DisableInactiveRendering(); | |
| 543 // Reactivate the FirstRunBubble so it responds to OnActivate messages. | |
| 544 SetWindowPos(GetParent(), 0, 0, 0, 0, | |
| 545 SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_SHOWWINDOW); | |
| 546 } | 128 } |
| 547 | |
| 548 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 549 void FirstRunBubble::OnActivate(UINT action, BOOL minimized, HWND window) { | |
| 550 // Keep the bubble around for kLingerTime milliseconds, to prevent accidental | |
| 551 // closure. | |
| 552 const int kLingerTime = 3000; | |
| 553 | |
| 554 // We might get re-enabled right before we are closed (sequence is: we get | |
| 555 // deactivated, we call close, before we are actually closed we get | |
| 556 // reactivated). Don't do the disabling of the parent in such cases. | |
| 557 if (action == WA_ACTIVE && !has_been_activated_) { | |
| 558 has_been_activated_ = true; | |
| 559 | |
| 560 ::EnableWindow(GetParent(), false); | |
| 561 | |
| 562 MessageLoop::current()->PostDelayedTask( | |
| 563 FROM_HERE, | |
| 564 base::Bind(&FirstRunBubble::EnableParent, | |
| 565 enable_window_method_factory_.GetWeakPtr()), | |
| 566 kLingerTime); | |
| 567 return; | |
| 568 } | |
| 569 | |
| 570 // Keep window from automatically closing until kLingerTime has passed. | |
| 571 if (::IsWindowEnabled(GetParent())) | |
| 572 Bubble::OnActivate(action, minimized, window); | |
| 573 } | |
| 574 #endif | |
| 575 | |
| 576 void FirstRunBubble::BubbleClosing(Bubble* bubble, bool closed_by_escape) { | |
| 577 // Make sure our parent window is re-enabled. | |
| 578 if (!IsWindowEnabled(GetParent())) | |
| 579 ::EnableWindow(GetParent(), true); | |
| 580 } | |
| OLD | NEW |