diff --git a/ebox-layer.el b/ebox-layer.el index ecfff2b..f7facc0 100644 --- a/ebox-layer.el +++ b/ebox-layer.el @@ -190,7 +190,7 @@ the fact once for its spatial root.") (car matches))) (defun ebox-layer--absolute-origin (node host bounds width height layer-width layer-height) - "Resolve NODE's origin in HOST's BASE, or nil for an invisible anchor. + "Resolve NODE's origin in HOST, or nil for an invisible anchor. BOUNDS is the already resolved anchor rectangle, or nil. WIDTH and HEIGHT bound the host; LAYER-WIDTH and LAYER-HEIGHT bound the panel." (let ((x (ebox-layer--offset node :left host width)) diff --git a/ebox-layout.el b/ebox-layout.el index 7ca7bab..2c9c489 100644 --- a/ebox-layout.el +++ b/ebox-layout.el @@ -1570,8 +1570,10 @@ the separate unwrapped measurement keeps that resolution non-recursive." (cond ((and (eq (ebox-get props :width) 'max-content) (not (memq (ebox--box-layout-kind props) '(normal column)))) + ;; This is an unbounded *visible* formatting context, not necessarily + ;; an intrinsic-size probe. Preserve the caller's measurement mode; + ;; suppressing paint here would discard positioned descendants. (let ((ebox-viewport-width nil) - (ebox--intrinsic-layout-measurement t) (ebox--inline-auto-width-intrinsic-p nil)) (funcall function))) (content-viewport diff --git a/ebox-native-reflow.el b/ebox-native-reflow.el index 8963748..6e05463 100644 --- a/ebox-native-reflow.el +++ b/ebox-native-reflow.el @@ -18,6 +18,8 @@ (require 'ebox-runtime-index) (require 'ebox-size) +(defvar ebox-incremental--render-portals-p) + (declare-function ebox-string-height "ebox" (string)) (declare-function ebox-surface--retained-property-value-equal-p "ebox-surface" (left right)) diff --git a/ebox-render-context.el b/ebox-render-context.el index 396fb66..7de8270 100644 --- a/ebox-render-context.el +++ b/ebox-render-context.el @@ -9,6 +9,10 @@ (require 'cl-lib) +(defvar ebox-layer--active-p) +(defvar ebox-incremental--render-portals-p) +(defvar ebox--render-source-index) + (declare-function ebox-interaction--refresh-hover! "ebox-interaction" (string start end &optional face)) @@ -266,6 +270,16 @@ including strings returned from a render-cache hit." (ebox--css-size-context-active-p t) (ebox--render-runtime-revision (plist-get ,render-state :runtime-revision)) + (ebox--render-source-index + (plist-get ,render-state :source-index)) + (ebox-layer--active-p + (if (plist-member ,render-state :layered-p) + (plist-get ,render-state :layered-p) + 'unknown)) + (ebox-incremental--render-portals-p + (if (plist-member ,render-state :root-portals-p) + (plist-get ,render-state :root-portals-p) + 'unknown)) (ebox--surface-materialization-active t) (ebox--render-cache-table (plist-get ,render-state :render-cache)) diff --git a/ebox-state-contract.el b/ebox-state-contract.el index 32793c5..2c32ef1 100644 --- a/ebox-state-contract.el +++ b/ebox-state-contract.el @@ -301,6 +301,9 @@ rather than relying on an implicit default.") (:symbol ebox--box-content-empty-width :reason dynamic-scalar-result :evidence numeric-inline-extent-returned-with-one-content-materialization) + (:symbol ebox-layer--portals + :reason dynamically-bound-render-collection + :evidence let-bound-per-spatial-root-and-discarded-with-the-candidate-render) (:symbol ebox--string-pixel-width-cache-ring-index :reason numeric-eviction-cursor :evidence value-is-an-integer-ring-position) diff --git a/ebox-surface.el b/ebox-surface.el index 24b110b..a434d5b 100644 --- a/ebox-surface.el +++ b/ebox-surface.el @@ -24,6 +24,7 @@ (require 'tp) (defvar ebox-region-types) +(declare-function ebox--runtime-node-ids "ebox" (node)) (defvar ebox--region-id-counter) (defvar ebox--runtime-node-id-counter) (defvar ebox--region-box-table) diff --git a/ebox.el b/ebox.el index 914e67e..0f54221 100644 --- a/ebox.el +++ b/ebox.el @@ -3854,6 +3854,8 @@ Defaults to the current buffer." :text-align :vertical-align :overflow :wrap-mode :scroll-offset :visibility) + (cl-loop for (property _value) on ebox-style--box-runtime-defaults by #'cddr + collect property) ebox-style--item-projection-properties) "Longhand box properties accepted by `ebox-region-update'.") diff --git a/tests/ebox-layer-publication-tests.el b/tests/ebox-layer-publication-tests.el index 044048e..d87cfdf 100644 --- a/tests/ebox-layer-publication-tests.el +++ b/tests/ebox-layer-publication-tests.el @@ -42,6 +42,27 @@ (plist-get (ebox-surface-buffer-snapshot (current-buffer)) :input)))))) +(ert-deftest ebox-layer-publication-updates-every-placement-property () + "Each layer declaration reaches the mounted runtime through a region update." + (dolist (properties '((:position relative) (:left (ch 2)) (:top (lh 1)) + (:z-index -1) (:layer root) (:anchor "lower") + (:placement top-end))) + (ert-info ((format "Update %S" properties)) + (ebox-layer-publication-test--with-buffer + (ebox-render-to-buffer + (current-buffer) (ebox-layer-publication-test--input nil t)) + (let* ((handle (ebox-region-resolve (current-buffer) "upper")) + (region-id (cdr (ebox-selector--region-target handle))) + (revision (ebox-surface-buffer-revision (current-buffer)))) + (should (apply #'ebox-region-update handle properties)) + (should (> (ebox-surface-buffer-revision (current-buffer)) revision)) + (should (equal (plist-get + (ebox--buffer-region-render-owner-node + (current-buffer) region-id) + (car properties)) + (cadr properties))) + (ebox-layer-publication-test--assert-fresh-render)))))) + (ert-deftest ebox-layer-publication-hidden-updates-reveal-current-input () "Hidden content and paint updates survive without painting over upper text." (ebox-layer-publication-test--with-buffer diff --git a/tests/ebox-layer-tests.el b/tests/ebox-layer-tests.el index 7a64de2..51301f9 100644 --- a/tests/ebox-layer-tests.el +++ b/tests/ebox-layer-tests.el @@ -77,5 +77,88 @@ :width (ch 6) "HIDDEN"))))) (should (equal (substring-no-properties output) "LATEST")))) +(ert-deftest ebox-layer-max-content-row-paints-positioned-descendants () + (let ((output (ebox-layer-test--render + '(row :width max-content + (box :width (ch 4) :height (lh 1) + (box "ABCD") + (box :position absolute :left (ch 1) + :width (ch 2) "XX")))))) + (should (equal (substring-no-properties output) "AXXD")))) + +(ert-deftest ebox-layer-anchor-dependency-resolves-before-paint-order () + (let ((output (ebox-layer-test--render + '(box :width (ch 10) :height (lh 4) + (box "0123456789\nabcdefghij\nABCDEFGHIJ\n0123456789") + (box :position absolute :anchor parent-panel :width (ch 4) + :z-index 2 "MENU") + (box :id parent-panel :position absolute :left (ch 2) + :top (lh 1) :width (ch 4) :z-index 1 "BASE"))))) + (should (equal (substring-no-properties output) + "0123456789\nabBASEghij\nABMENUGHIJ\n0123456789")))) + +(ert-deftest ebox-layer-anchor-cycles-are-rejected () + (should-error + (ebox-layer-test--render + '(box :width (ch 10) :height (lh 4) + (box :id a :position absolute :anchor b "A") + (box :id b :position absolute :anchor a "B"))))) + +(ert-deftest ebox-layer-absolute-margins-do-not-cover-underlay () + (let ((output (ebox-layer-test--render + '(box :width (ch 8) :height (lh 3) + (box "abcdefgh\nABCDEFGH\n01234567") + (box :position absolute :width (ch 4) :height (lh 1) + :margin ((lh 1) (ch 1)) :background-color "red" + "TOP!"))))) + (should (equal (substring-no-properties output) + "abcdefgh\nATOP!FGH\n01234567")))) + +(ert-deftest ebox-layer-absolute-keeps-all-flow-layout-sizes () + (dolist (tag '(box row column flex grid)) + (let* ((form `(,tag :width (ch 8) + (box :width (ch 8) :height (lh 1) "abcdefgh") + (box :position absolute :left (ch 2) + :width (ch 2) :height (lh 9) "XX"))) + (output (ebox-layer-test--render form))) + (ert-info ((format "Layout %s" tag)) + (should (= (length (ebox-string-lines output)) 1)) + (should (equal (substring-no-properties output) "abXXefgh")))))) + +(ert-deftest ebox-layer-root-projection-inside-an-absolute-panel () + (let ((output (ebox-layer-test--render + '(box :width (ch 10) :height (lh 4) + (box "0123456789\nabcdefghij\nABCDEFGHIJ\n0123456789") + (box :position absolute :left (ch 2) :top (lh 1) + :width (ch 4) :height (lh 1) + (box :id trigger "BASE") + (box :position absolute :layer root :anchor trigger + :width (ch 4) :height (lh 1) "MENU")))))) + (should (equal (substring-no-properties output) + "0123456789\nabBASEghij\nABMENUGHIJ\n0123456789")))) + +(ert-deftest ebox-layer-native-to-layer-transition-keeps-composition () + (skip-unless (and (fboundp 'ebox-native-reflow-layout-ready-p) + (ebox-native-reflow-layout-ready-p))) + (let ((ebox-viewport-width 80) (ebox-viewport-height 10) + (ebox-runtime-idle-prewarm nil) + (ebox-runtime-idle-reflow-cache-prewarm nil)) + (with-temp-buffer + (ebox-render-to-buffer + (current-buffer) + (ebox-build '(column :width (px 8) :height (lh 2) + (box :id "lower" :width (px 8) :height (lh 1) "LOWER001") + (box :id "upper" :width (px 8) :height (lh 1) "UPPER001")))) + (should (eq (plist-get (ebox--buffer-render-state (current-buffer)) + :projection-kind) 'native-frame)) + (should (ebox-region-update "upper" :position 'absolute)) + (should (plist-get (ebox--buffer-render-state (current-buffer)) :layered-p)) + (should-not (string-match-p "LOWER001" (buffer-string))) + (should (string-match-p "UPPER001" (buffer-string))) + (ebox-region-update "lower" :content "LOWER002") + (ebox-region-update "upper" :visibility 'hidden) + (should (string-match-p "LOWER002" (buffer-string))) + (should-not (string-match-p "UPPER001" (buffer-string)))))) + (provide 'ebox-layer-tests) ;;; ebox-layer-tests.el ends here diff --git a/tests/ebox-m0a-inventory-fixture.el b/tests/ebox-m0a-inventory-fixture.el index 4c9afcd..326eb5b 100644 --- a/tests/ebox-m0a-inventory-fixture.el +++ b/tests/ebox-m0a-inventory-fixture.el @@ -25,6 +25,9 @@ (:storage (:state-key :content-index) :why address-scalar-not-a-store :owner ebox-surface :evidence plist-key-holds-a-numeric-content-address) + (:storage (:state-key :z-index) + :why paint-order-scalar-not-a-store :owner ebox-layer + :evidence style-schema-requires-an-integer-used-only-for-layer-ordering) (:storage (:state-key :max-jobs) :why native-limit-not-a-store :owner ebox-native-reflow :evidence plist-key-holds-a-numeric-capacity) diff --git a/tests/ebox-package-tests.el b/tests/ebox-package-tests.el index 25f0836..7ae9182 100644 --- a/tests/ebox-package-tests.el +++ b/tests/ebox-package-tests.el @@ -162,7 +162,8 @@ "ebox-node-factory.el" "ebox-child-range.el" "ebox-tree.el" "ebox-measure.el" - "ebox-fragment.el" "ebox-render-context.el" "ebox-layout.el" + "ebox-fragment.el" "ebox-render-context.el" "ebox-composite.el" + "ebox-layer.el" "ebox-layout.el" "ebox-flex.el" "ebox-grid.el" "ebox-canonical.el" "ebox-buffer-backend.el" "ebox-patch-plan.el" "ebox-incremental.el"