diff --git a/TeXmacs/misc/themes/liii-night.css b/TeXmacs/misc/themes/liii-night.css index b5a3c33046..78a4649d38 100644 --- a/TeXmacs/misc/themes/liii-night.css +++ b/TeXmacs/misc/themes/liii-night.css @@ -1020,6 +1020,17 @@ QToolButton#text-toolbar-button:pressed { border: none; } +/*文本工具栏按钮选中样式*/ +QToolButton#text-toolbar-button:checked { + background: #4c4c4c; + border: none; +} + +QToolButton#text-toolbar-button:checked:hover { + background: #4c4c4c; + border: none; +} + /**************************************************************************** * 启动标签页样式 (Startup Tab) - 墨绿主题 Dark Mode * 主色: #215a6a (深墨绿) diff --git a/TeXmacs/misc/themes/liii.css b/TeXmacs/misc/themes/liii.css index d7b5fd6be8..163c09743e 100644 --- a/TeXmacs/misc/themes/liii.css +++ b/TeXmacs/misc/themes/liii.css @@ -993,6 +993,17 @@ QToolButton#text-toolbar-button:pressed { border: none; } +/*文本工具栏按钮选中样式*/ +QToolButton#text-toolbar-button:checked { + background: rgba(57, 177, 228, 0.3); + border: none; +} + +QToolButton#text-toolbar-button:checked:hover { + background: rgba(57, 177, 228, 0.3); + border: none; +} + /**************************************************************************** * 启动标签页样式 (Startup Tab) - 墨绿主题 * 主色: #215a6a (深墨绿) diff --git a/TeXmacs/progs/generic/text-toolbar.scm b/TeXmacs/progs/generic/text-toolbar.scm index 2fc3074ff2..763b1c00be 100644 --- a/TeXmacs/progs/generic/text-toolbar.scm +++ b/TeXmacs/progs/generic/text-toolbar.scm @@ -15,9 +15,17 @@ (texmacs-module (generic text-toolbar) (:use (generic format-edit) (generic format-menu) - (generic generic-edit))) + (generic generic-edit) + (generic generic-menu) + (table table-menu) + (text text-menu) + (math math-menu))) -(menu-bind text-toolbar-icons +;; 判断当前焦点位置是否存在可显示的选区工具栏上下文。 +(tm-define (text-toolbar-allowed-context?) + (not (not (text-toolbar-context (focus-tree))))) + +(menu-bind text-toolbar-text-icons ((balloon (icon "tm_bold.xpm") "Write bold text") (toggle-bold)) ((balloon (icon "tm_italic.xpm") "Write italic text") @@ -34,3 +42,284 @@ (make 'padded-center)) ((balloon (icon "tm_cell_right.xpm") "right aligned") (make 'padded-right-aligned))) + +(menu-bind text-toolbar-math-icons + (=> (balloon (icon "tm_fraction.xpm") "Insert a fraction") + ("Standard fraction" (make-fraction)) + ("Small inline fraction" (make 'tfrac)) + ("Large displayed fraction" (make 'dfrac)) + ("Slashed fraction" (make 'frac*)) + ("Continued fraction" (make 'cfrac))) + (=> (balloon (icon "tm_root.xpm") "Insert a root") + ("Square root" (make-sqrt)) + ("Multiple root" (make-var-sqrt))) + (=> (balloon (icon "tm_subsup.xpm") "Insert a script") + ("Subscript" (make-script #f #t)) + ("Superscript" (make-script #t #t)) + ("Left subscript" (make-script #f #f)) + ("Left superscript" (make-script #t #f)) + ("Subscript below" (make-below)) + ("Superscript above" (make-above))) + (=> (balloon (icon "tm_wide.xpm") "Insert an accent") + (tile 6 + ((balloon (icon "tm_tilda.xpm") "keyboard equivalent:") + (make-wide "~")) + ((balloon (icon "tm_bar.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_vect.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_hat.xpm") "keyboard equivalent:") + (make-wide "^")) + ((balloon (icon "tm_check.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_invbreve.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_breve.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_dot.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_ddot.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_acute.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_grave.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_overbrace.xpm") "keyboard equivalent:") + (make-wide "")) + ((balloon (icon "tm_underbrace.xpm") "keyboard equivalent:") + (make-wide-under "")) + ((balloon (icon "tm_underbar.xpm") "keyboard equivalent:") + (make-wide-under "")))) + ((balloon (icon "tm_marked.xpm") "Marked text") + (mark-text)) + (=> (balloon (icon "tm_color.xpm") "Select a foreground color") + (link color-menu))) + +(menu-bind text-toolbar-table-icons + (=> (balloon (icon "tm_cell_border.xpm") "Change border of cell") + (mini #f + (group "Border") + (link cell-alt-border-menu) + --- + (group "Pen width") + (link cell-compact-pen-width-menu) + --- + (group "Padding") + (link cell-padding-menu))) + (=> (balloon (icon "tm_cell_center.xpm") "Modify cell alignment") + (mini #f + (group "Horizontal alignment") + (link cell-halign-menu) + --- + (group "Vertical alignment") + (link cell-valign-menu))) + (=> (balloon (icon "tm_cell_background.xpm") "Set background color of cell") + (mini #f + ("None" (cell-set-background "")) + ("Foreground" (cell-set-background "foreground")) + --- + (pick-background "" (cell-set-background answer)) + --- + ("Other" (interactive cell-set-background))))) + +;; 提取当前选区对应的语义块根节点,例如定理、命题等环境。 +(tm-define (semantic-block-selection-tree) + (and (selection-active-any?) + (with t (path->tree (selection-path)) + (and (== (selection-tree) t) + (let loop ((t t)) + (cond ((or (tree-in? t (numbered-unnumbered-append (enunciation-tag-list))) + (tree-in? t (render-enunciation-tag-list))) + t) + ((tm-func? t 'document 1) + (loop (tree-ref t 0))) + (else #f))))))) + +(menu-bind text-toolbar-semantic-icons + (with t (semantic-block-selection-tree) + (when (and t (numbered-context? t)) + ((check (balloon (icon "tm_numbered.xpm") "Numbered") "v" + (numbered-numbered? t)) + (numbered-toggle t))) + ((check (balloon (icon "tm_cell_border.xpm") "Framed theorems") "v" + (has-style-package? "framed-theorems")) + (toggle-style-package "framed-theorems")) + (when (and t (> (length (focus-variants-of t)) 1)) + (=> (balloon (icon "tm_switch.xpm") "Structured variant") + (dynamic (focus-variant-menu t)))))) + +;; 提取当前选区对应的章节层级节点。 +(tm-define (chapter-selection-tree . opt-t) + (with l '(chapter section subsection subsubsection) + (if (nnull? opt-t) + (and (tree-in? (car opt-t) (numbered-unnumbered-append l)) + l) + (and (selection-active-any?) + (with t (path->tree (selection-path)) + (and (== (selection-tree) t) + (let loop ((t t)) + (cond ((tree-in? t (numbered-unnumbered-append l)) + t) + ((tm-func? t 'document 1) + (loop (tree-ref t 0))) + (else #f))))))))) + +;; 返回当前章节节点可切换的结构变体列表。 +(tm-define (focus-variants-of t) + (:require (chapter-selection-tree t)) + (chapter-selection-tree t)) + +(menu-bind text-toolbar-chapter-icons + (with t (chapter-selection-tree) + (when (and t (numbered-context? t)) + ((check (balloon (icon "tm_numbered.xpm") "Numbered") "v" + (numbered-numbered? t)) + (numbered-toggle t))) + (when t + (mini #t + (with l (focus-variants-of t) + (assuming (<= (length l) 1) + (inert ((balloon (icon "tm_section.xpm") "Structured variant") + (noop)))) + (assuming (> (length l) 1) + (=> (balloon (icon "tm_section.xpm") "Structured variant") + (dynamic (focus-variant-menu t))))))) + (with var (and t (focus-section-title-style-var t)) + (when var + ((check (balloon (icon "tm_cell_left.xpm") "Left aligned") "v" + (== (safe-init-env var) "left")) + (init-env var "left")) + ((check (balloon (icon "tm_cell_center.xpm") "Centered") "v" + (== (safe-init-env var) "center")) + (init-env var "center")))) + (with num-var (and t (section-number-style-var t)) + (when num-var + (=> (balloon (icon "tm_focus_prefs.xpm") "number style") + ((check "Arabic (1, 2, 3)" "v" (== (safe-init-env num-var) "arabic")) + (init-env num-var "arabic")) + ((check "Hanzi (一, 二, 三)" "v" (== (safe-init-env num-var) "hanzi")) + (init-env num-var "hanzi")) + ((check "Roman (I, II, III)" "v" (== (safe-init-env num-var) "Roman")) + (init-env num-var "Roman")) + ((check "roman (i, ii, iii)" "v" (== (safe-init-env num-var) "roman")) + (init-env num-var "roman")) + ((check "Alpha (A, B, C)" "v" (== (safe-init-env num-var) "Alpha")) + (init-env num-var "Alpha")) + ((check "alpha (a, b, c)" "v" (== (safe-init-env num-var) "alpha")) + (init-env num-var "alpha")) + ((check (verbatim "Circle (①, ②, ③)") "v" + (== (safe-init-env num-var) "circle")) + (init-env num-var "circle"))))))) + +;; 判断当前选区是否处于表格或单元格相关上下文中。 +(tm-define (table-selection-context? t) + (or (selection-active-table?) + (and (selection-active-any?) + (table-markup-context? (selection-tree))))) + +;; 判断当前选区是否处于语义块上下文中。 +(tm-define (semantic-block-selection-context? t) + (not (not (semantic-block-selection-tree)))) + +;; 判断当前选区是否处于章节标题上下文中。 +(tm-define (chapter-selection-context? t) + (not (not (chapter-selection-tree)))) + +;; 合并两个模式列表,并去掉重复项。 +(define (mode-list-union l1 l2) + (if (null? l1) l2 + (with mode (car l1) + (mode-list-union + (cdr l1) + (let loop ((l l2)) + (cond ((null? l) (cons mode l2)) + ((== (car l) mode) l2) + (else (loop (cdr l))))))))) + +;; 递归计算当前选区树实际携带的所有 mode。 +(define (selection-tree-modes t mode) + (cond ((tree-atomic? t) + (list mode)) + ((and (tm-func? t 'with) (>= (tree-arity t) 3)) + (with n (- (tree-arity t) 1) + (let loop ((i 0) (mode* mode)) + (if (>= i n) + (selection-tree-modes (tree-ref t n) mode*) + (with var (tree->string (tree-ref t i)) + (with val (tree->string (tree-ref t (+ i 1))) + (loop (+ i 2) + (if (== var "mode") val mode*)))))))) + (else + (let loop ((i 0) (modes '())) + (if (>= i (tree-arity t)) + (if (null? modes) (list mode) modes) + (loop (+ i 1) + (mode-list-union + modes + (selection-tree-modes + (tree-ref t i) + (or (tm->string (tree-child-env t i "mode" mode)) + mode))))))))) + +;; 计算当前选区内容的唯一 mode;若混合了多种 mode 则返回 #f。 +(tm-define (selection-content-mode) + (and (selection-active-any?) + ;; `selection-path` 是选区两端的公共祖先,可能已经位于外层环境。 + ;; 这里使用选区起点恢复实际生效的 mode。 + (with mode (tree->string (get-env-tree-at "mode" (selection-get-start))) + (with modes (selection-tree-modes (selection-tree) mode) + (and (== (length modes) 1) (car modes)))))) + +;; 判断当前选区是否位于目录区域中,或直接包含整个目录节点。 +(tm-define (table-of-contents-selection-context? t) + (and (selection-active-any?) + (or (tree-search-upwards t + (lambda (u) (tm-func? u 'table-of-contents 2))) + (tm-func? (path->tree (selection-path)) 'table-of-contents 2) + (with sel (selection-tree) + (or (tm-func? sel 'table-of-contents 2) + (nnull? (tree-search sel + (lambda (u) + (tm-func? u 'table-of-contents 2))))))))) + +;; 判断当前选区是否包含图片节点。 +(tm-define (image-selection-context? t) + (and (selection-active-any?) + (or (not (not (any-image-context?))) + (nnull? (tree-search (selection-tree) + (lambda (u) (tree-is? u 'image))))))) + +;; 判断当前选区是否为纯文本上下文。 +(tm-define (text-selection-context? t) + (== (selection-content-mode) "text")) + +;; 判断当前选区是否为纯数学上下文。 +(tm-define (math-selection-context? t) + (== (selection-content-mode) "math")) + +;; 根据当前选区推导应显示的工具栏类别。 +(tm-define (text-toolbar-context t) + (and (selection-active-any?) + (cond + ((table-of-contents-selection-context? t) #f) + ((image-selection-context? t) #f) + ((table-selection-context? t) 'table) + ((chapter-selection-context? t) 'chapter) + ((semantic-block-selection-context? t) 'semantic) + ((math-selection-context? t) 'math) + ((text-selection-context? t) 'text) + (else #f)))) + +(menu-bind text-toolbar-icons + (with context (text-toolbar-context (focus-tree)) + (cond + ((== context 'table) + (link text-toolbar-table-icons)) + ((== context 'chapter) + (link text-toolbar-chapter-icons)) + ((== context 'semantic) + (link text-toolbar-semantic-icons)) + ((== context 'text) + (link text-toolbar-text-icons)) + ((== context 'math) + (link text-toolbar-math-icons))))) diff --git a/TeXmacs/progs/init-research.scm b/TeXmacs/progs/init-research.scm index f4a26d6690..8f87150abb 100644 --- a/TeXmacs/progs/init-research.scm +++ b/TeXmacs/progs/init-research.scm @@ -247,6 +247,7 @@ (lazy-define (table table-widgets) open-cell-properties open-table-properties) (tm-property (open-cell-properties) (:interactive #t)) (tm-property (open-table-properties) (:interactive #t)) +(use-modules (generic text-toolbar)) ;;(display* "time: " (- (texmacs-time) boot-start) "\n") ;;(display* "memory: " (texmacs-memory) " bytes\n") @@ -526,4 +527,3 @@ (display "Timing:") (display (- (texmacs-time) start-time)) (newline) ;(quit-TeXmacs) )))))))))))) - diff --git a/TeXmacs/tests/201_63.scm b/TeXmacs/tests/201_63.scm index 18c9ecb983..bab4cb2b14 100644 --- a/TeXmacs/tests/201_63.scm +++ b/TeXmacs/tests/201_63.scm @@ -109,38 +109,47 @@ ;; Mode checking simulation tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define (should-show-toolbar? in-math? in-prog? in-code? in-verbatim? - has-selection? selection-empty?) +(define (should-show-toolbar? in-math? in-prog? in-code? in-verbatim? + has-selection? selection-empty? + has-image? in-toc?) (and (not in-math?) (not in-prog?) (not in-code?) (not in-verbatim?) has-selection? - (not selection-empty?))) + (not selection-empty?) + (not has-image?) + (not in-toc?))) ;; 普通文本选区:应该显示 -(check (should-show-toolbar? #f #f #f #f #t #f) => #t) +(check (should-show-toolbar? #f #f #f #f #t #f #f #f) => #t) ;; 数学模式中:不应该显示 -(check (should-show-toolbar? #t #f #f #f #t #f) => #f) +(check (should-show-toolbar? #t #f #f #f #t #f #f #f) => #f) ;; 编程模式中:不应该显示 -(check (should-show-toolbar? #f #t #f #f #t #f) => #f) +(check (should-show-toolbar? #f #t #f #f #t #f #f #f) => #f) ;; 代码模式中:不应该显示 -(check (should-show-toolbar? #f #f #t #f #t #f) => #f) +(check (should-show-toolbar? #f #f #t #f #t #f #f #f) => #f) ;; 原文模式中:不应该显示 -(check (should-show-toolbar? #f #f #f #t #t #f) => #f) +(check (should-show-toolbar? #f #f #f #t #t #f #f #f) => #f) ;; 无选区时:不应该显示 -(check (should-show-toolbar? #f #f #f #f #f #f) => #f) +(check (should-show-toolbar? #f #f #f #f #f #f #f #f) => #f) ;; 空选区时:不应该显示 -(check (should-show-toolbar? #f #f #f #f #t #t) => #f) +(check (should-show-toolbar? #f #f #f #f #t #t #f #f) => #f) + +;; 图片选区:不应该显示 +(check (should-show-toolbar? #f #f #f #f #t #f #t #f) => #f) + +;; 目录内部选区:不应该显示 +(check (should-show-toolbar? #f #f #f #f #t #f #f #t) => #f) ;; 数学模式 + 无选区:不应该显示 -(check (should-show-toolbar? #t #f #f #f #f #f) => #f) +(check (should-show-toolbar? #t #f #f #f #f #f #f #f) => #f) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Viewport intersection tests diff --git a/TeXmacs/tests/tmu/201_63.tmu b/TeXmacs/tests/tmu/201_63.tmu new file mode 100644 index 0000000000..7b0d223e9b --- /dev/null +++ b/TeXmacs/tests/tmu/201_63.tmu @@ -0,0 +1,296 @@ +> + +> + +<\body> + + + 选中目录内任意内容或者选中整个目录都不应该出现文本悬浮框 + + <\table-of-contents|toc> + math>|.>>>>|> + + line|.>>>>|>> + + eqution|.>>>>|>> + + align|.>>>>|>> + + eqnarray|.>>>>|>> + + text>|.>>>>|> + + table>|.>>>>|> + + chatper>|.>>>>|> + + section|.>>>>|>> + + subsection|.>>>>|>> + + semantic>|.>>>>|> + + CODE>|.>>>>|> + + + \; + + \; + + + + 选中这一整行内容应该出现文本悬浮框 + + + + + + 选中这一整行内容不应该出现悬浮框 =\,\,\> 选中行内公式的任意内容都会出现数学公式悬浮框 + + + + 选中数学公式内的任意内容应该出现数学公式的悬浮框\ + + <\equation*> + f|>|> + + + <\equation*> + f|>|> + + + \; + + + + <\enumerate> + 选中矩阵单元格内的任意内容应该出现数学公式的悬浮框,除非在单元格内使用text命令添加文本 + + 矩阵的底层逻辑是表格,选中单元格会出现表格悬浮框\ + + + \; + + <\equation*> + ||>|||>|||>>>> + + + \; + + + + <\enumerate> + 选中align多行数学公式内的任意内容应该出现数学公式的悬浮框 + + align的底层逻辑是表格,选中单元格会出现表格悬浮框 + + + <\align> + |>>||>>>> + + + + + <\enumerate> + 选中eqnarray多行数学公式内的任意内容应该出现数学公式的悬浮框 + + eqnarray的底层逻辑是表格,选中单元格会出现表格悬浮框 + + + <\eqnarray*> + ||>>|||>>>> + + + \; + + + + <\enumerate> + 选中单元格应该出现的是表格的悬浮框 + + 如果选中的是表格内容的内容,根据表格内的内容展示显示不同的悬浮窗口,如果选中的内容是文本展示文本悬浮框,如果选中的内容是数学公式显示数学公式悬浮框,如果选中内容是文本和数学公式混合内容应该不显示任何悬浮窗口 + + + \ >|||>||>>||>>|>>|>>|>>|>>>>> + + \; + + <\big-table|\\\>>|||>||||>|>>|>>|>>|>>>>>>> + \; + + + \; + + + + <\enumerate> + 选中章节内部的内容应该是根据选中内容来展示不同的悬浮窗口,选中是文本内容展示文本悬浮框,选中内容是数学公式展示公式悬浮框,选中内容包含文本和公式不显示任何悬浮窗口 + + 选中章节整体展示章节悬浮框\ + + + \; + + \C\|~>V\>> + + \\C\V\>> + + + + <\enumerate> + 选中单元格应该出现的是表格的悬浮框,如果选中的是表格内容的内容,根据表格内的内容展示显示不同的悬浮窗口,如果选中的内容是文本展示文本悬浮框,如果选中的内容是数学公式显示数学公式悬浮框,如果选中内容是文本和数学公式混合内容应该不显示任何悬浮窗口 + + 选中语义块整体展示语义块悬浮框\ + + + <\theorem> + define > + + <\equation*> + a=b + + + + <\proposition> + 选中语义块整体展示语义块悬浮框 + + + <\corollary> + define + + + <\axiom> + define + + + <\assumption> + define + + + <\notation> + \; + + + <\remark> + \; + + + <\exercise> + \; + + + <\question> + \; + + + <\solution*> + \; + + + <\answer*> + \; + + + + + 选中代码块内部任意内容或者选中整个代码块都不显示悬浮框\ + + <\cpp-code> + include \iostream\ + + int main() + + { + + cout\\"hello"; + + } + + + + + 选中图片都不显示悬浮框\ + + |png>|0.8par|0.156098par||>> + + <\big-figure||png>|0.8par|0.156098par||>> + figure + + + \; + + +<\initial> + <\collection> + + + + + +<\references> + <\collection> + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + + + +<\auxiliary> + <\collection> + <\associate|figure> + |1>|> + figure\ + |> + + <\associate|table> + |1>|> + \; + |> + + <\associate|toc> + |math-font-series||1目录>|.>>>>|> + + |math-font-series||2text>|.>>>>|> + + |math-font-series||3math>|.>>>>|> + + |3.1line|.>>>>|>> + + |3.2eqution|.>>>>|>> + + |3.3matrix|.>>>>|>> + + |3.4align|.>>>>|>> + + |3.5eqnarray|.>>>>|>> + + |math-font-series||4table>|.>>>>|> + + |math-font-series||5chatper >|.>>>>|> + + |5.1subsection |\\\C\V\>|.>>>>|>> + + |5.1.1subsubsection |\\\C\V\>|.>>>>|>> + + |math-font-series||6semantic>|.>>>>|> + + |math-font-series||7CODE>|.>>>>|> + + |math-font-series||8figure>|.>>>>|> + + + diff --git a/devel/201_63.md b/devel/201_63.md index 9453d56cdb..874cba2d05 100644 --- a/devel/201_63.md +++ b/devel/201_63.md @@ -14,20 +14,32 @@ 2. **显示条件测试** **会显示的情况**(需同时满足): - - ✅ 不在数学模式(`in-math?`) - - ✅ 不在编程模式(`in-prog?`) - - ✅ 不在代码模式(`in-code?`) - - ✅ 不在原文模式(`in-verbatim?`) - - ✅ 有活动的文本选区 - ✅ 选区内容非空 + - ✅ 当前选区可以识别出工具栏上下文(`text-toolbar-context`) + - ✅ 当前上下文属于以下之一: + - ✅ 纯文本选区(`text`,且不包含图片) + - ✅ 纯数学选区(`math`) + - ✅ 表格或单元格相关选区(`table`) + - ✅ 章节标题相关选区(`chapter`) + - ✅ 语义块相关选区,如 theorem / proposition(`semantic`) + + **建议按 `TeXmacs/tests/tmu/201_63.tmu` 分区手动验证**: + - `toc`:选中目录内任意内容,或直接选中整个目录,不应该显示文本悬浮框 + - `math`:选中公式,应该显示数学工具栏 + - `text`:选中文字,应该显示文本工具栏 + - `table`:选中表格/单元格,应该显示表格工具栏 + - `chapter`:选中章节标题,应该显示章节工具栏 + - `semantic`:选中 theorem / proposition 等,应该显示语义工具栏 **不会显示的情况**(满足任一): - - ❌ 在数学公式中选中 - ❌ 在代码块中选中 - ❌ 在程序块中选中 - ❌ 在原文环境中选中 - ❌ 没有选区(仅光标) - ❌ 选区为空字符串 + - ❌ 选中目录内任意内容,或选中整个目录(`table-of-contents`) + - ❌ 选中图片对象,例如 `figure` 分区中的 `image` + - ❌ 混合多种 mode,无法归类为单一 `text` / `math` 上下文 - ❌ 正在拖拽鼠标(`left_dragging`) - ❌ 选区不在视图范围内 diff --git a/src/Edit/Interface/edit_mouse.cpp b/src/Edit/Interface/edit_mouse.cpp index 286af16fb8..87b852c2e2 100644 --- a/src/Edit/Interface/edit_mouse.cpp +++ b/src/Edit/Interface/edit_mouse.cpp @@ -1189,8 +1189,7 @@ edit_interface_rep::should_show_text_toolbar () { } text_toolbar_last_check= now; - if (as_bool (call ("in-math?")) || as_bool (call ("in-prog?")) || - as_bool (call ("in-code?")) || as_bool (call ("in-verbatim?"))) { + if (!as_bool (call ("text-toolbar-allowed-context?"))) { text_toolbar_last_result= false; return false; } diff --git a/src/Plugins/Qt/QTMTextToolbar.cpp b/src/Plugins/Qt/QTMTextToolbar.cpp index 7969b74b24..d342bdde2b 100644 --- a/src/Plugins/Qt/QTMTextToolbar.cpp +++ b/src/Plugins/Qt/QTMTextToolbar.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,28 @@ #include #include +void +QTMTextToolbar::prepareTextToolbarButton (QToolButton* button, + QAction* action) { + if (button == nullptr || action == nullptr) return; + + // Match focus-toolbar semantics: a check-backed toolbar action is rendered + // selected whenever TeXmacs marks it as checkable. + action->setChecked (action->isCheckable ()); + + button->setObjectName ("text-toolbar-button"); + button->setAutoRaise (true); + button->setDefaultAction (action); + button->setPopupMode (QToolButton::InstantPopup); +#if defined(Q_OS_MAC) + button->setProperty ("platform", "mac"); +#endif + if (tm_style_sheet == "") button->setStyle (qtmstyle ()); + button->setCheckable (action->isCheckable ()); + button->setChecked (action->isChecked ()); + button->update (); +} + // 悬浮工具栏创建函数 QTMTextToolbar::QTMTextToolbar (QWidget* parent, qt_simple_widget_rep* owner) : QWidget (parent), owner (owner), layout (nullptr), @@ -88,7 +111,6 @@ QTMTextToolbar::clearButtons () { void QTMTextToolbar::rebuildButtonsFromScheme () { - eval ("(use-modules (generic text-toolbar))"); object menu= eval ("'(horizontal (link text-toolbar-icons))"); object obj = call ("make-menu-widget", menu, 0); if (!is_widget (obj)) return; @@ -125,25 +147,162 @@ QTMTextToolbar::rebuildButtonsFromScheme () { } QToolButton* button= new QToolButton (this); - button->setObjectName ("text-toolbar-button"); - button->setAutoRaise (true); - button->setDefaultAction (action); - button->setPopupMode (QToolButton::InstantPopup); -#if defined(Q_OS_MAC) - button->setProperty ("platform", "mac"); -#endif - if (tm_style_sheet == "") button->setStyle (qtmstyle ()); + prepareTextToolbarButton (button, action); layout->addWidget (button); } autoSize (); } +void +QTMTextToolbar::updateButtonsFromScheme () { + QSize old_icon_size; + QSize old_button_size; + int old_height= (cached_height > 0) ? cached_height : height (); + + const QList old_buttons= + findChildren (QString (), Qt::FindChildrenRecursively); + if (!old_buttons.isEmpty ()) { + QToolButton* button= old_buttons.first (); + if (button) { + old_icon_size = button->iconSize (); + old_button_size= button->minimumSize (); + } + } + + object menu= eval ("'(horizontal (link text-toolbar-icons))"); + object obj = call ("make-menu-widget", menu, 0); + if (!is_widget (obj)) return; + + qt_widget new_toolbar_widget= concrete (as_widget (obj)); + QList* list = new_toolbar_widget->get_qactionlist (); + if (!list) return; + + bool can_update_in_place= (layout && layout->count () == list->count ()); + if (can_update_in_place) { + for (int i= 0; i < list->count (); ++i) { + QAction* action= list->at (i); + if (!action) { + can_update_in_place= false; + break; + } + + QLayoutItem* item= layout->itemAt (i); + if (!item) { + can_update_in_place= false; + break; + } + + if (action->isSeparator ()) { + if (!qobject_cast (item->widget ())) { + can_update_in_place= false; + break; + } + continue; + } + + if (action->text ().isNull () && action->icon ().isNull ()) { + if (!item->spacerItem ()) { + can_update_in_place= false; + break; + } + continue; + } + + if (qobject_cast (action)) { + can_update_in_place= false; + break; + } + + QToolButton* button= qobject_cast (item->widget ()); + if (!button) { + can_update_in_place= false; + break; + } + + QAction* current_action= button->defaultAction (); + bool current_has_menu= + (current_action != nullptr && current_action->menu () != nullptr); + bool next_has_menu= (action->menu () != nullptr); + if (current_has_menu != next_has_menu) { + can_update_in_place= false; + break; + } + } + } + + if (can_update_in_place) { + text_toolbar_widget= new_toolbar_widget; + for (int i= 0; i < list->count (); ++i) { + QAction* action= list->at (i); + if (!action || action->isSeparator ()) continue; + if (action->text ().isNull () && action->icon ().isNull ()) continue; + QToolButton* button= + qobject_cast (layout->itemAt (i)->widget ()); + if (!button) continue; + const QList stale_actions= button->actions (); + for (QAction* stale_action : stale_actions) { + if (stale_action) button->removeAction (stale_action); + } + button->setMenu (nullptr); + prepareTextToolbarButton (button, action); + } + cached_width = width (); + cached_height= height (); + return; + } + + text_toolbar_widget= new_toolbar_widget; + clearButtons (); + + for (int i= 0; i < list->count (); ++i) { + QAction* action= list->at (i); + if (!action) continue; + + if (action->isSeparator ()) { + QFrame* sep= new QFrame (this); + sep->setFrameShape (QFrame::VLine); + sep->setFrameShadow (QFrame::Plain); + sep->setFixedWidth (1); + sep->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Expanding); + layout->addWidget (sep); + continue; + } + + if (action->text ().isNull () && action->icon ().isNull ()) { + layout->addSpacing (8); + continue; + } + + if (QWidgetAction* wa= qobject_cast (action)) { + QWidget* w= wa->requestWidget (this); + if (w) layout->addWidget (w); + continue; + } + + QToolButton* button= new QToolButton (this); + prepareTextToolbarButton (button, action); + if (old_icon_size.isValid ()) button->setIconSize (old_icon_size); + if (old_button_size.isValid ()) button->setFixedSize (old_button_size); + layout->addWidget (button); + } + + if (layout) { + layout->invalidate (); + layout->activate (); + } + adjustSize (); + if (old_height > 0) resize (width (), old_height); + cached_width = width (); + cached_height= (old_height > 0) ? old_height : height (); +} + void QTMTextToolbar::showTextToolbar (qt_renderer_rep* ren, rectangle selr, double magf, int scroll_x, int scroll_y, int canvas_x, int canvas_y) { cachePosition (selr, magf, scroll_x, scroll_y, canvas_x, canvas_y); + updateButtonsFromScheme (); if (!selectionInView ()) { hide (); return; diff --git a/src/Plugins/Qt/QTMTextToolbar.hpp b/src/Plugins/Qt/QTMTextToolbar.hpp index a001bf751e..7cacf82389 100644 --- a/src/Plugins/Qt/QTMTextToolbar.hpp +++ b/src/Plugins/Qt/QTMTextToolbar.hpp @@ -16,10 +16,12 @@ #include "qt_simple_widget.hpp" #include "rectangles.hpp" +#include #include #include #include #include +#include #include class QTMTextToolbar : public QWidget { @@ -57,7 +59,9 @@ class QTMTextToolbar : public QWidget { void getCachedPosition (qt_renderer_rep* ren, int& x, int& y); bool selectionInView () const; void rebuildButtonsFromScheme (); + void updateButtonsFromScheme (); void clearButtons (); + void prepareTextToolbarButton (QToolButton* button, QAction* action); bool eventFilter (QObject* obj, QEvent* event) override; };