Reduce flicker: buffered draw, update batching, opaque paint attribute
ober
447d4b55ae1c9820ecc15a9458addf9ef4934bc7
--- a/lib/jerboa-emacs/qt/buffer.sls +++ b/lib/jerboa-emacs/qt/buffer.sls @@ -39,8 +39,9 @@ (hash-remove! *doc-buffer-map* doc) (buffer-list-remove! buf))) (def (qt-buffer-attach! editor buf) - "Switch editor to display this buffer's document.\n Re-applies the document's read-only state after swap because QScintilla\n may have a widget-level readOnly flag that persists across document switches.\n Runs post-buffer-attach-hook to handle image/text display toggling." + "Switch editor to display this buffer's document.\n Re-applies the document's read-only state after swap because QScintilla\n may have a widget-level readOnly flag that persists across document switches.\n Runs post-buffer-attach-hook to handle image/text display toggling.\n All visual changes are batched via setUpdatesEnabled to prevent flicker." (verbose-log! "qt-buffer-attach! buf=" (buffer-name buf)) + (qt-widget-set-updates-enabled! editor #f) (let ([doc (buffer-doc-pointer buf)]) (verbose-log! "qt-buffer-attach! SCI_SETDOCPOINTER begin") (sci-send editor SCI_SETDOCPOINTER 0 doc) @@ -53,4 +54,5 @@ (run-hooks! 'post-buffer-attach-hook editor buf) (verbose-log! "qt-buffer-attach! done buf=" - (buffer-name buf))))) + (buffer-name buf))) + (qt-widget-set-updates-enabled! editor #t))) --- a/lib/jerboa-emacs/qt/sci-shim.sls +++ b/lib/jerboa-emacs/qt/sci-shim.sls @@ -117,23 +117,23 @@ qt-widget-set-focus! qt-widget-set-font-size! qt-widget-set-maximum-height! qt-widget-set-minimum-height! qt-widget-set-minimum-size! qt-widget-set-size-policy! - qt-widget-set-style-sheet! qt-widget-set-updates-enabled! - qt-widget-show! qt-widget-show-fullscreen! - qt-widget-show-maximized! qt-widget-show-minimized! - qt-widget-show-normal! qt-widget-width - qt-widget-window-state QT_MOD_SHIFT QT_MOD_ALT QT_MOD_META - QT_MOD_CTRL QT_KEY_ESCAPE QT_KEY_BACKSPACE QT_KEY_RETURN - QT_KEY_ENTER QT_KEY_DELETE QT_KEY_TAB QT_KEY_INSERT - QT_KEY_HOME QT_KEY_END QT_KEY_LEFT QT_KEY_RIGHT QT_KEY_UP - QT_KEY_DOWN QT_KEY_PAGE_UP QT_KEY_PAGE_DOWN QT_KEY_SPACE - QT_KEY_A QT_KEY_G QT_KEY_N QT_KEY_P QT_KEY_R QT_KEY_S - QT_KEY_W QT_KEY_Z QT_KEY_F1 QT_KEY_F2 QT_KEY_F3 QT_KEY_F4 - QT_KEY_F5 QT_KEY_F6 QT_KEY_F7 QT_KEY_F8 QT_KEY_F9 QT_KEY_F10 - QT_KEY_F11 QT_KEY_F12 QT_CURSOR_UP QT_CURSOR_DOWN - QT_CURSOR_START QT_CURSOR_END QT_CURSOR_START_OF_BLOCK - QT_CURSOR_END_OF_BLOCK QT_CURSOR_NEXT_CHAR - QT_CURSOR_NEXT_WORD QT_CURSOR_PREVIOUS_CHAR - QT_CURSOR_PREVIOUS_WORD) + qt-widget-set-style-sheet! qt-widget-set-attribute! + qt-widget-set-updates-enabled! qt-widget-show! + qt-widget-show-fullscreen! qt-widget-show-maximized! + qt-widget-show-minimized! qt-widget-show-normal! + qt-widget-width qt-widget-window-state QT_MOD_SHIFT + QT_MOD_ALT QT_MOD_META QT_MOD_CTRL QT_KEY_ESCAPE + QT_KEY_BACKSPACE QT_KEY_RETURN QT_KEY_ENTER QT_KEY_DELETE + QT_KEY_TAB QT_KEY_INSERT QT_KEY_HOME QT_KEY_END QT_KEY_LEFT + QT_KEY_RIGHT QT_KEY_UP QT_KEY_DOWN QT_KEY_PAGE_UP + QT_KEY_PAGE_DOWN QT_KEY_SPACE QT_KEY_A QT_KEY_G QT_KEY_N + QT_KEY_P QT_KEY_R QT_KEY_S QT_KEY_W QT_KEY_Z QT_KEY_F1 + QT_KEY_F2 QT_KEY_F3 QT_KEY_F4 QT_KEY_F5 QT_KEY_F6 QT_KEY_F7 + QT_KEY_F8 QT_KEY_F9 QT_KEY_F10 QT_KEY_F11 QT_KEY_F12 + QT_CURSOR_UP QT_CURSOR_DOWN QT_CURSOR_START QT_CURSOR_END + QT_CURSOR_START_OF_BLOCK QT_CURSOR_END_OF_BLOCK + QT_CURSOR_NEXT_CHAR QT_CURSOR_NEXT_WORD + QT_CURSOR_PREVIOUS_CHAR QT_CURSOR_PREVIOUS_WORD) (import (except (chezscheme) make-hash-table hash-table? iota \x31;+ \x31;- getenv path-extension path-absolute? thread? make-mutex --- a/lib/jerboa-emacs/qt/window.sls +++ b/lib/jerboa-emacs/qt/window.sls @@ -232,43 +232,43 @@ (parse-hex-color (face-bg region-face))]) (sci-send ed 2068 1 (rgb->sci r g b)))))) (def (qt-scintilla-setup-editor! ed) - "Configure QScintilla editor: theme, margins, caret, save-point signals." - (qt-apply-editor-theme! ed) - (sci-send ed SCI_SETMARGINTYPEN 0 SC_MARGIN_NUMBER) - (sci-send - ed - SCI_SETMARGINWIDTHN - 0 - (max 30 (* *default-font-size* 3))) - (sci-send ed SCI_SETMARGINWIDTHN 1 0) - (sci-send ed SCI_SETMARGINWIDTHN 2 0) - (sci-send ed SCI_SETMARGINWIDTHN 3 0) - (sci-send ed SCI_SETMARGINWIDTHN 4 0) - (let ([bg (let ([f (face-get 'default)]) - (if (and f (face-bg f)) - (let-values ([(r g b) - (parse-hex-color (face-bg f))]) - (rgb->sci r g b)) - (rgb->sci 30 30 46)))]) - (sci-send ed 2290 1 bg) - (sci-send ed 2291 1 bg)) - (sci-send ed SCI_SETCARETLINEVISIBLE 1) - (sci-send ed SCI_SETTABWIDTH 4) - (sci-send ed SCI_SETINDENT 4) (sci-send ed 2563 1) - (sci-send ed 2565 1) (sci-send ed 2608 1) - (sci-send ed 2567 1) - (qt-on-scintilla-save-point-reached! - ed - (lambda () - (let* ([doc (sci-send ed SCI_GETDOCPOINTER)] - [buf (hash-get *doc-buffer-map* doc)]) - (when buf (buffer-modified-set! buf #f))))) - (qt-on-scintilla-save-point-left! - ed - (lambda () - (let* ([doc (sci-send ed SCI_GETDOCPOINTER)] - [buf (hash-get *doc-buffer-map* doc)]) - (when buf (buffer-modified-set! buf #t)))))) + "Configure QScintilla editor: theme, margins, caret, save-point signals." + (qt-apply-editor-theme! ed) + (sci-send ed SCI_SETMARGINTYPEN 0 SC_MARGIN_NUMBER) + (sci-send + ed + SCI_SETMARGINWIDTHN + 0 + (max 30 (* *default-font-size* 3))) + (sci-send ed SCI_SETMARGINWIDTHN 1 0) + (sci-send ed SCI_SETMARGINWIDTHN 2 0) + (sci-send ed SCI_SETMARGINWIDTHN 3 0) + (sci-send ed SCI_SETMARGINWIDTHN 4 0) + (let ([bg (let ([f (face-get 'default)]) + (if (and f (face-bg f)) + (let-values ([(r g b) (parse-hex-color (face-bg f))]) + (rgb->sci r g b)) + (rgb->sci 30 30 46)))]) + (sci-send ed 2290 1 bg) + (sci-send ed 2291 1 bg)) + (sci-send ed SCI_SETCARETLINEVISIBLE 1) + (sci-send ed SCI_SETTABWIDTH 4) + (sci-send ed SCI_SETINDENT 4) (sci-send ed 2563 1) + (sci-send ed 2565 1) (sci-send ed 2608 1) + (sci-send ed 2567 1) (sci-send ed 2035 1) + (sci-send ed 2284 1) (qt-widget-set-attribute! ed 4 #t) + (qt-on-scintilla-save-point-reached! + ed + (lambda () + (let* ([doc (sci-send ed SCI_GETDOCPOINTER)] + [buf (hash-get *doc-buffer-map* doc)]) + (when buf (buffer-modified-set! buf #f))))) + (qt-on-scintilla-save-point-left! + ed + (lambda () + (let* ([doc (sci-send ed SCI_GETDOCPOINTER)] + [buf (hash-get *doc-buffer-map* doc)]) + (when buf (buffer-modified-set! buf #t)))))) (def (qt-make-new-window! container-parent buf) "Create a new qt-edit-window with a fresh editor in a new container.\n CONTAINER-PARENT is the QSplitter that will own the container." (let* ([container (qt-stacked-widget-create @@ -334,6 +334,7 @@ [main-win (qt-frame-main-win fr)] [saved-w (and main-win (qt-widget-width main-win))] [saved-h (and main-win (qt-widget-height main-win))]) + (when main-win (qt-widget-set-updates-enabled! main-win #f)) (let ([result (cond [(and parent (= (split-node-orientation parent) @@ -484,6 +485,7 @@ (qt-widget-resize! main-win saved-w saved-h)) (when result (qt-widget-set-focus! result)) (qt-frame-update-visual-indicators! fr) + (when main-win (qt-widget-set-updates-enabled! main-win #t)) result))) (def (qt-frame-split! fr) "Split vertically: add a new window below. Returns the new editor." --- a/src/jerboa-emacs/qt/buffer.ss +++ b/src/jerboa-emacs/qt/buffer.ss @@ -52,8 +52,12 @@ "Switch editor to display this buffer's document. Re-applies the document's read-only state after swap because QScintilla may have a widget-level readOnly flag that persists across document switches. - Runs post-buffer-attach-hook to handle image/text display toggling." + Runs post-buffer-attach-hook to handle image/text display toggling. + All visual changes are batched via setUpdatesEnabled to prevent flicker." (verbose-log! "qt-buffer-attach! buf=" (buffer-name buf)) + ;; Suppress intermediate repaints: doc swap + highlight + font loop + ;; are 3 visual changes that would flash in sequence without batching. + (qt-widget-set-updates-enabled! editor #f) (let ((doc (buffer-doc-pointer buf))) (verbose-log! "qt-buffer-attach! SCI_SETDOCPOINTER begin") (sci-send editor SCI_SETDOCPOINTER 0 doc) @@ -67,4 +71,5 @@ (verbose-log! "qt-buffer-attach! post-buffer-attach-hook begin") ;; Toggle image/editor display via hook (set up in qt/app.ss) (run-hooks! 'post-buffer-attach-hook editor buf) - (verbose-log! "qt-buffer-attach! done buf=" (buffer-name buf)))) + (verbose-log! "qt-buffer-attach! done buf=" (buffer-name buf))) + (qt-widget-set-updates-enabled! editor #t)) --- a/src/jerboa-emacs/qt/sci-shim.ss +++ b/src/jerboa-emacs/qt/sci-shim.ss @@ -118,7 +118,7 @@ qt-widget-height qt-widget-hide! qt-widget-resize! qt-widget-set-focus! qt-widget-set-font-size! qt-widget-set-maximum-height! qt-widget-set-minimum-height! qt-widget-set-minimum-size! qt-widget-set-size-policy! qt-widget-set-style-sheet! - qt-widget-set-updates-enabled! + qt-widget-set-attribute! qt-widget-set-updates-enabled! qt-widget-show! qt-widget-show-fullscreen! qt-widget-show-maximized! qt-widget-show-minimized! qt-widget-show-normal! qt-widget-width qt-widget-window-state ;; Qt constants re-exported for modules that only import sci-shim --- a/src/jerboa-emacs/qt/window.ss +++ b/src/jerboa-emacs/qt/window.ss @@ -282,6 +282,16 @@ (sci-send ed 2565 1) ; SCI_SETADDITIONALSELECTIONTYPING (sci-send ed 2608 1) ; SCI_SETADDITIONALCARETSVISIBLE (sci-send ed 2567 1) ; SCI_SETADDITIONALCARETSBLINK + ;; Scintilla internal double-buffering — reduces flicker during rapid + ;; style changes and text updates (on top of Qt's own double-buffering). + (sci-send ed 2035 1) ; SCI_SETBUFFEREDDRAW = on + ;; Two-phase drawing: background painted first, then text on top. + ;; Smoother than single-phase for syntax-highlighted text. + (sci-send ed 2284 1) ; SCI_SETTWOPHASEDRAW = on + ;; WA_OpaquePaintEvent (4): tell Qt not to clear the widget background + ;; before painting. Prevents "white flash" when widgets are created, + ;; resized, or switch documents. + (qt-widget-set-attribute! ed 4 #t) ;; Save-point signals for modified state tracking (qt-on-scintilla-save-point-reached! ed (lambda () @@ -371,6 +381,9 @@ (saved-w (and main-win (qt-widget-width main-win))) (saved-h (and main-win (qt-widget-height main-win)))) + ;; Suppress intermediate repaints during split: widget creation, + ;; reparenting, and size equalization flash without batching. + (when main-win (qt-widget-set-updates-enabled! main-win #f)) (let ((result (cond ;; ── Case A: parent has same orientation — add sibling ───────────────── @@ -470,6 +483,8 @@ (when result (qt-widget-set-focus! result)) ;; Update visual indicators (qt-frame-update-visual-indicators! fr) + ;; Re-enable updates — single + (when main-win (qt-widget-set-updates-enabled! main-win #t)) result))) (def (qt-frame-split! fr) --- a/vendor/chez-qt-ffi-static.ss +++ b/vendor/chez-qt-ffi-static.ss @@ -23,7 +23,7 @@ ffi-qt-widget-set-minimum-width ffi-qt-widget-set-minimum-height ffi-qt-widget-set-maximum-width ffi-qt-widget-set-maximum-height ffi-qt-widget-set-cursor ffi-qt-widget-unset-cursor - ffi-qt-widget-resize ffi-qt-widget-set-style-sheet + ffi-qt-widget-resize ffi-qt-widget-set-style-sheet ffi-qt-widget-set-attribute ffi-qt-widget-set-tooltip ffi-qt-widget-set-font-size ffi-qt-widget-destroy @@ -967,6 +967,8 @@ (define ffi-qt-widget-set-style-sheet (foreign-procedure "qt_widget_set_style_sheet" (void* string) void)) + (define ffi-qt-widget-set-attribute + (foreign-procedure "qt_widget_set_attribute" (void* int int) void)) (define ffi-qt-widget-set-tooltip (foreign-procedure "qt_widget_set_tooltip" (void* string) void)) --- a/vendor/chez-qt-qt.ss +++ b/vendor/chez-qt-qt.ss @@ -30,7 +30,7 @@ qt-widget-set-minimum-width! qt-widget-set-minimum-height! qt-widget-set-maximum-width! qt-widget-set-maximum-height! qt-widget-set-cursor! qt-widget-unset-cursor! - qt-widget-resize! qt-widget-set-style-sheet! + qt-widget-resize! qt-widget-set-style-sheet! qt-widget-set-attribute! qt-widget-set-tooltip! qt-widget-set-font-size! qt-widget-destroy! @@ -928,6 +928,7 @@ (define (qt-widget-unset-cursor! w) (ffi-qt-widget-unset-cursor w)) (define (qt-widget-resize! w width height) (ffi-qt-widget-resize w width height)) (define (qt-widget-set-style-sheet! w css) (ffi-qt-widget-set-style-sheet w css)) + (define (qt-widget-set-attribute! w attr on) (ffi-qt-widget-set-attribute w attr (if on 1 0))) (define (qt-widget-set-tooltip! w text) (ffi-qt-widget-set-tooltip w text)) (define (qt-widget-set-font-size! w size) (ffi-qt-widget-set-font-size w size)) --- a/vendor/qt_shim.cpp +++ b/vendor/qt_shim.cpp @@ -633,6 +633,12 @@ extern "C" void qt_widget_set_style_sheet(qt_widget_t w, const char* css) { QT_VOID(static_cast<QWidget*>(w)->setStyleSheet(QString::fromUtf8(css))); } +extern "C" void qt_widget_set_attribute(qt_widget_t w, int attribute, int on) { + QT_NULL_CHECK_VOID(w); + QT_VOID(static_cast<QWidget*>(w)->setAttribute( + static_cast<Qt::WidgetAttribute>(attribute), on != 0)); +} + extern "C" void qt_widget_set_tooltip(qt_widget_t w, const char* text) { QT_NULL_CHECK_VOID(w); QT_VOID(static_cast<QWidget*>(w)->setToolTip(QString::fromUtf8(text))); --- a/vendor/qt_shim.h +++ b/vendor/qt_shim.h @@ -46,6 +46,7 @@ void qt_widget_set_cursor(qt_widget_t w, int shape); void qt_widget_unset_cursor(qt_widget_t w); void qt_widget_resize(qt_widget_t w, int width, int height); void qt_widget_set_style_sheet(qt_widget_t w, const char* css); +void qt_widget_set_attribute(qt_widget_t w, int attribute, int on); void qt_widget_set_tooltip(qt_widget_t w, const char* text); void qt_widget_set_font_size(qt_widget_t w, int size); void qt_widget_destroy(qt_widget_t w);