react-x11 2.18.1 → 2.18.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.
@@ -101,8 +101,8 @@ export class WindowFlush {
101
101
  return changed;
102
102
  }
103
103
 
104
- /** …and their paint, with the frame's own damage: a claim inside a
105
- * `<glarea>`'s children is in that list like any other. */
104
+ /** …and their paint, with the damage the panes are owed — their own
105
+ * list, which the claims that can reach them go to (`_paneReach`). */
106
106
  _paintOverlays(damage) {
107
107
  for (const area of this._overlaid) area._paintOverlay(damage);
108
108
  }
@@ -195,13 +195,21 @@ export class WindowFlush {
195
195
  // A bounded frame watches the walk: whatever this pass actually moved
196
196
  // claims its old and new rects through the sink, and the frame stays
197
197
  // a few rects instead of the whole window. An unbounded frame skips
198
- // the bookkeeping — it repaints everything anyway.
199
- if (this._damage !== FULL_DAMAGE) {
198
+ // the bookkeeping — it repaints everything anyway — unless the panes
199
+ // over a `<glarea>` are still bounded: they paint from a list of
200
+ // their own, and a move of the children they hold is theirs to claim.
201
+ const panes = this._overlaid.size !== 0;
202
+ if (
203
+ this._damage !== FULL_DAMAGE ||
204
+ (panes && this._paneDamage !== FULL_DAMAGE)
205
+ ) {
200
206
  const cap = this._damageRectCap();
201
207
  layoutDiff.sink = (rect) => {
202
- if (this._damage === FULL_DAMAGE) return;
203
- layoutMoved = true;
204
- this._damage = addDamageRect(this._damage, rect, cap);
208
+ if (this._damage !== FULL_DAMAGE) {
209
+ layoutMoved = true;
210
+ this._damage = addDamageRect(this._damage, rect, cap);
211
+ }
212
+ if (panes) this._addPaneDamage(rect);
205
213
  };
206
214
  }
207
215
  try {
@@ -233,7 +241,13 @@ export class WindowFlush {
233
241
  for (const node of this._reflowed) {
234
242
  // …and the pre-mutation walk this frame reused goes with it
235
243
  node._reflowBefore = null;
236
- if (node.destroyed || this._damage === FULL_DAMAGE) continue;
244
+ if (
245
+ node.destroyed ||
246
+ (this._damage === FULL_DAMAGE &&
247
+ (!panes || this._paneDamage === FULL_DAMAGE))
248
+ ) {
249
+ continue;
250
+ }
237
251
  // clipped to a blitting viewport above it, like every other claim
238
252
  // this frame, and written to that viewport's ledger too (issue
239
253
  // #398): the claim would otherwise coalesce into the scroll's own
@@ -245,11 +259,14 @@ export class WindowFlush {
245
259
  if (sv && !sv._recordBlitClaim(after)) {
246
260
  sv._pendingBlitFrom = BLIT_POISONED;
247
261
  }
248
- this._damage = addDamageRect(
249
- this._damage,
250
- after,
251
- this._damageRectCap(),
252
- );
262
+ if (this._damage !== FULL_DAMAGE) {
263
+ this._damage = addDamageRect(
264
+ this._damage,
265
+ after,
266
+ this._damageRectCap(),
267
+ );
268
+ }
269
+ if (panes) this._addPaneDamage(after);
253
270
  }
254
271
  this._reflowed.clear();
255
272
  } else if (this._reflowed.size) {
@@ -286,6 +303,10 @@ export class WindowFlush {
286
303
  if (!this.needsPaint) return false;
287
304
  this.needsPaint = false;
288
305
  const damage = this._takeDamage(width, height);
306
+ // …and the panes' own, which they paint instead of the window's
307
+ const paneDamage =
308
+ this._overlaid.size !== 0 ? this._takePaneDamage(width, height) : null;
309
+ this._paneDamage = null;
289
310
  if (debugPaint === 'full' && !damage && width > 0 && height > 0) {
290
311
  // Silent full-window repaints are the perf bug class this renderer
291
312
  // actually has (see AGENTS.md); this is what surfaces them. The stack
@@ -305,8 +326,8 @@ export class WindowFlush {
305
326
  // list was still taken (its bookkeeping is what keeps the two paths one
306
327
  // code) and is simply not consumed; the presenter diffs at the layer.
307
328
  if (typeof this.window.presentFrame === 'function') {
308
- // the panes are no presenter's: they paint from the damage either way
309
- if (this._overlaid.size !== 0) this._paintOverlays(damage);
329
+ // the panes are no presenter's: they paint from their damage either way
330
+ if (this._overlaid.size !== 0) this._paintOverlays(paneDamage);
310
331
  this.window.presentFrame(this, damage);
311
332
  this.app._reactX11Startup?.painted();
312
333
  return true;
@@ -328,10 +349,11 @@ export class WindowFlush {
328
349
  for (const rect of damage ?? [null]) {
329
350
  this._paintRegion(ctx, rect, width, height);
330
351
  }
331
- // …and the panes over the surfaces, with the same damage: a claim from
352
+ // …and the panes over the surfaces, with their own damage: a claim from
332
353
  // a `<glarea>`'s children is theirs to repaint — the window's pass under
333
- // the surface is one nobody sees. Inside the cache's frame, like a pass.
334
- if (this._overlaid.size !== 0) this._paintOverlays(damage);
354
+ // the surface is one nobody sees — and one from a node that cannot reach
355
+ // them is not. Inside the cache's frame, like a pass.
356
+ if (this._overlaid.size !== 0) this._paintOverlays(paneDamage);
335
357
  // after every region: an entry drawn in one damage rect must not be
336
358
  // evicted before the next rect of the same frame asks for it
337
359
  this._paintCache?.endFrame();
@@ -205,6 +205,10 @@ export class WindowNode extends Scrollable(Node) {
205
205
  // and paints with its damage (nodes/window/flush.js, src/gloverlay.js).
206
206
  // Empty is one `size` read a frame.
207
207
  this._overlaid = new Set();
208
+ // …and the damage their panes repaint, a list beside the window's:
209
+ // each claim goes to one or both by what it can reach (`_paneReach`).
210
+ // Null with nothing owed.
211
+ this._paneDamage = null;
208
212
  this.events = new EventManager(this);
209
213
  // ids of the child windows in the order the *server* stacks them,
210
214
  // bottom to top — see _restackWindowChildren
package/src/styles.js CHANGED
@@ -2008,6 +2008,28 @@ function samePosition(a, b) {
2008
2008
  return true;
2009
2009
  }
2010
2010
 
2011
+ /**
2012
+ * Whether the only properties that differ between two resolved styles are
2013
+ * the insets — `left`, `top` and the rest: a box that moved, and drew
2014
+ * nothing differently (issue #644). Every other key counts, whether layout,
2015
+ * paint or text reads it, so a style that also changed anything else
2016
+ * answers false.
2017
+ */
2018
+ export function onlyInsetsChanged(props, oldProps = {}) {
2019
+ let moved = false;
2020
+ for (const key of Object.keys(props)) {
2021
+ if (props[key] === oldProps[key]) continue;
2022
+ if (!INSETS.has(key)) return false;
2023
+ moved = true;
2024
+ }
2025
+ for (const key of Object.keys(oldProps)) {
2026
+ if (key in props) continue;
2027
+ if (!INSETS.has(key)) return false;
2028
+ moved = true;
2029
+ }
2030
+ return moved;
2031
+ }
2032
+
2011
2033
  /** @returns true if any paint-only prop changed */
2012
2034
  export function paintPropsChanged(props, oldProps = {}) {
2013
2035
  for (const key of PAINT_PROPS) {