blob: ded314fc0a2e6df1aff945e51b0f46a78a061c0a [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_COREWM_TOOLTIP_CONTROLLER_H_
#define UI_VIEWS_COREWM_TOOLTIP_CONTROLLER_H_
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "ui/aura/client/cursor_client_observer.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tracker.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/point.h"
#include "ui/views/corewm/tooltip.h"
#include "ui/views/views_export.h"
#include "ui/wm/public/activation_change_observer.h"
#include "ui/wm/public/tooltip_client.h"
namespace aura {
class Window;
}
namespace gfx {
class Rect;
} // namespace gfx
namespace wm {
class ActivationClient;
class TooltipObserver;
} // namespace wm
namespace views::corewm {
class Tooltip;
class TooltipStateManager;
namespace test {
class TooltipControllerTestHelper;
} // namespace test
// TooltipController listens for events that can have an impact on the
// tooltip state.
class VIEWS_EXPORT TooltipController
: public wm::TooltipClient,
public ui::EventHandler,
public aura::client::CursorClientObserver,
public aura::WindowObserver,
public wm::ActivationChangeObserver {
public:
TooltipController(std::unique_ptr<Tooltip> tooltip,
wm::ActivationClient* activation_client);
TooltipController(const TooltipController&) = delete;
TooltipController& operator=(const TooltipController&) = delete;
~TooltipController() override;
void AddObserver(wm::TooltipObserver* observer);
void RemoveObserver(wm::TooltipObserver* observer);
// Overridden from wm::TooltipClient.
int GetMaxWidth(const gfx::Point& location) const override;
void UpdateTooltip(aura::Window* target) override;
void UpdateTooltipFromKeyboard(const gfx::Rect& bounds,
aura::Window* target) override;
bool IsTooltipSetFromKeyboard(aura::Window* target) override;
void SetHideTooltipTimeout(aura::Window* target,
base::TimeDelta timeout) override;
void SetTooltipsEnabled(bool enable) override;
// Overridden from ui::EventHandler.
void OnKeyEvent(ui::KeyEvent* event) override;
void OnMouseEvent(ui::MouseEvent* event) override;
void OnTouchEvent(ui::TouchEvent* event) override;
void OnCancelMode(ui::CancelModeEvent* event) override;
std::string_view GetLogContext() const override;
// Overridden from aura::client::CursorClientObserver.
void OnCursorVisibilityChanged(bool is_visible) override;
// Overridden from aura::WindowObserver.
void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
void OnWindowDestroying(aura::Window* window) override;
void OnWindowDestroyed(aura::Window* window) override;
void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) override;
// Overridden from wm::ActivationChangeObserver.
void OnWindowActivated(ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) override;
// Updates tooltip triggered by keyboard with `anchor_point` value.
// This should be called instead of UpdateTooltipFromKeyboard() when the
// anchor point is already calculated (e.g. Exo).
void UpdateTooltipFromKeyboardWithAnchorPoint(const gfx::Point& anchor_point,
aura::Window* target);
// Sets show tooltip delay for `target` window.
void SetShowTooltipDelay(aura::Window* target, base::TimeDelta delay);
private:
friend class test::TooltipControllerTestHelper;
// Resets the window and calls `TooltipStateManager::HideAndReset`.
void HideAndReset();
// Updates the tooltip if required (if there is any change in the tooltip
// text, tooltip id or the aura::Window).
void UpdateIfRequired(TooltipTrigger trigger);
// Returns true if there's a drag-and-drop in progress.
bool IsDragDropInProgress() const;
// Returns true if the cursor is visible.
bool IsCursorVisible() const;
// Gets the delay after which the tooltip should be shown/hidden.
base::TimeDelta GetShowTooltipDelay();
base::TimeDelta GetHideTooltipDelay();
// Sets observed window to |target| if it is different from existing window.
// Calls RemoveObserver on the existing window if it is not NULL.
// Calls AddObserver on the new window if it is not NULL.
void SetObservedWindow(aura::Window* target);
// Returns true if the tooltip id stored on the state manager and the one
// stored on the window are different.
bool IsTooltipIdUpdateNeeded() const;
// Returns true if the tooltip text stored on the state manager and the one
// stored on the window are different.
bool IsTooltipTextUpdateNeeded() const;
// Removes show/hide tooltip delay from `show_tooltip_delay_map_` and
// `hide_tooltip_timeout_map_`.
void RemoveTooltipDelayFromMap(aura::Window* window);
// Stops tracking the window on which the cursor was when the mouse was
// pressed if we're on another window or if a new tooltip is triggered by
// keyboard.
void ResetWindowAtMousePressedIfNeeded(aura::Window* target,
bool force_reset);
// To prevent the tooltip to show again after a mouse press event, we want
// to hide it until the cursor moves to another window.
bool ShouldHideBecauseMouseWasOncePressed();
// Returns true if the current event is a duplicate event generated by a
// pen/stylus hovering over the same window.
bool IsDuplicatePenHoverEvent(ui::EventPointerType pointer_type);
aura::Window* tooltip_window_at_mouse_press() {
auto& windows = tooltip_window_at_mouse_press_tracker_.windows();
return windows.empty() ? nullptr : windows[0];
}
// The window on which we are currently listening for events. When there's a
// keyboard-triggered visible tooltip, its value is set to the tooltip parent
// window. Otherwise, it's following the cursor.
raw_ptr<aura::Window> observed_window_ = nullptr;
// These fields are for tracking state when the user presses a mouse button.
// The tooltip should stay hidden after a mouse press event on the view until
// the cursor moves to another view.
std::u16string tooltip_text_at_mouse_press_;
// NOTE: this either has zero or one window.
aura::WindowTracker tooltip_window_at_mouse_press_tracker_;
// Location of the last events in |tooltip_window_|'s coordinates.
// |last_mouse_loc_| and |last_focus_loc_| are used to position tooltips
// triggered by either the mouse or the keyboard, respectively. We also
// track |last_mouse_loc_| to be able to ignore spurious and/or redundant
// events.
gfx::Point last_mouse_loc_;
gfx::Point last_touch_loc_;
gfx::Point last_focus_loc_;
// True if the current event is a duplicate event generated by hovering with a
// pen/stylus. Hovering with a pen generates a constant stream of move events,
// so duplicate pen events on the same window should be ignored to prevent the
// tooltip's show timer from being restarted on each event.
bool is_duplicate_pen_hover_event_ = false;
// The last tooltip text that was shown when the pen was hovering.
// TODO(crbug.com/40246278): Replace this with a unique tooltip identifier
// when one is implemented. For now, the tooltip text is the closest thing to
// a tooltip identifier there is.
std::u16string last_pen_tooltip_text_;
// Whether tooltips can be displayed or not.
bool tooltips_enabled_ = true;
// Whether tooltip should be skip delay before showing.
// This may be set to true only for testing.
// Do NOT override this value except from TooltipControllerTestHelper.
bool skip_show_delay_for_testing_ = false;
// The show delay before showing tooltip may differ for external app's
// tooltip. This map specifies the show delay for each target window.
// TODO(crbug.com/c/374244480): consider removing this when removing lacros
// support code from //c/exo. This is only used by aura shell to show tooltips
// with delays for external apps.
std::map<aura::Window*, base::TimeDelta> show_tooltip_delay_map_;
// Web content tooltips should be shown indefinitely and those added on Views
// should be hidden automatically after a timeout. This map stores the timeout
// value for each aura::Window.
// TODO(bebeaudr): Currently, all Views tooltips are hidden after the same
// timeout and all web content views should be shown indefinitely. If this
// general rule is always true, then we don't need a complex map here. A set
// of aura::Window* would be enough with an attribute named
// "disabled_hide_timeout_views_set_" or something like that.
std::map<aura::Window*, base::TimeDelta> hide_tooltip_timeout_map_;
// We want to hide tooltips whenever our client window loses focus. This will
// ensure that no tooltip stays visible when the user navigated away from
// our client.
raw_ptr<wm::ActivationClient> activation_client_;
// The TooltipStateManager is responsible for keeping track of the current
// tooltip state (its text, position, id, etc.) and to modify it when asked
// by the TooltipController or the show/hide timers.
std::unique_ptr<TooltipStateManager> state_manager_;
};
} // namespace views::corewm
#endif // UI_VIEWS_COREWM_TOOLTIP_CONTROLLER_H_