Bug 1712669 - Make Windows tooltip margin depend on cursor size. r=yjuglaret,win-reviewers,desktop-theme-reviewers,dao

I think we should start off with this. Other platforms can implement
this too, but exposing this to content seems like quite the rabbit hole.

Differential Revision: https://phabricator.services.mozilla.com/D203405
This commit is contained in:
Emilio Cobos Álvarez 2024-03-19 14:17:58 +00:00
parent 50bd75fa63
commit ade0599fdf
8 changed files with 49 additions and 10 deletions

View file

@ -2186,6 +2186,12 @@ nsMargin nsMenuPopupFrame::GetMargin() const {
margin.left += auOffset.x; margin.left += auOffset.x;
margin.right += auOffset.x; margin.right += auOffset.x;
} }
if (mPopupType == PopupType::Tooltip) {
const auto auOffset =
CSSPixel::ToAppUnits(LookAndFeel::TooltipOffsetVertical());
margin.top += auOffset;
margin.bottom += auOffset;
}
return margin; return margin;
} }

View file

@ -30,6 +30,8 @@ var gOriginalWidth = -1;
var gOriginalHeight = -1; var gOriginalHeight = -1;
var gButton = null; var gButton = null;
const kTooltipOffsetVertical = 10;
function runTest() function runTest()
{ {
startPopupTests(popupTests); startPopupTests(popupTests);
@ -112,7 +114,7 @@ var popupTests = [
Math.round(buttonrect.left + parseFloat(popupstyle.marginLeft) + 6), Math.round(buttonrect.left + parseFloat(popupstyle.marginLeft) + 6),
testname + " left position of tooltip"); testname + " left position of tooltip");
is(Math.round(rect.top), is(Math.round(rect.top),
Math.round(buttonrect.top + parseFloat(popupstyle.marginTop) + 6), Math.round(buttonrect.top + parseFloat(popupstyle.marginTop) + 6 + kTooltipOffsetVertical),
testname + " top position of tooltip"); testname + " top position of tooltip");
var labelrect = document.getElementById("label").getBoundingClientRect(); var labelrect = document.getElementById("label").getBoundingClientRect();
@ -159,7 +161,7 @@ var popupTests = [
Math.round(buttonrect.left + parseFloat(popupstyle.marginLeft) + 4), Math.round(buttonrect.left + parseFloat(popupstyle.marginLeft) + 4),
testname + " left position of tooltip"); testname + " left position of tooltip");
is(Math.round(rect.top), is(Math.round(rect.top),
Math.round(buttonrect.top + parseFloat(popupstyle.marginTop) + 4), Math.round(buttonrect.top + parseFloat(popupstyle.marginTop) + 4 + kTooltipOffsetVertical),
testname + " top position of tooltip"); testname + " top position of tooltip");
var labelrect = document.getElementById("label").getBoundingClientRect(); var labelrect = document.getElementById("label").getBoundingClientRect();
@ -204,7 +206,7 @@ var popupTests = [
Math.round(buttonrect.left + parseFloat(popupstyle.marginLeft) + 6), Math.round(buttonrect.left + parseFloat(popupstyle.marginLeft) + 6),
testname + " left position of tooltip"); testname + " left position of tooltip");
is(Math.round(rect.top), is(Math.round(rect.top),
Math.round(buttonrect.top + parseFloat(popupstyle.marginTop) + 6), Math.round(buttonrect.top + parseFloat(popupstyle.marginTop) + 6 + kTooltipOffsetVertical),
testname + " top position of tooltip"); testname + " top position of tooltip");
var labelrect = document.getElementById("label").getBoundingClientRect(); var labelrect = document.getElementById("label").getBoundingClientRect();
@ -359,7 +361,14 @@ function moveWindowTo(x, y, callback, arg)
} }
} }
window.arguments[0].SimpleTest.waitForFocus(runTest, window); (async function() {
let parent = window.arguments[0];
await Promise.all([
parent.SimpleTest.promiseFocus(window),
parent.SpecialPowers.pushPrefEnv({set: [["ui.tooltipOffsetVertical", kTooltipOffsetVertical]]}),
]);
runTest();
})();
]]> ]]>
</script> </script>

View file

@ -389,10 +389,6 @@ tooltip {
pointer-events: none; pointer-events: none;
} }
tooltip:not([position]) {
margin-top: 21px;
}
/** /**
* It's important that these styles are in a UA sheet, because the default * It's important that these styles are in a UA sheet, because the default
* tooltip is native anonymous content * tooltip is native anonymous content

View file

@ -186,6 +186,7 @@ class LookAndFeel {
*/ */
ContextMenuOffsetVertical, ContextMenuOffsetVertical,
ContextMenuOffsetHorizontal, ContextMenuOffsetHorizontal,
TooltipOffsetVertical,
/* /*
* A boolean value indicating whether client-side decorations are * A boolean value indicating whether client-side decorations are
@ -321,6 +322,11 @@ class LookAndFeel {
return GetInt(IntID::UseOverlayScrollbars); return GetInt(IntID::UseOverlayScrollbars);
} }
static constexpr int32_t kDefaultTooltipOffset = 21;
static int32_t TooltipOffsetVertical() {
return GetInt(IntID::TooltipOffsetVertical, kDefaultTooltipOffset);
}
// Returns keyCode value of a modifier key which is used for accesskey. // Returns keyCode value of a modifier key which is used for accesskey.
// Returns 0 if the platform doesn't support access key. // Returns 0 if the platform doesn't support access key.
static uint32_t GetMenuAccessKey(); static uint32_t GetMenuAccessKey();

View file

@ -167,6 +167,7 @@ static const char sIntPrefs[][45] = {
"ui.scrollbarFadeDuration", "ui.scrollbarFadeDuration",
"ui.contextMenuOffsetVertical", "ui.contextMenuOffsetVertical",
"ui.contextMenuOffsetHorizontal", "ui.contextMenuOffsetHorizontal",
"ui.tooltipOffsetVertical",
"ui.GtkCSDAvailable", "ui.GtkCSDAvailable",
"ui.GtkCSDMinimizeButton", "ui.GtkCSDMinimizeButton",
"ui.GtkCSDMaximizeButton", "ui.GtkCSDMaximizeButton",

View file

@ -36,6 +36,17 @@ static int32_t GetSystemParam(long flag, int32_t def) {
return ::SystemParametersInfo(flag, 0, &value, 0) ? value : def; return ::SystemParametersInfo(flag, 0, &value, 0) ? value : def;
} }
static int32_t GetTooltipOffsetVertical() {
static constexpr DWORD kDefaultCursorSize = 32;
const DWORD cursorSize =
GetSystemParam(MOZ_SPI_CURSORSIZE, kDefaultCursorSize);
if (cursorSize == kDefaultCursorSize) {
return LookAndFeel::kDefaultTooltipOffset;
}
return std::ceilf(float(LookAndFeel::kDefaultTooltipOffset) *
float(cursorSize) / float(kDefaultCursorSize));
}
static bool SystemWantsDarkTheme() { static bool SystemWantsDarkTheme() {
if (nsUXThemeData::IsHighContrastOn()) { if (nsUXThemeData::IsHighContrastOn()) {
return LookAndFeel::IsDarkColor( return LookAndFeel::IsDarkColor(
@ -523,6 +534,9 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
case IntID::ContextMenuOffsetHorizontal: case IntID::ContextMenuOffsetHorizontal:
aResult = 2; aResult = 2;
break; break;
case IntID::TooltipOffsetVertical:
aResult = GetTooltipOffsetVertical();
break;
case IntID::SystemUsesDarkTheme: case IntID::SystemUsesDarkTheme:
aResult = SystemWantsDarkTheme(); aResult = SystemWantsDarkTheme();
break; break;

View file

@ -40,6 +40,10 @@
#define SYS_COLOR_MAX 30 #define SYS_COLOR_MAX 30
#define SYS_COLOR_COUNT (SYS_COLOR_MAX - SYS_COLOR_MIN + 1) #define SYS_COLOR_COUNT (SYS_COLOR_MAX - SYS_COLOR_MIN + 1)
// Undocumented SPI, see bug 1712669 comment 4.
#define MOZ_SPI_CURSORSIZE 0x2028
#define MOZ_SPI_SETCURSORSIZE 0x2029
namespace mozilla::widget::WinRegistry { namespace mozilla::widget::WinRegistry {
class KeyWatcher; class KeyWatcher;
} }

View file

@ -78,6 +78,7 @@
#include <limits> #include <limits>
#include "mozilla/widget/WinMessages.h" #include "mozilla/widget/WinMessages.h"
#include "nsLookAndFeel.h"
#include "nsWindow.h" #include "nsWindow.h"
#include "nsWindowTaskbarConcealer.h" #include "nsWindowTaskbarConcealer.h"
#include "nsAppRunner.h" #include "nsAppRunner.h"
@ -4939,10 +4940,12 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam,
case WM_SETTINGCHANGE: { case WM_SETTINGCHANGE: {
if (wParam == SPI_SETCLIENTAREAANIMATION || if (wParam == SPI_SETCLIENTAREAANIMATION ||
wParam == SPI_SETKEYBOARDDELAY || wParam == SPI_SETMOUSEVANISH) { wParam == SPI_SETKEYBOARDDELAY || wParam == SPI_SETMOUSEVANISH ||
wParam == MOZ_SPI_SETCURSORSIZE) {
// These need to update LookAndFeel cached values. // These need to update LookAndFeel cached values.
// They affect reduced motion settings / caret blink count / show // They affect reduced motion settings / caret blink count / show
// pointer while typing, so no need to invalidate style / layout. // pointer while typing / tooltip offset, so no need to invalidate style
// / layout.
NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly); NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly);
break; break;
} }