refactor: delegate resize ownership to Ebox
This commit is contained in:
parent
24b69a64c5
commit
256868a7ee
@ -74,7 +74,7 @@
|
||||
## Implementation constraints
|
||||
- Framework/styling system: Emacs 29.1+ and public Ebox constructors/properties only.
|
||||
- Design-token constraints: Reuse the Flex reference's restrained color families without introducing a theme package.
|
||||
- Performance constraints: One synchronous initial render; window resizing uses one bounded idle debounce and Ebox's incremental viewport update path.
|
||||
- Performance constraints: One synchronous initial render; window resizing delegates to Ebox's single bounded viewport controller and incremental update path.
|
||||
- Compatibility constraints: No ETAF dependency and no `ebox--*` calls.
|
||||
- Test/screenshot expectations: `make check` passes and a fullscreen one-window screenshot shows complete, unclipped labeled sections.
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ ECSS 0.1.0 and TP 1.0.0 are independent packages and may be installed in either
|
||||
|
||||
Standalone gallery entry points default to a definite 720 px canvas so nested examples have stable space in batch renders and one-window demos. Split-window `.ebox` previews instead bind `(viewport)` to the preview window's display-safe width, so viewport-based separators and sections fit the right-hand pane. `etaf-playground` remains the separate higher-level Component and Runtime showcase.
|
||||
|
||||
Standalone and batch fixture renders use the same compact 720 px default viewport unless the caller dynamically binds `ebox-viewport-width`; viewport-based references therefore stay inside the example canvas. When `C-c C-c` renders an `.ebox` source buffer, the command keeps the source on the left, opens the preview on the right, and binds `(viewport)` to the preview window's display-safe width. The preview then debounces window-size changes and republishes the width through Ebox's incremental viewport update path.
|
||||
Standalone and batch fixture renders use the same compact 720 px default viewport unless the caller dynamically binds `ebox-viewport-width`; viewport-based references therefore stay inside the example canvas. When `C-c C-c` renders an `.ebox` source buffer, the command keeps the source on the left, opens the preview on the right, and binds `(viewport)` to Ebox's shared display-safe window width. Continuous window changes are coalesced by Ebox's single viewport controller; the Playground owns no second resize hook or timer.
|
||||
|
||||
```elisp
|
||||
(require 'ebox-playground)
|
||||
|
||||
@ -6,7 +6,7 @@ ECSS 0.1.0 与 TP 1.0.0 是互相独立的包,安装顺序任意;两者都
|
||||
|
||||
独立画廊入口默认使用明确的 720 px 画布,因此 batch 渲染和单窗口演示里的嵌套示例有稳定空间。分屏 `.ebox` 预览则会把 `(viewport)` 绑定为右侧预览窗口的显示安全宽度,所以依赖 viewport 的分隔条和区块会适配右侧窗格。高层 Component/Runtime 展示仍由独立的 `etaf-playground` 提供。
|
||||
|
||||
独立和 batch fixture 渲染默认也使用紧凑的 720 px 视口;如果调用方动态绑定了 `ebox-viewport-width`,则保留调用方的值。因此依赖 viewport 的参考例子也会保持在示例画布内。通过 `C-c C-c` 渲染 `.ebox` 源文件时,命令会把源文件保留在左侧、在右侧打开预览,并将 `(viewport)` 绑定为预览窗口的显示安全像素宽度;预览随后会合并连续的 window-size 变化,并通过 Ebox 已有的 viewport 增量更新路径发布宽度。
|
||||
独立和 batch fixture 渲染默认也使用紧凑的 720 px 视口;如果调用方动态绑定了 `ebox-viewport-width`,则保留调用方的值。因此依赖 viewport 的参考例子也会保持在示例画布内。通过 `C-c C-c` 渲染 `.ebox` 源文件时,命令会把源文件保留在左侧、在右侧打开预览,并将 `(viewport)` 绑定为 Ebox 统一计算的显示安全宽度;连续 window-size 变化由 Ebox 唯一的 viewport controller 合并,Playground 不再拥有第二套 resize hook 或 timer。
|
||||
|
||||
```elisp
|
||||
(require 'ebox-playground)
|
||||
|
||||
@ -20,18 +20,6 @@
|
||||
(require 'subr-x)
|
||||
(defvar read-eval)
|
||||
|
||||
(defvar-local ebox-playground--resize-timer nil
|
||||
"Pending idle timer for the current preview buffer.")
|
||||
|
||||
(defvar-local ebox-playground--preview-viewport-width nil
|
||||
"Last viewport width published for the current preview buffer.")
|
||||
|
||||
(defvar-local ebox-playground--preview-auto-resize-p nil
|
||||
"Non-nil when the current preview follows its displaying window.")
|
||||
|
||||
(defvar ebox-playground--tracked-preview-buffers (make-hash-table :test 'eq)
|
||||
"Preview buffers whose windows should drive viewport updates.")
|
||||
|
||||
(defconst ebox-playground-buffer-name "*Ebox Playground*"
|
||||
"Default buffer name used by `ebox-playground-open'.")
|
||||
|
||||
@ -77,7 +65,6 @@
|
||||
(viewport-width
|
||||
(or bound-width preview-width
|
||||
ebox-playground-default-viewport-width))
|
||||
(auto-resize-p (and (null bound-width) (numberp preview-width)))
|
||||
(ebox-viewport-width viewport-width)
|
||||
(source-buffer (current-buffer))
|
||||
(source-form (ebox-playground--source-form))
|
||||
@ -88,9 +75,6 @@
|
||||
(set-window-buffer source-window source-buffer)
|
||||
(set-window-buffer preview-window buffer)
|
||||
(select-window source-window)
|
||||
(ebox-playground--track-preview-buffer
|
||||
buffer viewport-width auto-resize-p
|
||||
(plist-get node :bgcolor))
|
||||
(message "Rendered Ebox DSL into %s" name)
|
||||
buffer))
|
||||
|
||||
@ -101,11 +85,6 @@
|
||||
"Run Ebox DSL examples from source files."
|
||||
:group 'ebox)
|
||||
|
||||
(defcustom ebox-playground-resize-idle-delay 0.05
|
||||
"Seconds to wait before publishing a settled preview window width."
|
||||
:type 'number
|
||||
:group 'ebox-playground)
|
||||
|
||||
(defconst ebox-playground-directory
|
||||
(file-name-directory (or load-file-name buffer-file-name))
|
||||
"Directory containing the Ebox Playground package.")
|
||||
@ -123,13 +102,8 @@
|
||||
:group 'ebox-playground)
|
||||
|
||||
(defun ebox-playground--window-viewport-width (&optional window)
|
||||
"Return WINDOW's display-safe viewport width in pixels, or nil when unavailable."
|
||||
(when (window-live-p window)
|
||||
(let ((width (window-body-width window t)))
|
||||
(and (numberp width)
|
||||
(> width 0)
|
||||
(max 1 (- (floor width)
|
||||
(max 1 (frame-char-width (window-frame window)))))))))
|
||||
"Return Ebox's shared display-safe viewport width for WINDOW."
|
||||
(ebox-viewport-window-width window))
|
||||
|
||||
(defun ebox-playground--effective-viewport-width (&optional window)
|
||||
"Return the caller's width, WINDOW's text width, or the compact default."
|
||||
@ -140,12 +114,6 @@
|
||||
((ebox-playground--window-viewport-width window))
|
||||
(t ebox-playground-default-viewport-width)))
|
||||
|
||||
(defun ebox-playground--cancel-resize-timer ()
|
||||
"Cancel the current preview's pending resize publication."
|
||||
(when (timerp ebox-playground--resize-timer)
|
||||
(cancel-timer ebox-playground--resize-timer))
|
||||
(setq ebox-playground--resize-timer nil))
|
||||
|
||||
(defun ebox-playground--apply-preview-display-settings (&optional canvas-background)
|
||||
"Apply canvas redisplay settings to the current preview buffer.
|
||||
CANVAS-BACKGROUND fills display space outside generated root lines."
|
||||
@ -205,88 +173,13 @@ CANVAS-BACKGROUND fills display space outside generated root lines."
|
||||
(unless published-p
|
||||
(ebox-playground--restore-preview-display-snapshot snapshot))))))
|
||||
|
||||
(defun ebox-playground--resize-preview (buffer window)
|
||||
"Publish BUFFER's current WINDOW viewport after a resize settles."
|
||||
(when (buffer-live-p buffer)
|
||||
(with-current-buffer buffer
|
||||
(setq-local ebox-playground--resize-timer nil)
|
||||
(when (and ebox-playground--preview-auto-resize-p
|
||||
(window-live-p window)
|
||||
(eq (window-buffer window) buffer))
|
||||
(let ((width (ebox-playground--window-viewport-width window)))
|
||||
(when (and width
|
||||
(/= width
|
||||
(or ebox-playground--preview-viewport-width 0)))
|
||||
(let ((report (ebox-rerender-buffer-with-context buffer width)))
|
||||
;; Keep the last published width as the retry key. A failed
|
||||
;; candidate must not make a later identical resize look like a
|
||||
;; successful no-op.
|
||||
(when (plist-get report :runtime-published)
|
||||
(setq-local ebox-playground--preview-viewport-width width)))))))))
|
||||
|
||||
(defun ebox-playground--window-size-change (frame)
|
||||
"Schedule a viewport update after a size change in FRAME.
|
||||
The default `window-size-change-functions' dispatcher passes a frame, so the
|
||||
preview window must be resolved from that frame before its viewport is read."
|
||||
(when (frame-live-p frame)
|
||||
(dolist (window (window-list frame 'no-minibuf))
|
||||
(let ((buffer (window-buffer window)))
|
||||
(when (and (buffer-live-p buffer)
|
||||
(gethash buffer ebox-playground--tracked-preview-buffers))
|
||||
(with-current-buffer buffer
|
||||
(let ((width (ebox-playground--window-viewport-width window)))
|
||||
(when (and width
|
||||
(/= width
|
||||
(or ebox-playground--preview-viewport-width 0)))
|
||||
(ebox-playground--cancel-resize-timer)
|
||||
(setq-local ebox-playground--resize-timer
|
||||
(run-with-idle-timer
|
||||
(max 0 ebox-playground-resize-idle-delay)
|
||||
nil #'ebox-playground--resize-preview
|
||||
buffer window))))))))))
|
||||
|
||||
(defun ebox-playground--untrack-preview-buffer (buffer)
|
||||
"Stop tracking BUFFER and remove the shared window hook when unused."
|
||||
(remhash buffer ebox-playground--tracked-preview-buffers)
|
||||
(when (= (hash-table-count ebox-playground--tracked-preview-buffers) 0)
|
||||
(remove-hook 'window-size-change-functions
|
||||
#'ebox-playground--window-size-change)))
|
||||
|
||||
(defun ebox-playground--cleanup-preview ()
|
||||
"Release resize state owned by the current preview buffer."
|
||||
(ebox-playground--cancel-resize-timer)
|
||||
(ebox-playground--untrack-preview-buffer (current-buffer)))
|
||||
|
||||
(defun ebox-playground--track-preview-buffer
|
||||
(buffer viewport-width &optional auto-resize-p canvas-background)
|
||||
"Track BUFFER at VIEWPORT-WIDTH and optionally follow its display window.
|
||||
AUTO-RESIZE-P enables following the display window. CANVAS-BACKGROUND fills
|
||||
display space outside generated root lines."
|
||||
(ebox-playground--untrack-preview-buffer buffer)
|
||||
(with-current-buffer buffer
|
||||
(ebox-playground--cancel-resize-timer)
|
||||
(ebox-playground--apply-preview-display-settings canvas-background)
|
||||
(setq-local ebox-playground--preview-viewport-width viewport-width)
|
||||
(setq-local ebox-playground--preview-auto-resize-p auto-resize-p)
|
||||
(add-hook 'kill-buffer-hook #'ebox-playground--cleanup-preview nil t))
|
||||
(when auto-resize-p
|
||||
(puthash buffer t ebox-playground--tracked-preview-buffers)
|
||||
(add-hook 'window-size-change-functions
|
||||
#'ebox-playground--window-size-change))
|
||||
buffer)
|
||||
|
||||
(defun ebox-playground--render-node-to-buffer
|
||||
(buffer node viewport-width &optional auto-resize-p)
|
||||
(defun ebox-playground--render-node-to-buffer (buffer node viewport-width)
|
||||
"Render NODE into BUFFER using VIEWPORT-WIDTH.
|
||||
AUTO-RESIZE-P enables following the display window."
|
||||
(let* ((ebox-viewport-width
|
||||
(or viewport-width
|
||||
(ebox-playground--effective-viewport-width)))
|
||||
(target buffer))
|
||||
(ebox-playground--track-preview-buffer
|
||||
(ebox-playground--render-to-preview-buffer target node)
|
||||
ebox-viewport-width auto-resize-p
|
||||
(and (consp node) (plist-get node :bgcolor)))))
|
||||
Visible mounted buffers follow their window through Ebox's viewport controller."
|
||||
(let ((ebox-viewport-width
|
||||
(or viewport-width
|
||||
(ebox-playground--effective-viewport-width))))
|
||||
(ebox-playground--render-to-preview-buffer buffer node)))
|
||||
|
||||
(defun ebox-playground--static-value-p (value)
|
||||
"Return non-nil when VALUE is a literal or explicitly quoted form."
|
||||
|
||||
@ -82,8 +82,6 @@ than charging initial frame settling to resize."
|
||||
(let ((report (ebox-rerender-buffer-with-context preview width)))
|
||||
(unless (plist-get report :runtime-published)
|
||||
(error "flex preview initial viewport was not published"))))
|
||||
(with-current-buffer preview
|
||||
(setq-local ebox-playground--preview-viewport-width width))
|
||||
width))
|
||||
|
||||
(defun ebox-playground-flex-resize-evaluator-run ()
|
||||
@ -96,7 +94,7 @@ daemon evaluator does not enable background prewarm, keeping the measured
|
||||
action free of concurrent cache-warming CPU work."
|
||||
(let ((ebox-render-gc-cons-threshold nil)
|
||||
(ebox-render-gc-cons-percentage nil)
|
||||
(ebox-playground-resize-idle-delay 0.05)
|
||||
(ebox-viewport-resize-delay 0.05)
|
||||
(source nil)
|
||||
(preview nil)
|
||||
(preview-window nil)
|
||||
|
||||
@ -214,14 +214,12 @@
|
||||
(lambda (&optional _frame) 8)))
|
||||
(should (= (ebox-playground--effective-viewport-width
|
||||
(selected-window))
|
||||
979)))))
|
||||
971)))))
|
||||
|
||||
(ert-deftest ebox-playground-window-viewport-leaves-display-reserve ()
|
||||
"A preview viewport should leave one character column for redisplay."
|
||||
(cl-letf (((symbol-function 'window-body-width)
|
||||
(lambda (&optional _window _pixelwise) 987))
|
||||
((symbol-function 'frame-char-width)
|
||||
(lambda (&optional _frame) 8)))
|
||||
(ert-deftest ebox-playground-window-viewport-uses-ebox-sampler ()
|
||||
"A preview and the Ebox resize controller share one viewport sample."
|
||||
(cl-letf (((symbol-function 'ebox-viewport-window-width)
|
||||
(lambda (_window) 979)))
|
||||
(should (= (ebox-playground--window-viewport-width (selected-window))
|
||||
979))))
|
||||
|
||||
@ -242,7 +240,7 @@
|
||||
(with-current-buffer preview
|
||||
(should (= (ebox-playground-test--max-line-width
|
||||
(buffer-string))
|
||||
(- window-width 8))))))
|
||||
(- window-width 16))))))
|
||||
(when (get-buffer preview)
|
||||
(kill-buffer preview)))))
|
||||
|
||||
@ -252,12 +250,12 @@
|
||||
ebox-playground-directory))
|
||||
(preview (format "*Ebox Preview: %s*" path))
|
||||
(window-width 699)
|
||||
(viewport 691))
|
||||
(viewport 683))
|
||||
(unwind-protect
|
||||
(cl-letf (((symbol-function 'window-body-width)
|
||||
(lambda (&optional _window _pixelwise) window-width))
|
||||
((symbol-function 'frame-char-width)
|
||||
(lambda (&optional _frame) (- window-width viewport))))
|
||||
(lambda (&optional _frame) 8)))
|
||||
(delete-other-windows)
|
||||
(with-temp-buffer
|
||||
(setq buffer-file-name path)
|
||||
@ -265,7 +263,6 @@
|
||||
(ebox-dsl-mode)
|
||||
(ebox-dsl-render)
|
||||
(with-current-buffer preview
|
||||
(should (= ebox-playground--preview-viewport-width viewport))
|
||||
(should (cl-every
|
||||
(lambda (width) (<= width viewport))
|
||||
(ebox-playground-test--line-widths
|
||||
@ -309,7 +306,7 @@
|
||||
"A preview resize should publish an incremental Ebox viewport update."
|
||||
(let ((preview "*Ebox Preview: /tmp/ebox-playground-viewport-resize.ebox*")
|
||||
(window-width 987)
|
||||
(ebox-playground-resize-idle-delay 0))
|
||||
(ebox-viewport-resize-delay 0))
|
||||
(unwind-protect
|
||||
(cl-letf (((symbol-function 'window-body-width)
|
||||
(lambda (&optional _window _pixelwise) window-width))
|
||||
@ -323,16 +320,23 @@
|
||||
(setq window-width 1234)
|
||||
(with-current-buffer preview
|
||||
(let ((window (get-buffer-window (current-buffer))))
|
||||
(should (memq #'ebox-playground--window-size-change
|
||||
(should (memq #'ebox--window-size-change
|
||||
window-size-change-functions))
|
||||
(ebox-playground--window-size-change (window-frame window))
|
||||
(should (timerp ebox-playground--resize-timer))
|
||||
(let ((noninteractive nil))
|
||||
(ebox--window-size-change (window-frame window)))
|
||||
;; Batch Emacs does not service idle timers from `sit-for'.
|
||||
(ebox-playground--resize-preview (current-buffer) window)))
|
||||
(let ((pending
|
||||
(gethash (current-buffer)
|
||||
ebox--viewport-resize-pending-table)))
|
||||
(should pending)
|
||||
(when (timerp (aref pending 4))
|
||||
(cancel-timer (aref pending 4)))
|
||||
(ebox--viewport-resize-apply
|
||||
(current-buffer) (aref pending 0)))))
|
||||
(with-current-buffer preview
|
||||
(should (= (ebox-playground-test--max-line-width
|
||||
(buffer-string))
|
||||
(- window-width 8)))
|
||||
(- window-width 16)))
|
||||
(let ((report (ebox-buffer-update-report (current-buffer))))
|
||||
(should (eq (plist-get report :constraint-source) 'viewport))
|
||||
(should (eq (plist-get report :strategy) 'owner-rerender))))))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user