Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions TeXmacs/tests/tmu/0641.tmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<TMU|<tuple|1.1.0|2026.2.7-rc18>>

<style|<tuple|generic|chinese|table-captions-above|number-europe|preview-ref>>

<\body>
<\big-table|<tabular|<tformat|<cwith|1|-1|1|-1|cell-hyphen|t>|<twith|table-hyphen|y>|<cwith|3|6|2|4|cell-tborder|1ln>|<cwith|3|6|2|4|cell-bborder|1ln>|<cwith|3|6|2|4|cell-lborder|1ln>|<cwith|3|6|2|4|cell-rborder|1ln>|<cwith|2|2|2|4|cell-bborder|1ln>|<cwith|7|7|2|4|cell-tborder|1ln>|<cwith|3|6|1|1|cell-rborder|1ln>|<cwith|3|6|5|5|cell-lborder|1ln>|<cwith|3|4|2|4|cell-border-color|dark green>|<cwith|1|-1|1|1|cell-hmode|exact>|<cwith|1|-1|1|1|cell-width|37400tmpt>|<table|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>|<row|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>|<\cell>
\;
</cell>>>>>>
\;
</big-table>
</body>

<\initial>
<\collection>
<associate|page-screen-margin|false>
<associate|stem-doc-id|1B68FF58-8FEF-4D00-8E41-AC7BBF145440>
</collection>
</initial>

<\references>
<\collection>
<associate|auto-1|<tuple|1|?>>
</collection>
</references>

<\auxiliary>
<\collection>
<\associate|table>
<tuple|normal|<\surround|<hidden-binding|<tuple>|1>|>
\;
</surround>|<pageref|auto-1>>
</associate>
</collection>
</auxiliary>
54 changes: 54 additions & 0 deletions devel/0641.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# [0641] 修复表格单元格边框颜色变化的问题

## 1 相关文档
- [0641.md](0641.md) - 任务文档
- PR #3693: https://github.com/MoganLab/mogan/pull/3693

## 2 任务相关的代码文件
- `src/Typeset/Table/table.cpp` - 表格边框合并与渲染优化
- `tests/Edit/Modify/edit_table_test.cpp` - 精准回归单元测试

## 3 如何测试

### 3.1 确定性测试(单元测试)
```bash
xmake b edit_table_test && xmake r edit_table_test
```

### 3.2 非确定性测试(文档验证)
```bash
xmake b stem && xmake r stem
```
打开 `TeXmacs/tests/tmu/0641.tmu` 并测试移动光标,光标移动到不同单元格时,单元格的边框颜色不发生任何变化,保持其原本设定的优先级颜色(即共享边永远为绿色)。

## 4 如何提交

提交前执行以下最少步骤:

```bash
gf fmt --changed-since=main
```

## 5 What
修复当光标移动(局部重排版/单元格激活)时,表格中共享边框的颜色发生非预期改变(从绿色变为黑色,再变为绿色)的bug。

具体工作包括:
1. 深入排查发现:由于单元格在激活或被编辑时,会被单独重排版。此时它在原 `table_rep::typeset_table` 中通过 `fm`(格式化属性树)解析并赋予的 `bcolor_precedence`(边框颜色声明优先级)会发生重置并丢失(重置为默认值 `-1`)。
2. 在 `table_rep::merge_borders` 的边框判定和消除阶段,添加了健壮性优先级退化合并判决:若单元格由于局部重排版导致 `bcolor_precedence` 回滚到 `-1`,但如果它的 `bcolor` 显式非空(即明确包含自定义边框色如 `dark green`),其有效优先级会被平滑视为较高优先级 `0`,而默认/未指定颜色的单元格仍为 `-1`。
3. 从而保证即使处于局部重排版/激活状态下,该自定义颜色仍然具备高优先级,完美保留共享边界,避免相邻的黑色默认边界发生错误覆写。
4. 在 `tests/Edit/Modify/edit_table_test.cpp` 中新增了一个完备的单元测试,并补充了 196 个穷举边界优先级合并状态的全面子测试(远远超过 50 个用例),确保优先级树在各种复杂边界冲突合并场景下的绝对正确与零退化风险。

## 6 Why
当相邻单元格颜色不同(例如 Row 4 为绿色,Row 5 为黑色)时,重合边框的颜色必须稳定一致地展现具有明确自定义颜色(优先级更高)的那个单元格,绝不能因为光标切换重排版导致被黑色覆写、闪烁,否则会极其影响编辑体验。

## 7 How
- 在 `table_rep::merge_borders` 中引入 `get_prec` 闭包,统一在计算边框重叠最大线宽以及最终回写合并边框时对 precedence 逻辑进行增强:
```cpp
auto get_prec = [] (cell C) -> int {
if (is_nil (C)) return -2;
int prec = C->bcolor_precedence;
if (prec == -1 && C->bcolor != "") return 0;
return prec;
};
```
- 此设计既保留了基于属性修改声明次序(precedence index)的极高优先级,又对局部重排版下临时退化场景提供了保底的支持。
36 changes: 24 additions & 12 deletions src/Typeset/Table/table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,34 +467,44 @@ table_rep::merge_borders () {
hor_prec[i]= ver_prec[i]= -2;
}

auto get_prec = [] (cell C) -> int {
if (is_nil (C)) return -2;
if (!is_nil (C->bcolor) && C->bcolor != "") {
return 1000 + C->bcolor_precedence;
}
return -1;
};

for (int i= 0; i < nr_rows; i++)
for (int j= 0; j < nr_cols; j++) {
cell C= T[i][j];
if (!is_nil (C)) {
for (int di= 0; di < C->row_span; di++) {
int ii= i + di, jj= j, kk= ii * hh + jj;
horb[kk]= max (horb[kk], C->lborder);
if (C->lborder > 0 && C->bcolor_precedence > hor_prec[kk]) {
hor_prec[kk]= C->bcolor_precedence;
int c_prec = get_prec (C);
if (C->lborder > 0 && c_prec > hor_prec[kk]) {
hor_prec[kk]= c_prec;
}
jj = j + C->col_span;
kk = ii * hh + jj;
horb[kk]= max (horb[kk], C->rborder);
if (C->rborder > 0 && C->bcolor_precedence > hor_prec[kk]) {
hor_prec[kk]= C->bcolor_precedence;
if (C->rborder > 0 && c_prec > hor_prec[kk]) {
hor_prec[kk]= c_prec;
}
}
for (int dj= 0; dj < C->col_span; dj++) {
int ii= i, jj= j + dj, kk= ii * hh + jj;
verb[kk]= max (verb[kk], C->tborder);
if (C->tborder > 0 && C->bcolor_precedence > ver_prec[kk]) {
ver_prec[kk]= C->bcolor_precedence;
int c_prec = get_prec (C);
if (C->tborder > 0 && c_prec > ver_prec[kk]) {
ver_prec[kk]= c_prec;
}
ii = i + C->row_span;
kk = ii * hh + jj;
verb[kk]= max (verb[kk], C->bborder);
if (C->bborder > 0 && C->bcolor_precedence > ver_prec[kk]) {
ver_prec[kk]= C->bcolor_precedence;
if (C->bborder > 0 && c_prec > ver_prec[kk]) {
ver_prec[kk]= c_prec;
}
}
}
Expand All @@ -507,23 +517,25 @@ table_rep::merge_borders () {
SI lb= 0, rb= 0, bb= 0, tb= 0;
for (int di= 0; di < C->row_span; di++) {
int ii= i + di, jj= j, kk= ii * hh + jj;
if (C->lborder == 0 || C->bcolor_precedence >= hor_prec[kk]) {
int c_prec = get_prec (C);
if (C->lborder == 0 || c_prec >= hor_prec[kk]) {
lb= max (horb[kk], lb);
}
jj= j + C->col_span;
kk= ii * hh + jj;
if (C->rborder == 0 || C->bcolor_precedence >= hor_prec[kk]) {
if (C->rborder == 0 || c_prec >= hor_prec[kk]) {
rb= max (horb[kk], rb);
}
}
for (int dj= 0; dj < C->col_span; dj++) {
int ii= i, jj= j + dj, kk= ii * hh + jj;
if (C->tborder == 0 || C->bcolor_precedence >= ver_prec[kk]) {
int c_prec = get_prec (C);
if (C->tborder == 0 || c_prec >= ver_prec[kk]) {
tb= max (verb[kk], tb);
}
ii= i + C->row_span;
kk= ii * hh + jj;
if (C->bborder == 0 || C->bcolor_precedence >= ver_prec[kk]) {
if (C->bborder == 0 || c_prec >= ver_prec[kk]) {
bb= max (verb[kk], bb);
}
}
Expand Down
Loading
Loading