ntk 6.1.0 → 6.2.0

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.
@@ -105,8 +105,10 @@ export default class HtmlView {
105
105
  if (this.window) {
106
106
  this._ctx = this.window.getContext('2d');
107
107
  this.window.on('expose', () => this.render());
108
- this.window.on('resize', () => {
109
- this._layoutWidth = -1;
108
+ // a move is a ConfigureNotify too; re-laying out a document per step
109
+ // of a window drag is exactly what ev.resized is for
110
+ this.window.on('resize', (ev) => {
111
+ if (ev.resized) this._layoutWidth = -1;
110
112
  });
111
113
  this.window.on('mousedown', (ev) => {
112
114
  if (ev.keycode === 4 || ev.keycode === 5) {
@@ -74,8 +74,10 @@ export default class MarkdownView {
74
74
  if (this.window) {
75
75
  this._ctx = this.window.getContext('2d');
76
76
  this.window.on('expose', () => this.render());
77
- this.window.on('resize', () => {
78
- this._layoutWidth = -1; // invalidate
77
+ // a move is a ConfigureNotify too; re-laying out a document per step
78
+ // of a window drag is exactly what ev.resized is for
79
+ this.window.on('resize', (ev) => {
80
+ if (ev.resized) this._layoutWidth = -1; // invalidate
79
81
  });
80
82
  this.window.on('mousedown', (ev) => {
81
83
  if (ev.keycode !== 1 || !this.onLink) return; // left button only
package/lib/window.js CHANGED
@@ -386,6 +386,10 @@ export default class Window extends Drawable {
386
386
  this.height = args.height ?? 800;
387
387
  this.x = args.x ?? 0;
388
388
  this.y = args.y ?? 0;
389
+ // the baseline 'resize' compares against (see _tagResize): what the
390
+ // window was asked to be, so the window manager's first ConfigureNotify
391
+ // reports only what it actually overrode
392
+ this._deliveredGeom = { x: this.x, y: this.y, width: this.width, height: this.height };
389
393
  const values = {
390
394
  // NorthWest bit gravity: keep the old content anchored during
391
395
  // resize instead of discarding it (less flicker between the resize
@@ -419,6 +423,8 @@ export default class Window extends Drawable {
419
423
  this.eventMask = 0;
420
424
  this.visual = 0;
421
425
  this.depth = 0;
426
+ // an adopted window's geometry is not known until GetGeometry replies
427
+ this._deliveredGeom = null;
422
428
  // populate width and height
423
429
  X.GetGeometry(this.id, (err, res) => {
424
430
  if (!err) {
@@ -427,6 +433,10 @@ export default class Window extends Drawable {
427
433
  this.x = res.xPos;
428
434
  this.y = res.yPos;
429
435
  this.depth = res.depth;
436
+ // nothing was known about this window until now, so until this
437
+ // reply lands a 'resize' reports both moved and resized (see
438
+ // _tagResize) rather than guessing
439
+ this._deliveredGeom ??= { x: this.x, y: this.y, width: this.width, height: this.height };
430
440
  }
431
441
  this._readyPromiseResolve();
432
442
  });
@@ -523,6 +533,7 @@ export default class Window extends Drawable {
523
533
  // deliver buffered state events before a discrete one so handlers see
524
534
  // them in the order they happened (a drag sees the move, then the up)
525
535
  this._flushCoalesced();
536
+ if (eventName === 'resize') this._tagResize(ntkev);
526
537
  this.emit(eventName, ntkev);
527
538
  // a WM_DELETE_WINDOW ClientMessage is the window manager *asking*, and
528
539
  // 'close' is that question in a form an application can answer
@@ -628,8 +639,6 @@ export default class Window extends Drawable {
628
639
  this._dirty = false;
629
640
  this._backingValid = false;
630
641
  this._presentScheduled = false;
631
- this._backedW = this.width;
632
- this._backedH = this.height;
633
642
 
634
643
  this._presentGc = this.X.AllocID();
635
644
  this.X.CreateGC(this._presentGc, this.id, { graphicsExposures: 0 });
@@ -642,10 +651,11 @@ export default class Window extends Drawable {
642
651
  this.X.ChangeWindowAttributes(this.id, { eventMask: this.eventMask }, () => {});
643
652
  }
644
653
 
654
+ // a pure move is a ConfigureNotify too, and reallocating a backing
655
+ // pixmap per step of a window drag is the bug this flag exists to spare
656
+ // everyone (see _tagResize)
645
657
  this.on('resize', (ev) => {
646
- if (ev.width === this._backedW && ev.height === this._backedH) return;
647
- this._backedW = ev.width;
648
- this._backedH = ev.height;
658
+ if (!ev.resized) return;
649
659
  this._allocBacking(ev.width, ev.height);
650
660
  this._backingValid = false;
651
661
  this._requestRedraw();
@@ -875,6 +885,46 @@ export default class Window extends Drawable {
875
885
  this._scheduleFrame();
876
886
  }
877
887
 
888
+ /**
889
+ * Say what a 'resize' actually changed.
890
+ *
891
+ * 'resize' is ConfigureNotify, which fires for pure moves and for
892
+ * reparents as much as for size changes — under an opaque-move window
893
+ * manager a window drag is one per pointer step. Every consumer used to
894
+ * rediscover that by keeping its own copy of the last size (this library
895
+ * included, in the backing-store listener), and one that didn't paid a
896
+ * full relayout per step of a drag.
897
+ *
898
+ * So the delivered event says which it was:
899
+ *
900
+ * - `ev.resized` — the size differs from the last delivered event's
901
+ * - `ev.moved` — the position does
902
+ * - `ev.previous` — that event's `{x, y, width, height}`, for a delta;
903
+ * null on an adopted window whose geometry is not known yet, where
904
+ * both flags read true because nothing can be ruled out
905
+ *
906
+ * Measured against the last *delivered* event, not the last raw one, so
907
+ * that coalescing cannot swallow a change: a frame that merges two moves
908
+ * and a resize reports both, where per-hop flags would report only the
909
+ * final hop. The merged raw events in `ev.coalesced` are not tagged for
910
+ * that reason — the flags are a property of the delivery.
911
+ *
912
+ * Position is compared as reported. A reparenting window manager sends
913
+ * real ConfigureNotify in frame coordinates and synthetic ones in root
914
+ * coordinates (ICCCM 4.2.3), so a frame's own offset can read as a move
915
+ * on the first event after the switch. That is the safe direction —
916
+ * spurious work, never missed work — and the alternative is a
917
+ * TranslateCoordinates round trip per event, which is the cost this flag
918
+ * exists to avoid.
919
+ */
920
+ _tagResize(ev) {
921
+ const prev = this._deliveredGeom;
922
+ ev.previous = prev;
923
+ ev.moved = !prev || ev.x !== prev.x || ev.y !== prev.y;
924
+ ev.resized = !prev || ev.width !== prev.width || ev.height !== prev.height;
925
+ this._deliveredGeom = { x: ev.x, y: ev.y, width: ev.width, height: ev.height };
926
+ }
927
+
878
928
  _flushCoalesced() {
879
929
  const pending = this._frame.pending;
880
930
  if (!pending.size) return;
@@ -884,6 +934,7 @@ export default class Window extends Drawable {
884
934
  const ev = pending.get(name);
885
935
  if (ev) {
886
936
  pending.delete(name);
937
+ if (name === 'resize') this._tagResize(ev);
887
938
  this.emit(name, ev);
888
939
  }
889
940
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ntk",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "description": "Desktop UI toolkit for X11 with canvas-like 2d and OpenGL rendering",
5
5
  "author": "Andrey Sidorov <sidorares@yandex.ru>",
6
6
  "license": "MIT",