react-x11 2.26.1 → 2.26.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cocoa/app.js +6 -0
- package/src/cocoa/fonts.js +23 -5
- package/src/events.js +84 -1
- package/src/frame/index.js +5 -0
package/package.json
CHANGED
package/src/cocoa/app.js
CHANGED
|
@@ -259,6 +259,12 @@ export class CocoaApp {
|
|
|
259
259
|
? new CocoaFilePanels(this)
|
|
260
260
|
: null;
|
|
261
261
|
|
|
262
|
+
// A window that is not the active one does not track the pointer here:
|
|
263
|
+
// no `:hover`, no enter or leave, no motion, until it is the active one
|
|
264
|
+
// again (`EventManager.hoverLive`). The platform's convention and not
|
|
265
|
+
// X11's, where every toolkit hovers in a window without the keyboard.
|
|
266
|
+
this.hoverNeedsActiveWindow = true;
|
|
267
|
+
|
|
262
268
|
// AppKit-rendered control bezels. Its *presence* is the capability:
|
|
263
269
|
// `useSupports('nativeControls')` and the widget set's `controls:
|
|
264
270
|
// 'auto'` policy both test for this property, so a backend without it
|
package/src/cocoa/fonts.js
CHANGED
|
@@ -76,13 +76,30 @@ function woffToSfnt(woff) {
|
|
|
76
76
|
return out;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The system font's two names from before CSS gave it one: `-apple-system`
|
|
81
|
+
* is WebKit's and Gecko's, `BlinkMacSystemFont` is Blink's, and a stack
|
|
82
|
+
* written for the web leads with both — GitHub's is `-apple-system,
|
|
83
|
+
* BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial,
|
|
84
|
+
* sans-serif`. CSS Fonts 4 standardised them as `system-ui`, the name the
|
|
85
|
+
* bridge knows. As families of their own neither exists, so the list moved
|
|
86
|
+
* on to the first one that does and a page written for SF was set in
|
|
87
|
+
* Helvetica. They name this platform's face and mean nothing on another,
|
|
88
|
+
* which is why they are resolved here and not in the X11 engine.
|
|
89
|
+
*/
|
|
90
|
+
const SYSTEM_UI_ALIASES = new Set(['-apple-system', 'blinkmacsystemfont']);
|
|
91
|
+
|
|
79
92
|
/** family list string -> array: 'Inter, "SF Pro", sans-serif' */
|
|
80
93
|
function familyList(family) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
const names = Array.isArray(family)
|
|
95
|
+
? family
|
|
96
|
+
: String(family ?? 'sans-serif')
|
|
97
|
+
.split(',')
|
|
98
|
+
.map((f) => f.trim().replace(/^["']|["']$/g, ''))
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
return names.map((name) =>
|
|
101
|
+
SYSTEM_UI_ALIASES.has(String(name).toLowerCase()) ? 'system-ui' : name,
|
|
102
|
+
);
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
function numericWeight(weight) {
|
|
@@ -519,6 +536,7 @@ const GENERIC_FAMILIES = new Set([
|
|
|
519
536
|
'system-ui',
|
|
520
537
|
'ui-sans-serif',
|
|
521
538
|
'ui-monospace',
|
|
539
|
+
...SYSTEM_UI_ALIASES,
|
|
522
540
|
]);
|
|
523
541
|
|
|
524
542
|
/** The size a face is probed at when the question has no size in it. */
|
package/src/events.js
CHANGED
|
@@ -647,6 +647,12 @@ export class EventManager {
|
|
|
647
647
|
// …and the focused node hears the focus group's answer, not this
|
|
648
648
|
// window's, whichever of them the X focus just moved between
|
|
649
649
|
manager._syncDefaultFocus();
|
|
650
|
+
// …and so does hover, where the platform keeps it to the active window:
|
|
651
|
+
// this window's, and the top-level's whose answer it is — a dialog
|
|
652
|
+
// taking the keyboard is what puts its owner's hover back
|
|
653
|
+
const top = this.topLevelManager;
|
|
654
|
+
this._syncHover(native);
|
|
655
|
+
if (top !== this) top._syncHover(native);
|
|
650
656
|
if (!changed) return;
|
|
651
657
|
// …and the things that keep a window of their own open on the strength
|
|
652
658
|
// of this one having focus — a menu, a dropdown — which the focused node
|
|
@@ -1014,6 +1020,12 @@ export class EventManager {
|
|
|
1014
1020
|
inspectHandler('move', this._hit(native), native);
|
|
1015
1021
|
return;
|
|
1016
1022
|
}
|
|
1023
|
+
// a window the pointer does not hover in hears no motion (`hoverLive`);
|
|
1024
|
+
// a gesture that began in it is the pointer's own business and runs on
|
|
1025
|
+
if (!this.hoverLive && !this._gesture()) {
|
|
1026
|
+
this._dropHover(native);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1017
1029
|
runWithPriority(ContinuousEventPriority, () => {
|
|
1018
1030
|
// an active drag owns the pointer: drag-path diffing replaces hover,
|
|
1019
1031
|
// onDrag replaces mousemove. Below the threshold this falls through.
|
|
@@ -1088,6 +1100,77 @@ export class EventManager {
|
|
|
1088
1100
|
return isFocusable(node);
|
|
1089
1101
|
}
|
|
1090
1102
|
|
|
1103
|
+
/**
|
|
1104
|
+
* Whether the pointer hovers in this window at all.
|
|
1105
|
+
*
|
|
1106
|
+
* Nothing in CSS or in the DOM's events ties hover to focus: `:hover` is
|
|
1107
|
+
* "while the user designates an element with a pointing device", and a
|
|
1108
|
+
* `mousemove` is owed whenever the pointer moves over an element. What
|
|
1109
|
+
* ties them is the platform. On macOS a window that is not the active one
|
|
1110
|
+
* does not track the pointer — AppKit's own controls roll over in the key
|
|
1111
|
+
* window alone, and a browser there drops the moves over a window that is
|
|
1112
|
+
* neither key nor main and tells the page the pointer left — while on
|
|
1113
|
+
* X11, Wayland and Windows every toolkit hovers in a window that does not
|
|
1114
|
+
* have the keyboard, and with focus-follows-mouse the question hardly
|
|
1115
|
+
* arises. So the backend says which it is (`app.hoverNeedsActiveWindow`),
|
|
1116
|
+
* and only the Cocoa one does.
|
|
1117
|
+
*
|
|
1118
|
+
* It is also half enforced there already, which is how this was found:
|
|
1119
|
+
* a cursor set by an application that is not the active one is ignored by
|
|
1120
|
+
* the window server, so a link in a background window lit up and the
|
|
1121
|
+
* pointer over it stayed an arrow.
|
|
1122
|
+
*
|
|
1123
|
+
* "Active" is the top-level's answer for everything sharing its focus
|
|
1124
|
+
* (`keyboardFocused`), so a window whose dialog has the keyboard still
|
|
1125
|
+
* hovers, as a main window does under a key panel. A popup nothing
|
|
1126
|
+
* manages — a menu, a dropdown, a popover under a tray item — is on the
|
|
1127
|
+
* screen because the user just asked for it, in an application that may
|
|
1128
|
+
* have no active window at all, and always hovers.
|
|
1129
|
+
*/
|
|
1130
|
+
get hoverLive() {
|
|
1131
|
+
if (!this.node.app?.hoverNeedsActiveWindow) return true;
|
|
1132
|
+
if (this.node.isPopup && this.node.attributes?.overrideRedirect === true) {
|
|
1133
|
+
return true;
|
|
1134
|
+
}
|
|
1135
|
+
return this.topLevelManager.keyboardFocused;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/** A press, a capture or a drag holds the pointer. */
|
|
1139
|
+
_gesture() {
|
|
1140
|
+
return Boolean(this._captured() || this.downNode || this._dragArmed);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* The pointer is over the window and hovers nothing in it: what its
|
|
1145
|
+
* leaving does to hover, less the leaving. `_pointer` stays, so the hover
|
|
1146
|
+
* comes back at that point when the window does (`_syncHover`), and
|
|
1147
|
+
* `<window onMouseOut>` is not called, since the pointer is still in it.
|
|
1148
|
+
*/
|
|
1149
|
+
_dropHover(native) {
|
|
1150
|
+
if (!this.hoverPath.length && !this.attentionNode) return;
|
|
1151
|
+
runWithPriority(ContinuousEventPriority, () => {
|
|
1152
|
+
this._updateHover([], native);
|
|
1153
|
+
if (this._attentionNodes.size !== 0) {
|
|
1154
|
+
this._attentionSamples.length = 0;
|
|
1155
|
+
this._setAttention(null, native);
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* The keyboard moved between windows: hover goes with it where the
|
|
1162
|
+
* platform has it follow (`hoverLive`). A window that stopped being the
|
|
1163
|
+
* active one drops its hover as if the pointer had left; one that became
|
|
1164
|
+
* it is asked what is under the pointer, which did not move to say so.
|
|
1165
|
+
* Not during a gesture, which keeps what it has until it ends.
|
|
1166
|
+
*/
|
|
1167
|
+
_syncHover(native) {
|
|
1168
|
+
if (!this.node.app?.hoverNeedsActiveWindow) return;
|
|
1169
|
+
if (this.node.destroyed || this._gesture()) return;
|
|
1170
|
+
if (this.hoverLive) this.refreshHover();
|
|
1171
|
+
else this._dropHover({ ...native, ...this._pointer });
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1091
1174
|
_onMouseOut(native) {
|
|
1092
1175
|
// The pointer went into one of this window's surfaces, not out of the
|
|
1093
1176
|
// window: X reports crossing into a child window as leaving this one,
|
|
@@ -1211,7 +1294,7 @@ export class EventManager {
|
|
|
1211
1294
|
refreshHover() {
|
|
1212
1295
|
const at = this._pointer;
|
|
1213
1296
|
if (!at || inspectHandler || this.node.destroyed) return;
|
|
1214
|
-
if (this.
|
|
1297
|
+
if (this._gesture() || !this.hoverLive) return;
|
|
1215
1298
|
runWithPriority(ContinuousEventPriority, () => {
|
|
1216
1299
|
const target = this._hit(at);
|
|
1217
1300
|
const path = this._path(target);
|
package/src/frame/index.js
CHANGED
|
@@ -618,6 +618,11 @@ function PaneHostView({
|
|
|
618
618
|
onMouseDown: forward('mousedown', (ev) => ({ keycode: ev.button ?? 1 })),
|
|
619
619
|
onMouseUp: forward('mouseup', (ev) => ({ keycode: ev.button ?? 1 })),
|
|
620
620
|
onMouseMove: forward('mousemove', () => ({})),
|
|
621
|
+
// …and the pointer leaving, which the pane has no window to hear for
|
|
622
|
+
// itself: without it what the pointer last crossed in the pane stayed
|
|
623
|
+
// hovered once it was over the host, or the host was no longer the
|
|
624
|
+
// active window (`EventManager.hoverLive`)
|
|
625
|
+
onMouseLeave: forward('mouseout', () => ({})),
|
|
621
626
|
// The host's event is in pixels — events.js turned each notch into
|
|
622
627
|
// WHEEL_NOTCH_PX of them — and the pane's window turns what it is sent
|
|
623
628
|
// into pixels again, so it is sent notches: sent the pixels, one notch
|