blob: a1935191504505c89acf90b90549571783209048 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/corewm/tooltip_state_manager.h"
#include <stddef.h>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "ui/gfx/text_elider.h"
#include "ui/wm/public/tooltip_client.h"
#include "ui/wm/public/tooltip_observer.h"
namespace views::corewm {
namespace {
#if BUILDFLAG(IS_WIN)
// Drawing a long word in tooltip is very slow on Windows. crbug.com/513693
constexpr size_t kMaxTooltipLength = 1024;
#else
constexpr size_t kMaxTooltipLength = 2048;
#endif
} // namespace
TooltipStateManager::TooltipStateManager(std::unique_ptr<Tooltip> tooltip)
: tooltip_(std::move(tooltip)) {}
TooltipStateManager::~TooltipStateManager() = default;
void TooltipStateManager::AddObserver(wm::TooltipObserver* observer) {
DCHECK(tooltip_);
tooltip_->AddObserver(observer);
}
void TooltipStateManager::RemoveObserver(wm::TooltipObserver* observer) {
DCHECK(tooltip_);
tooltip_->RemoveObserver(observer);
}
int TooltipStateManager::GetMaxWidth(const gfx::Point& location) const {
return tooltip_->GetMaxWidth(location);
}
void TooltipStateManager::HideAndReset() {
// Hide any open tooltips.
will_hide_tooltip_timer_.Stop();
tooltip_->Hide();
// Cancel pending tooltips and reset states.
will_show_tooltip_timer_.Stop();
tooltip_id_ = nullptr;
tooltip_parent_window_ = nullptr;
}
void TooltipStateManager::Show(aura::Window* window,
const std::u16string& tooltip_text,
const gfx::Point& position,
TooltipTrigger trigger,
const base::TimeDelta show_delay,
const base::TimeDelta hide_delay) {
HideAndReset();
position_ = position;
tooltip_id_ = wm::GetTooltipId(window);
tooltip_text_ = tooltip_text;
tooltip_parent_window_ = window;
tooltip_trigger_ = trigger;
std::u16string truncated_text =
gfx::TruncateString(tooltip_text_, kMaxTooltipLength, gfx::WORD_BREAK);
std::u16string trimmed_text;
base::TrimWhitespace(truncated_text, base::TRIM_ALL, &trimmed_text);
// If the string consists entirely of whitespace, then don't both showing it
// (an empty tooltip is useless).
if (trimmed_text.empty()) {
return;
}
// Initialize the one-shot timer to show the tooltip after a delay. Any
// running timers have already been canceled by calling HideAndReset above.
// This ensures that the tooltip won't show up too early. The delayed
// appearance of a tooltip is by default and the `show_delay` is only set to
// 0 in the unit tests.
StartWillShowTooltipTimer(trimmed_text, show_delay, hide_delay);
}
void TooltipStateManager::UpdatePositionIfNeeded(const gfx::Point& position,
TooltipTrigger trigger) {
// The position should only be updated when the tooltip has been triggered but
// is not yet visible. Also, we only want to allow the update when it's set
// off by the same trigger that started the |will_show_tooltip_timer_| in
// the first place. Otherwise, for example, the position of a keyboard
// triggered tooltip could be updated by an unrelated mouse exited event. The
// tooltip would then show up at the wrong location.
if (!will_show_tooltip_timer_.IsRunning() || trigger != tooltip_trigger_) {
return;
}
position_ = position;
}
void TooltipStateManager::ShowNow(const std::u16string& trimmed_text,
const base::TimeDelta hide_delay) {
if (!tooltip_parent_window_) {
return;
}
if (!tooltip_parent_window_->GetRootWindow()) {
// This can happen if the window is in the process of closing.
return;
}
tooltip_->Update(tooltip_parent_window_, trimmed_text, position_,
tooltip_trigger_);
tooltip_->Show();
if (!hide_delay.is_zero()) {
will_hide_tooltip_timer_.Start(FROM_HERE, hide_delay, this,
&TooltipStateManager::HideAndReset);
}
}
void TooltipStateManager::StartWillShowTooltipTimer(
const std::u16string& trimmed_text,
const base::TimeDelta show_delay,
const base::TimeDelta hide_delay) {
if (!show_delay.is_zero()) {
// Start will show tooltip timer is show_delay is non-zero.
will_show_tooltip_timer_.Start(
FROM_HERE, show_delay,
base::BindOnce(&TooltipStateManager::ShowNow,
weak_factory_.GetWeakPtr(), trimmed_text, hide_delay));
return;
} else {
// This other path is needed for the unit tests to pass because Show is not
// immediately called when we have a `show_delay` of zero.
// TODO(bebeaudr): Fix this by ensuring that the unit tests wait for the
// timer to fire before continuing.
ShowNow(trimmed_text, hide_delay);
}
}
} // namespace views::corewm