1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
// 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
|