| 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 "ui/views/corewm/tooltip_controller.h" | 5 #include "ui/views/corewm/tooltip_controller.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
| 13 #include "base/time/time.h" | 13 #include "base/time/time.h" |
| 14 #include "build/build_config.h" | 14 #include "build/build_config.h" |
| 15 #include "ui/aura/client/capture_client.h" | 15 #include "ui/aura/client/capture_client.h" |
| 16 #include "ui/aura/client/cursor_client.h" | 16 #include "ui/aura/client/cursor_client.h" |
| 17 #include "ui/aura/client/drag_drop_client.h" | 17 #include "ui/aura/client/drag_drop_client.h" |
| 18 #include "ui/aura/client/screen_position_client.h" | 18 #include "ui/aura/client/screen_position_client.h" |
| 19 #include "ui/aura/env.h" | 19 #include "ui/aura/env.h" |
| 20 #include "ui/aura/window.h" | 20 #include "ui/aura/window.h" |
| 21 #include "ui/display/screen.h" | 21 #include "ui/display/screen.h" |
| 22 #include "ui/events/event.h" | 22 #include "ui/events/event.h" |
| 23 #include "ui/gfx/font.h" | 23 #include "ui/gfx/font.h" |
| 24 #include "ui/gfx/geometry/rect.h" | 24 #include "ui/gfx/geometry/rect.h" |
| 25 #include "ui/gfx/text_elider.h" | 25 #include "ui/gfx/text_elider.h" |
| 26 #include "ui/views/corewm/tooltip.h" | 26 #include "ui/views/corewm/tooltip.h" |
| 27 #include "ui/views/widget/tooltip_manager.h" | 27 #include "ui/views/widget/tooltip_manager.h" |
| 28 #include "ui/wm/public/tooltip_client.h" |
| 28 | 29 |
| 29 namespace views { | 30 namespace views { |
| 30 namespace corewm { | 31 namespace corewm { |
| 31 namespace { | 32 namespace { |
| 32 | 33 |
| 33 const int kTooltipTimeoutMs = 500; | 34 const int kDelayForTooltipUpdateInMs = 500; |
| 34 const int kDefaultTooltipShownTimeoutMs = 10000; | 35 const int kDefaultTooltipShownTimeoutMs = 10000; |
| 35 #if defined(OS_WIN) | 36 #if defined(OS_WIN) |
| 36 // Drawing a long word in tooltip is very slow on Windows. crbug.com/513693 | 37 // Drawing a long word in tooltip is very slow on Windows. crbug.com/513693 |
| 37 const size_t kMaxTooltipLength = 1024; | 38 const size_t kMaxTooltipLength = 1024; |
| 38 #else | 39 #else |
| 39 const size_t kMaxTooltipLength = 2048; | 40 const size_t kMaxTooltipLength = 2048; |
| 40 #endif | 41 #endif |
| 41 | 42 |
| 42 // Returns true if |target| is a valid window to get the tooltip from. | 43 // Returns true if |target| is a valid window to get the tooltip from. |
| 43 // |event_target| is the original target from the event and |target| the window | 44 // |event_target| is the original target from the event and |target| the window |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 } // namespace | 123 } // namespace |
| 123 | 124 |
| 124 //////////////////////////////////////////////////////////////////////////////// | 125 //////////////////////////////////////////////////////////////////////////////// |
| 125 // TooltipController public: | 126 // TooltipController public: |
| 126 | 127 |
| 127 TooltipController::TooltipController(std::unique_ptr<Tooltip> tooltip) | 128 TooltipController::TooltipController(std::unique_ptr<Tooltip> tooltip) |
| 128 : tooltip_window_(NULL), | 129 : tooltip_window_(NULL), |
| 129 tooltip_id_(NULL), | 130 tooltip_id_(NULL), |
| 130 tooltip_window_at_mouse_press_(NULL), | 131 tooltip_window_at_mouse_press_(NULL), |
| 131 tooltip_(std::move(tooltip)), | 132 tooltip_(std::move(tooltip)), |
| 132 tooltips_enabled_(true) { | 133 tooltips_enabled_(true), |
| 133 tooltip_timer_.Start(FROM_HERE, | 134 tooltip_show_delayed_(true) {} |
| 134 base::TimeDelta::FromMilliseconds(kTooltipTimeoutMs), | |
| 135 this, &TooltipController::TooltipTimerFired); | |
| 136 } | |
| 137 | 135 |
| 138 TooltipController::~TooltipController() { | 136 TooltipController::~TooltipController() { |
| 139 if (tooltip_window_) | 137 if (tooltip_window_) |
| 140 tooltip_window_->RemoveObserver(this); | 138 tooltip_window_->RemoveObserver(this); |
| 141 } | 139 } |
| 142 | 140 |
| 143 int TooltipController::GetMaxWidth(const gfx::Point& location) const { | 141 int TooltipController::GetMaxWidth(const gfx::Point& location) const { |
| 144 return tooltip_->GetMaxWidth(location); | 142 return tooltip_->GetMaxWidth(location); |
| 145 } | 143 } |
| 146 | 144 |
| 147 void TooltipController::UpdateTooltip(aura::Window* target) { | 145 void TooltipController::UpdateTooltip(aura::Window* target) { |
| 148 // If tooltip is visible, we may want to hide it. If it is not, we are ok. | 146 // If tooltip is visible, we may want to hide it. If it is not, we are ok. |
| 149 if (tooltip_window_ == target && tooltip_->IsVisible()) | 147 if (tooltip_window_ == target && tooltip_->IsVisible()) |
| 150 UpdateIfRequired(); | 148 UpdateIfRequired(); |
| 151 | 149 |
| 152 // Reset |tooltip_window_at_mouse_press_| if the moving within the same window | 150 // Reset |tooltip_window_at_mouse_press_| if the moving within the same window |
| 153 // but over a region that has different tooltip text. By resetting | 151 // but over a region that has different tooltip text. |
| 154 // |tooltip_window_at_mouse_press_| we ensure the next time the timer fires | |
| 155 // we'll requery for the tooltip text. | |
| 156 // This handles the case of clicking on a view, moving within the same window | 152 // This handles the case of clicking on a view, moving within the same window |
| 157 // but over a different view, than back to the original. | 153 // but over a different view, than back to the original. |
| 158 if (tooltip_window_at_mouse_press_ && | 154 if (tooltip_window_at_mouse_press_ && |
| 159 target == tooltip_window_at_mouse_press_ && | 155 target == tooltip_window_at_mouse_press_ && |
| 160 aura::client::GetTooltipText(target) != tooltip_text_at_mouse_press_) { | 156 aura::client::GetTooltipText(target) != tooltip_text_at_mouse_press_) { |
| 161 tooltip_window_at_mouse_press_ = NULL; | 157 tooltip_window_at_mouse_press_ = NULL; |
| 162 } | 158 } |
| 163 | |
| 164 // If we had stopped the tooltip timer for some reason, we must restart it if | |
| 165 // there is a change in the tooltip. | |
| 166 if (!tooltip_timer_.IsRunning()) { | |
| 167 if (tooltip_window_ != target || (tooltip_window_ && | |
| 168 tooltip_text_ != aura::client::GetTooltipText(tooltip_window_))) { | |
| 169 tooltip_timer_.Start(FROM_HERE, | |
| 170 base::TimeDelta::FromMilliseconds(kTooltipTimeoutMs), | |
| 171 this, &TooltipController::TooltipTimerFired); | |
| 172 } | |
| 173 } | |
| 174 } | 159 } |
| 175 | 160 |
| 176 void TooltipController::SetTooltipShownTimeout(aura::Window* target, | 161 void TooltipController::SetTooltipShownTimeout(aura::Window* target, |
| 177 int timeout_in_ms) { | 162 int timeout_in_ms) { |
| 178 tooltip_shown_timeout_map_[target] = timeout_in_ms; | 163 tooltip_shown_timeout_map_[target] = timeout_in_ms; |
| 179 } | 164 } |
| 180 | 165 |
| 181 void TooltipController::SetTooltipsEnabled(bool enable) { | 166 void TooltipController::SetTooltipsEnabled(bool enable) { |
| 182 if (tooltips_enabled_ == enable) | 167 if (tooltips_enabled_ == enable) |
| 183 return; | 168 return; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 204 curr_mouse_loc_ = event->location(); | 189 curr_mouse_loc_ = event->location(); |
| 205 aura::Window* target = NULL; | 190 aura::Window* target = NULL; |
| 206 // Avoid a call to display::Screen::GetWindowAtScreenPoint() since it can | 191 // Avoid a call to display::Screen::GetWindowAtScreenPoint() since it can |
| 207 // be very expensive on X11 in cases when the tooltip is hidden anyway. | 192 // be very expensive on X11 in cases when the tooltip is hidden anyway. |
| 208 if (tooltips_enabled_ && | 193 if (tooltips_enabled_ && |
| 209 !aura::Env::GetInstance()->IsMouseButtonDown() && | 194 !aura::Env::GetInstance()->IsMouseButtonDown() && |
| 210 !IsDragDropInProgress()) { | 195 !IsDragDropInProgress()) { |
| 211 target = GetTooltipTarget(*event, &curr_mouse_loc_); | 196 target = GetTooltipTarget(*event, &curr_mouse_loc_); |
| 212 } | 197 } |
| 213 SetTooltipWindow(target); | 198 SetTooltipWindow(target); |
| 214 if (tooltip_timer_.IsRunning()) | |
| 215 tooltip_timer_.Reset(); | |
| 216 | 199 |
| 217 if (tooltip_->IsVisible()) | 200 if (tooltip_->IsVisible() || |
| 201 (tooltip_window_ && |
| 202 tooltip_text_ != aura::client::GetTooltipText(tooltip_window_))) |
| 218 UpdateIfRequired(); | 203 UpdateIfRequired(); |
| 219 break; | 204 break; |
| 220 } | 205 } |
| 221 case ui::ET_MOUSE_PRESSED: | 206 case ui::ET_MOUSE_PRESSED: |
| 222 if ((event->flags() & ui::EF_IS_NON_CLIENT) == 0) { | 207 if ((event->flags() & ui::EF_IS_NON_CLIENT) == 0) { |
| 223 aura::Window* target = static_cast<aura::Window*>(event->target()); | 208 aura::Window* target = static_cast<aura::Window*>(event->target()); |
| 224 // We don't get a release for non-client areas. | 209 // We don't get a release for non-client areas. |
| 225 tooltip_window_at_mouse_press_ = target; | 210 tooltip_window_at_mouse_press_ = target; |
| 226 if (target) | 211 if (target) |
| 227 tooltip_text_at_mouse_press_ = aura::client::GetTooltipText(target); | 212 tooltip_text_at_mouse_press_ = aura::client::GetTooltipText(target); |
| 228 } | 213 } |
| 229 tooltip_->Hide(); | 214 tooltip_->Hide(); |
| 230 break; | 215 break; |
| 231 case ui::ET_MOUSEWHEEL: | 216 case ui::ET_MOUSEWHEEL: |
| 232 // Hide the tooltip for click, release, drag, wheel events. | 217 // Hide the tooltip for click, release, drag, wheel events. |
| 233 if (tooltip_->IsVisible()) | 218 if (tooltip_->IsVisible()) |
| 234 tooltip_->Hide(); | 219 tooltip_->Hide(); |
| 235 | |
| 236 // Don't reshow the tooltip during scroll. | |
| 237 if (tooltip_timer_.IsRunning()) | |
| 238 tooltip_timer_.Reset(); | |
| 239 break; | 220 break; |
| 240 default: | 221 default: |
| 241 break; | 222 break; |
| 242 } | 223 } |
| 243 } | 224 } |
| 244 | 225 |
| 245 void TooltipController::OnTouchEvent(ui::TouchEvent* event) { | 226 void TooltipController::OnTouchEvent(ui::TouchEvent* event) { |
| 246 // TODO(varunjain): need to properly implement tooltips for | 227 // TODO(varunjain): need to properly implement tooltips for |
| 247 // touch events. | 228 // touch events. |
| 248 // Hide the tooltip for touch events. | 229 // Hide the tooltip for touch events. |
| 249 tooltip_->Hide(); | 230 tooltip_->Hide(); |
| 250 SetTooltipWindow(NULL); | 231 SetTooltipWindow(NULL); |
| 251 } | 232 } |
| 252 | 233 |
| 253 void TooltipController::OnCancelMode(ui::CancelModeEvent* event) { | 234 void TooltipController::OnCancelMode(ui::CancelModeEvent* event) { |
| 254 tooltip_->Hide(); | 235 tooltip_->Hide(); |
| 255 SetTooltipWindow(NULL); | 236 SetTooltipWindow(NULL); |
| 256 } | 237 } |
| 257 | 238 |
| 239 void TooltipController::OnCursorVisibilityChanged(bool is_visible) { |
| 240 UpdateIfRequired(); |
| 241 } |
| 242 |
| 258 void TooltipController::OnWindowDestroyed(aura::Window* window) { | 243 void TooltipController::OnWindowDestroyed(aura::Window* window) { |
| 259 if (tooltip_window_ == window) { | 244 if (tooltip_window_ == window) { |
| 260 tooltip_->Hide(); | 245 tooltip_->Hide(); |
| 261 tooltip_shown_timeout_map_.erase(tooltip_window_); | 246 tooltip_shown_timeout_map_.erase(tooltip_window_); |
| 262 tooltip_window_ = NULL; | 247 tooltip_window_ = NULL; |
| 263 } | 248 } |
| 264 } | 249 } |
| 265 | 250 |
| 251 void TooltipController::OnWindowPropertyChanged(aura::Window* window, |
| 252 const void* key, |
| 253 intptr_t old) { |
| 254 if ((key == aura::client::kTooltipIdKey || |
| 255 key == aura::client::kTooltipTextKey) && |
| 256 aura::client::GetTooltipText(window) != base::string16() && |
| 257 (tooltip_text_ != aura::client::GetTooltipText(window) || |
| 258 tooltip_id_ != aura::client::GetTooltipId(window))) |
| 259 UpdateIfRequired(); |
| 260 } |
| 261 |
| 266 //////////////////////////////////////////////////////////////////////////////// | 262 //////////////////////////////////////////////////////////////////////////////// |
| 267 // TooltipController private: | 263 // TooltipController private: |
| 268 | 264 |
| 269 void TooltipController::TooltipTimerFired() { | |
| 270 UpdateIfRequired(); | |
| 271 } | |
| 272 | |
| 273 void TooltipController::TooltipShownTimerFired() { | 265 void TooltipController::TooltipShownTimerFired() { |
| 274 tooltip_->Hide(); | 266 tooltip_->Hide(); |
| 275 | |
| 276 // Since the user presumably no longer needs the tooltip, we also stop the | |
| 277 // tooltip timer so that tooltip does not pop back up. We will restart this | |
| 278 // timer if the tooltip changes (see UpdateTooltip()). | |
| 279 tooltip_timer_.Stop(); | |
| 280 } | 267 } |
| 281 | 268 |
| 282 void TooltipController::UpdateIfRequired() { | 269 void TooltipController::UpdateIfRequired() { |
| 283 if (!tooltips_enabled_ || | 270 if (!tooltips_enabled_ || aura::Env::GetInstance()->IsMouseButtonDown() || |
| 284 aura::Env::GetInstance()->IsMouseButtonDown() || | |
| 285 IsDragDropInProgress() || !IsCursorVisible()) { | 271 IsDragDropInProgress() || !IsCursorVisible()) { |
| 286 tooltip_->Hide(); | 272 tooltip_->Hide(); |
| 287 return; | 273 return; |
| 288 } | 274 } |
| 289 | 275 |
| 290 base::string16 tooltip_text; | 276 base::string16 tooltip_text; |
| 291 if (tooltip_window_) | 277 if (tooltip_window_) |
| 292 tooltip_text = aura::client::GetTooltipText(tooltip_window_); | 278 tooltip_text = aura::client::GetTooltipText(tooltip_window_); |
| 293 | 279 |
| 294 // If the user pressed a mouse button. We will hide the tooltip and not show | 280 // If the user pressed a mouse button. We will hide the tooltip and not show |
| 295 // it until there is a change in the tooltip. | 281 // it until there is a change in the tooltip. |
| 296 if (tooltip_window_at_mouse_press_) { | 282 if (tooltip_window_at_mouse_press_) { |
| 297 if (tooltip_window_ == tooltip_window_at_mouse_press_ && | 283 if (tooltip_window_ == tooltip_window_at_mouse_press_ && |
| 298 tooltip_text == tooltip_text_at_mouse_press_) { | 284 tooltip_text == tooltip_text_at_mouse_press_) { |
| 299 tooltip_->Hide(); | 285 tooltip_->Hide(); |
| 300 return; | 286 return; |
| 301 } | 287 } |
| 302 tooltip_window_at_mouse_press_ = NULL; | 288 tooltip_window_at_mouse_press_ = NULL; |
| 303 } | 289 } |
| 304 | 290 |
| 305 // If the uniqueness indicator is different from the previously encountered | 291 // If the uniqueness indicator is different from the previously encountered |
| 306 // one, we should force tooltip update | 292 // one, we should force tooltip update |
| 307 const void* tooltip_id = aura::client::GetTooltipId(tooltip_window_); | 293 const void* tooltip_id = aura::client::GetTooltipId(tooltip_window_); |
| 308 bool ids_differ = false; | 294 bool ids_differ = false; |
| 309 ids_differ = tooltip_id_ != tooltip_id; | 295 ids_differ = tooltip_id_ != tooltip_id; |
| 310 tooltip_id_ = tooltip_id; | 296 tooltip_id_ = tooltip_id; |
| 311 | 297 |
| 312 // We add the !tooltip_->IsVisible() below because when we come here from | |
| 313 // TooltipTimerFired(), the tooltip_text may not have changed but we still | |
| 314 // want to update the tooltip because the timer has fired. | |
| 315 // If we come here from UpdateTooltip(), we have already checked for tooltip | 298 // If we come here from UpdateTooltip(), we have already checked for tooltip |
| 316 // visibility and this check below will have no effect. | 299 // visibility and this check below will have no effect. |
| 317 if (tooltip_text_ != tooltip_text || !tooltip_->IsVisible() || ids_differ) { | 300 if (tooltip_text_ != tooltip_text || !tooltip_->IsVisible() || ids_differ) { |
| 318 tooltip_shown_timer_.Stop(); | 301 tooltip_shown_timer_.Stop(); |
| 319 tooltip_text_ = tooltip_text; | 302 tooltip_text_ = tooltip_text; |
| 320 base::string16 trimmed_text = | 303 base::string16 trimmed_text = |
| 321 gfx::TruncateString(tooltip_text_, kMaxTooltipLength, gfx::WORD_BREAK); | 304 gfx::TruncateString(tooltip_text_, kMaxTooltipLength, gfx::WORD_BREAK); |
| 322 // If the string consists entirely of whitespace, then don't both showing it | 305 // If the string consists entirely of whitespace, then don't both showing it |
| 323 // (an empty tooltip is useless). | 306 // (an empty tooltip is useless). |
| 324 base::string16 whitespace_removed_text; | |
| 325 base::TrimWhitespace(trimmed_text, base::TRIM_ALL, | 307 base::TrimWhitespace(trimmed_text, base::TRIM_ALL, |
| 326 &whitespace_removed_text); | 308 &tooltip_text_whitespace_trimmed_); |
| 327 if (whitespace_removed_text.empty()) { | 309 if (tooltip_text_whitespace_trimmed_.empty()) { |
| 328 tooltip_->Hide(); | 310 tooltip_->Hide(); |
| 311 } else if (tooltip_show_delayed_) { |
| 312 // Initialize the one-shot timer to show the tooltip in a while. |
| 313 // If there is already a request queued then cancel it and post the new |
| 314 // request. This ensures that tooltip won't show up too early. |
| 315 // The delayed appearance of a tooltip is by default. |
| 316 if (tooltip_defer_timer_.IsRunning()) { |
| 317 tooltip_defer_timer_.Reset(); |
| 318 } else { |
| 319 tooltip_defer_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds( |
| 320 kDelayForTooltipUpdateInMs), |
| 321 this, &TooltipController::ShowTooltip); |
| 322 } |
| 329 } else { | 323 } else { |
| 330 gfx::Point widget_loc = curr_mouse_loc_ + | 324 ShowTooltip(); // Allow tooltip to show up without delay for unit tests. |
| 331 tooltip_window_->GetBoundsInScreen().OffsetFromOrigin(); | |
| 332 tooltip_->SetText(tooltip_window_, whitespace_removed_text, widget_loc); | |
| 333 tooltip_->Show(); | |
| 334 int timeout = GetTooltipShownTimeout(); | |
| 335 if (timeout > 0) { | |
| 336 tooltip_shown_timer_.Start(FROM_HERE, | |
| 337 base::TimeDelta::FromMilliseconds(timeout), | |
| 338 this, &TooltipController::TooltipShownTimerFired); | |
| 339 } | |
| 340 } | 325 } |
| 341 } | 326 } |
| 342 } | 327 } |
| 343 | 328 |
| 329 void TooltipController::ShowTooltip() { |
| 330 if (!tooltip_window_) |
| 331 return; |
| 332 gfx::Point widget_loc = |
| 333 curr_mouse_loc_ + tooltip_window_->GetBoundsInScreen().OffsetFromOrigin(); |
| 334 tooltip_->SetText(tooltip_window_, tooltip_text_whitespace_trimmed_, |
| 335 widget_loc); |
| 336 tooltip_->Show(); |
| 337 int timeout = GetTooltipShownTimeout(); |
| 338 if (timeout > 0) { |
| 339 tooltip_shown_timer_.Start(FROM_HERE, |
| 340 base::TimeDelta::FromMilliseconds(timeout), this, |
| 341 &TooltipController::TooltipShownTimerFired); |
| 342 } |
| 343 } |
| 344 |
| 344 bool TooltipController::IsTooltipVisible() { | 345 bool TooltipController::IsTooltipVisible() { |
| 345 return tooltip_->IsVisible(); | 346 return tooltip_->IsVisible(); |
| 346 } | 347 } |
| 347 | 348 |
| 348 bool TooltipController::IsDragDropInProgress() { | 349 bool TooltipController::IsDragDropInProgress() { |
| 349 if (!tooltip_window_) | 350 if (!tooltip_window_) |
| 350 return false; | 351 return false; |
| 351 aura::client::DragDropClient* client = | 352 aura::client::DragDropClient* client = |
| 352 aura::client::GetDragDropClient(tooltip_window_->GetRootWindow()); | 353 aura::client::GetDragDropClient(tooltip_window_->GetRootWindow()); |
| 353 return client && client->IsDragDropInProgress(); | 354 return client && client->IsDragDropInProgress(); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 378 return; | 379 return; |
| 379 if (tooltip_window_) | 380 if (tooltip_window_) |
| 380 tooltip_window_->RemoveObserver(this); | 381 tooltip_window_->RemoveObserver(this); |
| 381 tooltip_window_ = target; | 382 tooltip_window_ = target; |
| 382 if (tooltip_window_) | 383 if (tooltip_window_) |
| 383 tooltip_window_->AddObserver(this); | 384 tooltip_window_->AddObserver(this); |
| 384 } | 385 } |
| 385 | 386 |
| 386 } // namespace corewm | 387 } // namespace corewm |
| 387 } // namespace views | 388 } // namespace views |
| OLD | NEW |