Home | History | Annotate | Download | only in corewm
      1 // Copyright (c) 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 "ui/views/corewm/tooltip_controller.h"
      6 
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "ui/aura/client/cursor_client.h"
      9 #include "ui/aura/client/tooltip_client.h"
     10 #include "ui/aura/env.h"
     11 #include "ui/aura/root_window.h"
     12 #include "ui/aura/test/aura_test_base.h"
     13 #include "ui/aura/test/event_generator.h"
     14 #include "ui/aura/window.h"
     15 #include "ui/base/resource/resource_bundle.h"
     16 #include "ui/base/text/text_elider.h"
     17 #include "ui/gfx/font.h"
     18 #include "ui/gfx/point.h"
     19 #include "ui/views/corewm/tooltip_controller_test_helper.h"
     20 #include "ui/views/view.h"
     21 #include "ui/views/widget/widget.h"
     22 
     23 #if defined(OS_WIN)
     24 #include "ui/base/win/scoped_ole_initializer.h"
     25 #endif
     26 #if !defined(OS_CHROMEOS)
     27 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
     28 #endif
     29 
     30 namespace views {
     31 namespace corewm {
     32 namespace test {
     33 namespace {
     34 
     35 views::Widget* CreateWidget(aura::RootWindow* root) {
     36   views::Widget* widget = new views::Widget;
     37   views::Widget::InitParams params;
     38   params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
     39   params.accept_events = true;
     40   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     41 #if defined(OS_CHROMEOS)
     42   params.parent = root;
     43 #else
     44   params.native_widget = new DesktopNativeWidgetAura(widget);
     45 #endif
     46   params.bounds = gfx::Rect(0, 0, 200, 100);
     47   widget->Init(params);
     48   widget->Show();
     49   return widget;
     50 }
     51 
     52 gfx::Font GetDefaultFont() {
     53   return ui::ResourceBundle::GetSharedInstance().GetFont(
     54       ui::ResourceBundle::BaseFont);
     55 }
     56 
     57 TooltipController* GetController(Widget* widget) {
     58   return static_cast<TooltipController*>(
     59       aura::client::GetTooltipClient(
     60           widget->GetNativeWindow()->GetRootWindow()));
     61 }
     62 
     63 }  // namespace
     64 
     65 class TooltipControllerTest : public aura::test::AuraTestBase {
     66  public:
     67   TooltipControllerTest() : view_(NULL) {}
     68   virtual ~TooltipControllerTest() {}
     69 
     70   virtual void SetUp() OVERRIDE {
     71     aura::test::AuraTestBase::SetUp();
     72 #if defined(OS_CHROMEOS)
     73     controller_.reset(new TooltipController(gfx::SCREEN_TYPE_ALTERNATE));
     74     root_window()->AddPreTargetHandler(controller_.get());
     75     SetTooltipClient(root_window(), controller_.get());
     76 #endif
     77     widget_.reset(CreateWidget(root_window()));
     78     widget_->SetContentsView(new View);
     79     view_ = new TooltipTestView;
     80     widget_->GetContentsView()->AddChildView(view_);
     81     view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds());
     82     helper_.reset(new TooltipControllerTestHelper(
     83                       GetController(widget_.get())));
     84     generator_.reset(new aura::test::EventGenerator(GetRootWindow()));
     85   }
     86 
     87   virtual void TearDown() OVERRIDE {
     88 #if defined(OS_CHROMEOS)
     89     root_window()->RemovePreTargetHandler(controller_.get());
     90     SetTooltipClient(root_window(), NULL);
     91     controller_.reset();
     92 #endif
     93     generator_.reset();
     94     helper_.reset();
     95     widget_.reset();
     96     aura::test::AuraTestBase::TearDown();
     97   }
     98 
     99  protected:
    100   aura::Window* GetWindow() {
    101     return widget_->GetNativeWindow();
    102   }
    103 
    104   aura::RootWindow* GetRootWindow() {
    105     return GetWindow()->GetRootWindow();
    106   }
    107 
    108   TooltipTestView* PrepareSecondView() {
    109     TooltipTestView* view2 = new TooltipTestView;
    110     widget_->GetContentsView()->AddChildView(view2);
    111     view_->SetBounds(0, 0, 100, 100);
    112     view2->SetBounds(100, 0, 100, 100);
    113     return view2;
    114   }
    115 
    116   scoped_ptr<views::Widget> widget_;
    117   TooltipTestView* view_;
    118   scoped_ptr<TooltipControllerTestHelper> helper_;
    119   scoped_ptr<aura::test::EventGenerator> generator_;
    120 
    121  private:
    122   scoped_ptr<TooltipController> controller_;
    123 #if defined(OS_WIN)
    124   ui::ScopedOleInitializer ole_initializer_;
    125 #endif
    126 
    127   DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest);
    128 };
    129 
    130 TEST_F(TooltipControllerTest, ViewTooltip) {
    131   view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
    132   EXPECT_EQ(string16(), helper_->GetTooltipText());
    133   EXPECT_EQ(NULL, helper_->GetTooltipWindow());
    134   generator_->MoveMouseToCenterOf(GetWindow());
    135 
    136   EXPECT_EQ(GetWindow(), GetRootWindow()->GetEventHandlerForPoint(
    137       generator_->current_location()));
    138   string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
    139   EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow()));
    140   EXPECT_EQ(string16(), helper_->GetTooltipText());
    141   EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow());
    142 
    143   // Fire tooltip timer so tooltip becomes visible.
    144   helper_->FireTooltipTimer();
    145 
    146   EXPECT_TRUE(helper_->IsTooltipVisible());
    147   generator_->MoveMouseBy(1, 0);
    148 
    149   EXPECT_TRUE(helper_->IsTooltipVisible());
    150   EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow()));
    151   EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
    152   EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow());
    153 }
    154 
    155 TEST_F(TooltipControllerTest, TooltipsInMultipleViews) {
    156   view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
    157   EXPECT_EQ(string16(), helper_->GetTooltipText());
    158   EXPECT_EQ(NULL, helper_->GetTooltipWindow());
    159 
    160   PrepareSecondView();
    161   aura::Window* window = GetWindow();
    162   aura::RootWindow* root_window = GetRootWindow();
    163 
    164   // Fire tooltip timer so tooltip becomes visible.
    165   generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint());
    166   helper_->FireTooltipTimer();
    167   EXPECT_TRUE(helper_->IsTooltipVisible());
    168   for (int i = 0; i < 49; ++i) {
    169     generator_->MoveMouseBy(1, 0);
    170     EXPECT_TRUE(helper_->IsTooltipVisible());
    171     EXPECT_EQ(window, root_window->GetEventHandlerForPoint(
    172             generator_->current_location()));
    173     string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
    174     EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
    175     EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
    176     EXPECT_EQ(window, helper_->GetTooltipWindow());
    177   }
    178   for (int i = 0; i < 49; ++i) {
    179     generator_->MoveMouseBy(1, 0);
    180     EXPECT_FALSE(helper_->IsTooltipVisible());
    181     EXPECT_EQ(window, root_window->GetEventHandlerForPoint(
    182             generator_->current_location()));
    183     string16 expected_tooltip;  // = ""
    184     EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
    185     EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
    186     EXPECT_EQ(window, helper_->GetTooltipWindow());
    187   }
    188 }
    189 
    190 TEST_F(TooltipControllerTest, EnableOrDisableTooltips) {
    191   view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
    192   EXPECT_EQ(string16(), helper_->GetTooltipText());
    193   EXPECT_EQ(NULL, helper_->GetTooltipWindow());
    194 
    195   generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint());
    196   string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
    197 
    198   // Fire tooltip timer so tooltip becomes visible.
    199   helper_->FireTooltipTimer();
    200   EXPECT_TRUE(helper_->IsTooltipVisible());
    201 
    202   // Diable tooltips and check again.
    203   helper_->controller()->SetTooltipsEnabled(false);
    204   EXPECT_FALSE(helper_->IsTooltipVisible());
    205   helper_->FireTooltipTimer();
    206   EXPECT_FALSE(helper_->IsTooltipVisible());
    207 
    208   // Enable tooltips back and check again.
    209   helper_->controller()->SetTooltipsEnabled(true);
    210   EXPECT_FALSE(helper_->IsTooltipVisible());
    211   helper_->FireTooltipTimer();
    212   EXPECT_TRUE(helper_->IsTooltipVisible());
    213 }
    214 
    215 TEST_F(TooltipControllerTest, TrimTooltipToFitTests) {
    216   const int max_width = 4000;
    217   string16 tooltip;
    218   int width, line_count, expect_lines;
    219   int max_pixel_width = 400;  // copied from constants in tooltip_controller.cc
    220   int max_lines = 10;  // copied from constants in tooltip_controller.cc
    221   gfx::Font font = GetDefaultFont();
    222   size_t tooltip_len;
    223 
    224   // Error in computed size vs. expected size should not be greater than the
    225   // size of the longest word.
    226   int error_in_pixel_width = font.GetStringWidth(ASCIIToUTF16("tooltip"));
    227 
    228   // Long tooltips should wrap to next line
    229   tooltip.clear();
    230   width = line_count = -1;
    231   expect_lines = 3;
    232   for (; font.GetStringWidth(tooltip) <= (expect_lines - 1) * max_pixel_width;)
    233     tooltip.append(ASCIIToUTF16("This is part of the tooltip"));
    234   tooltip_len = tooltip.length();
    235   TooltipControllerTestHelper::TrimTooltipToFit(
    236       max_width, &tooltip, &width, &line_count);
    237   EXPECT_NEAR(max_pixel_width, width, error_in_pixel_width);
    238   EXPECT_EQ(expect_lines, line_count);
    239   EXPECT_EQ(tooltip_len + expect_lines - 1, tooltip.length());
    240 
    241   // More than |max_lines| lines should get truncated at 10 lines.
    242   tooltip.clear();
    243   width = line_count = -1;
    244   expect_lines = 13;
    245   for (; font.GetStringWidth(tooltip) <= (expect_lines - 1) * max_pixel_width;)
    246     tooltip.append(ASCIIToUTF16("This is part of the tooltip"));
    247   TooltipControllerTestHelper::TrimTooltipToFit(
    248       max_width, &tooltip, &width, &line_count);
    249   EXPECT_NEAR(max_pixel_width, width, error_in_pixel_width);
    250   EXPECT_EQ(max_lines, line_count);
    251 
    252   // Long multi line tooltips should wrap individual lines.
    253   tooltip.clear();
    254   width = line_count = -1;
    255   expect_lines = 4;
    256   for (; font.GetStringWidth(tooltip) <= (expect_lines - 2) * max_pixel_width;)
    257     tooltip.append(ASCIIToUTF16("This is part of the tooltip"));
    258   tooltip.insert(tooltip.length() / 2, ASCIIToUTF16("\n"));
    259   tooltip_len = tooltip.length();
    260   TooltipControllerTestHelper::TrimTooltipToFit(
    261       max_width, &tooltip, &width, &line_count);
    262   EXPECT_NEAR(max_pixel_width, width, error_in_pixel_width);
    263   EXPECT_EQ(expect_lines, line_count);
    264   // We may have inserted the line break above near a space which will get
    265   // trimmed. Hence we may be off by 1 in the final tooltip length calculation.
    266   EXPECT_NEAR(tooltip_len + expect_lines - 2, tooltip.length(), 1);
    267 
    268 #if !defined(OS_WIN)
    269   // Tooltip with really long word gets elided.
    270   tooltip.clear();
    271   width = line_count = -1;
    272   tooltip = UTF8ToUTF16(std::string('a', max_pixel_width));
    273   TooltipControllerTestHelper::TrimTooltipToFit(
    274       max_width, &tooltip, &width, &line_count);
    275   EXPECT_NEAR(max_pixel_width, width, 5);
    276   EXPECT_EQ(1, line_count);
    277   EXPECT_EQ(ui::ElideText(UTF8ToUTF16(std::string('a', max_pixel_width)), font,
    278                           max_pixel_width, ui::ELIDE_AT_END), tooltip);
    279 #endif
    280 
    281   // Normal small tooltip should stay as is.
    282   tooltip.clear();
    283   width = line_count = -1;
    284   tooltip = ASCIIToUTF16("Small Tooltip");
    285   TooltipControllerTestHelper::TrimTooltipToFit(
    286       max_width, &tooltip, &width, &line_count);
    287   EXPECT_EQ(font.GetStringWidth(ASCIIToUTF16("Small Tooltip")), width);
    288   EXPECT_EQ(1, line_count);
    289   EXPECT_EQ(ASCIIToUTF16("Small Tooltip"), tooltip);
    290 
    291   // Normal small multi-line tooltip should stay as is.
    292   tooltip.clear();
    293   width = line_count = -1;
    294   tooltip = ASCIIToUTF16("Multi line\nTooltip");
    295   TooltipControllerTestHelper::TrimTooltipToFit(
    296       max_width, &tooltip, &width, &line_count);
    297   int expected_width = font.GetStringWidth(ASCIIToUTF16("Multi line"));
    298   expected_width = std::max(expected_width,
    299                             font.GetStringWidth(ASCIIToUTF16("Tooltip")));
    300   EXPECT_EQ(expected_width, width);
    301   EXPECT_EQ(2, line_count);
    302   EXPECT_EQ(ASCIIToUTF16("Multi line\nTooltip"), tooltip);
    303 
    304   // Whitespaces in tooltips are preserved.
    305   tooltip.clear();
    306   width = line_count = -1;
    307   tooltip = ASCIIToUTF16("Small  Tool  t\tip");
    308   TooltipControllerTestHelper::TrimTooltipToFit(
    309       max_width, &tooltip, &width, &line_count);
    310   EXPECT_EQ(font.GetStringWidth(ASCIIToUTF16("Small  Tool  t\tip")), width);
    311   EXPECT_EQ(1, line_count);
    312   EXPECT_EQ(ASCIIToUTF16("Small  Tool  t\tip"), tooltip);
    313 }
    314 
    315 TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) {
    316   view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1"));
    317   EXPECT_EQ(string16(), helper_->GetTooltipText());
    318   EXPECT_EQ(NULL, helper_->GetTooltipWindow());
    319 
    320   TooltipTestView* view2 = PrepareSecondView();
    321   view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2"));
    322 
    323   aura::Window* window = GetWindow();
    324 
    325   // Fire tooltip timer so tooltip becomes visible.
    326   generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint());
    327   helper_->FireTooltipTimer();
    328   EXPECT_TRUE(helper_->IsTooltipVisible());
    329   EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
    330 
    331   generator_->PressKey(ui::VKEY_1, 0);
    332   EXPECT_FALSE(helper_->IsTooltipVisible());
    333   EXPECT_FALSE(helper_->IsTooltipTimerRunning());
    334   EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
    335 
    336   // Moving the mouse inside |view1| should not change the state of the tooltip
    337   // or the timers.
    338   for (int i = 0; i < 49; i++) {
    339     generator_->MoveMouseBy(1, 0);
    340     EXPECT_FALSE(helper_->IsTooltipVisible());
    341     EXPECT_FALSE(helper_->IsTooltipTimerRunning());
    342     EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
    343     EXPECT_EQ(window,
    344               GetRootWindow()->GetEventHandlerForPoint(
    345                   generator_->current_location()));
    346     string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1");
    347     EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
    348     EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
    349     EXPECT_EQ(window, helper_->GetTooltipWindow());
    350   }
    351 
    352   // Now we move the mouse on to |view2|. It should re-start the tooltip timer.
    353   generator_->MoveMouseBy(1, 0);
    354   EXPECT_TRUE(helper_->IsTooltipTimerRunning());
    355   helper_->FireTooltipTimer();
    356   EXPECT_TRUE(helper_->IsTooltipVisible());
    357   EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
    358   string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2");
    359   EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
    360   EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
    361   EXPECT_EQ(window, helper_->GetTooltipWindow());
    362 }
    363 
    364 TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) {
    365   view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1"));
    366   EXPECT_EQ(string16(), helper_->GetTooltipText());
    367   EXPECT_EQ(NULL, helper_->GetTooltipWindow());
    368 
    369   TooltipTestView* view2 = PrepareSecondView();
    370   view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2"));
    371 
    372   aura::Window* window = GetWindow();
    373 
    374   // Fire tooltip timer so tooltip becomes visible.
    375   generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint());
    376   helper_->FireTooltipTimer();
    377   EXPECT_TRUE(helper_->IsTooltipVisible());
    378   EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
    379 
    380   helper_->FireTooltipShownTimer();
    381   EXPECT_FALSE(helper_->IsTooltipVisible());
    382   EXPECT_FALSE(helper_->IsTooltipTimerRunning());
    383   EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
    384 
    385   // Moving the mouse inside |view1| should not change the state of the tooltip
    386   // or the timers.
    387   for (int i = 0; i < 49; ++i) {
    388     generator_->MoveMouseBy(1, 0);
    389     EXPECT_FALSE(helper_->IsTooltipVisible());
    390     EXPECT_FALSE(helper_->IsTooltipTimerRunning());
    391     EXPECT_FALSE(helper_->IsTooltipShownTimerRunning());
    392     EXPECT_EQ(window, GetRootWindow()->GetEventHandlerForPoint(
    393                   generator_->current_location()));
    394     string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1");
    395     EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
    396     EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
    397     EXPECT_EQ(window, helper_->GetTooltipWindow());
    398   }
    399 
    400   // Now we move the mouse on to |view2|. It should re-start the tooltip timer.
    401   generator_->MoveMouseBy(1, 0);
    402   EXPECT_TRUE(helper_->IsTooltipTimerRunning());
    403   helper_->FireTooltipTimer();
    404   EXPECT_TRUE(helper_->IsTooltipVisible());
    405   EXPECT_TRUE(helper_->IsTooltipShownTimerRunning());
    406   string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2");
    407   EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window));
    408   EXPECT_EQ(expected_tooltip, helper_->GetTooltipText());
    409   EXPECT_EQ(window, helper_->GetTooltipWindow());
    410 }
    411 
    412 }  // namespace test
    413 }  // namespace corewm
    414 }  // namespace views
    415