ElyTable: a filtered range keeps its rows only for a cell inside it (R2.9)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-09 03:43:29 -04:00
1 parent e85884212b
commit cc67055ccc
3 files changed
+61 -12

No files matched your search

+1 -1
View File
@@ -263,5 +263,5 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely
- 2026-10-09(用户定:“打开方式”里不得出现 LibreOffice):系统会登记磁盘上能找到的 .app、挂载镜像里的 .app 与运行过的包,构建目录里的 `LibreOffice.app` 与测试挂载的官方镜像曾因此成为 xlsx 等的默认程序。现构建后由 `scripts/inert-lo.sh` 去掉其全部文件与 URL 声明、快速查看与聚焦插件,注销它,并在 `engines/` 放 `.metadata_never_index`;Ely 只取其 Frameworks 与 Resources。任何脚本与测试都不挂载 LibreOffice 镜像:LOK 探测用本构建的内核,单测所需的官方内核由 `scripts/extract-official-lo.sh` 以 7zz 解成无包的文件夹。`tests/scripts/lo-unregistered.sh` 以 NSWorkspace 核对 docx、xlsx、pptx、odt、csv 的“打开方式”里没有 LibreOffice。
- 2026-10-09(用户报:ElyTable 点格跳到别处、拖不出选区):Calc 在 `sc_print_twips_msgs` 下以打印 twips(行高原值)报光标、选区与 `.uno:SheetGeometryData`,图块与鼠标却在图块 twips(每行按 `ScViewData::ToPixel` 截成整像素),两者随行号漂移(5000 行处约 140 行)。视图对外一律打印 twips;`LokView::set_geometry` 收下工作表几何后,`sheet::Axis::tile`、`print` 照内核的截断换算(每像素 twips 取图块的像素与 twips 之比),绘制、显示、跳转与冻结都先换成图块 twips,鼠标以图块 twips 发出(`tile_point`),拖动句柄仍为打印 twips;`.uno:` 读回与应用(表头、填充柄)照旧用打印 twips,表头的起点取 `LokView::scrolled_start`。内核读鼠标用的比例原取最后一块图块,worker 现于每次 `setClientVisibleArea` 前以 `setClientZoom` 告知图块比例,点击不再依赖先画过图块。
- 2026-10-09(用户报:ElyPDF 改字报“这种字体画不出 V”):文档自带的子集字体常无可用的 Unicode 表(LibreOffice 导出的子集表为占位,0x00 起逐一对应字形号),按 Unicode 找字形一律失败。现以“文档自带字体”写字时直接用页面上的那个字体对象(`shim/ely_pen.c`;`pdf_load_font` 按对象缓存,与文字层的字体同一对象),以它自己的编码写:按文字提取的读法(ToUnicode,缺项或坏值退回 cid_to_ucs,同 pdf-op-run.c)倒出 Unicode 到编码的表,排版取它的宽度(`pdf_lookup_hmtx`),原有的 ToUnicode 与宽度不动,两个字共用一个字形时各写各的编码(原先按字形号写、重建 ToUnicode,共用字形的字被改写,且 CMap 漏写条目数,PDFKit 读不出字)。删去原文字块后页面资源里可能已无该字体,故按对象号以新名字重新引用。只支持编码即 CID 的字体(简单字体与 Identity-H),其余拒绝并说明;页面上没有的字仍报缺字。排版按同一套宽度量出的框,比较时留 0.01 点的余量(原块宽度即这些宽度之和,浮点误差曾使末字折行)。
- 2026-10-09:ElyTable 的自动筛选(R2.9)。内核会重新推算未命名的自动筛选区:每次打开筛选列表时向下扩到紧邻的数据行(`ExtendDataArea`、`ExtendBackColorArea`,为接上后来追加的行),命令从单格找到它时按连续数据区重算(`ScDocShell::GetDBData`:应用条件、关闭筛选及其撤销);选区下方紧邻的合计行因此进了列表、随数据一起被隐藏,存后重开的筛选区同样会扩。补丁 `0040-lok-calc-autofilter-keeps-its-rows.patch`:LOK 下已有的自动筛选区保持设定时的行(同 Excel),不再隐式扩展;单格开筛选时仍取整个数据区。代价:紧贴在筛选区下方追加的行不自动并入,需重新选定后再开筛选。
- 2026-10-09:ElyTable 的自动筛选(R2.9)。内核会重新推算未命名的自动筛选区:每次打开筛选列表时向下扩到紧邻的数据行(`ExtendDataArea`、`ExtendBackColorArea`,为接上后来追加的行),命令从单格找到它时按连续数据区重算(`ScDocShell::GetDBData`:应用条件、关闭筛选及其撤销);选区下方紧邻的合计行因此进了列表、随数据一起被隐藏,存后重开的筛选区同样会扩。补丁 `0040-lok-calc-autofilter-keeps-its-rows.patch`:LOK 下已有的自动筛选区保持设定时的行(同 Excel),不再隐式扩展(光标在区内时;光标在同表另一块数据里即按那一块);单格开筛选时仍取整个数据区。代价:紧贴在筛选区下方追加的行不自动并入,需重新选定后再开筛选。
- 2026-10-09:ElyTree 的差异按文件类型着色(R2.12)。组件库 7f2ac3e:`DiffViewer` 按路径的扩展名判定散文与纯文本(md、markdown、txt、rst、adoc、org、csv、tsv、log 等,无扩展名同),这些文件不按代码着色(原先一律套通用的代码词法,Markdown 的 `#`、引号等被当作代码上色)。同批带入上游的块编辑器跨块选择、浮层滚轮、输入法候选框位置、插入点按设备像素对齐与格式修正。
+54 -6
View File
@@ -18,6 +18,33 @@ const BLOCK: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
</table:table></office:spreadsheet></office:body>
</office:document>"#;
/// Two blocks: A1:A5 as in `BLOCK`, and D1:D3.
const TWO: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
<office:body><office:spreadsheet><table:table table:name="S">
<table:table-row><table:table-cell><text:p>Name</text:p></table:table-cell><table:table-cell table:number-columns-repeated="2"/><table:table-cell><text:p>Other</text:p></table:table-cell></table:table-row>
<table:table-row><table:table-cell office:value-type="float" office:value="1"><text:p>1</text:p></table:table-cell><table:table-cell table:number-columns-repeated="2"/><table:table-cell office:value-type="float" office:value="9"><text:p>9</text:p></table:table-cell></table:table-row>
<table:table-row><table:table-cell office:value-type="float" office:value="2"><text:p>2</text:p></table:table-cell><table:table-cell table:number-columns-repeated="2"/><table:table-cell office:value-type="float" office:value="10"><text:p>10</text:p></table:table-cell></table:table-row>
<table:table-row><table:table-cell office:value-type="float" office:value="3"><text:p>3</text:p></table:table-cell></table:table-row>
<table:table-row><table:table-cell office:value-type="float" office:value="6"><text:p>6</text:p></table:table-cell></table:table-row>
</table:table></office:spreadsheet></office:body>
</office:document>"#;
/// The book saved as flat XML.
fn saved(worker: &mut Worker, doc: &Doc, dir: &std::path::Path) -> String {
let out = dir.join("kept.fods");
doc.save_as(worker, &crate::file_url(&out), "fods").expect("saves");
std::fs::read_to_string(&out).expect("the saved book")
}
/// Whether the row holding `value` is hidden by a filter.
fn hidden(xml: &str, value: &str) -> bool {
let before = &xml[..xml.find(&format!("office:value=\"{value}\"")).expect("the value")];
before[before.rfind("<table:table-row").expect("its row")..].contains("table:visibility=\"filter\"")
}
/// Column A's filter entries.
fn entries(worker: &mut Worker, doc: &Doc) -> Vec<String> {
let query = sheet::cell_query(sheet::FILTER, 0, 0);
@@ -38,13 +65,9 @@ fn keep_one(worker: &mut Worker, doc: &Doc, dir: &std::path::Path) -> (String, b
worker.run(doc, "cell.goto", &["A1"]).expect("A1");
let one = r#"{"column":0,"conditions":[{"op":"values","values":["1"]}]}"#;
worker.run(doc, "data.filter", &[one]).expect("keeps 1");
let out = dir.join("kept.fods");
doc.save_as(worker, &crate::file_url(&out), "fods").expect("saves");
let xml = std::fs::read_to_string(&out).expect("the saved book");
let xml = saved(worker, doc, dir);
let range = xml.split("table:target-range-address=\"").nth(1).expect("a filter range");
let total = &xml[..xml.find("office:value=\"6\"").expect("the total")];
let row = &total[total.rfind("<table:table-row").expect("its row")..];
(range[..range.find('"').expect("closed")].to_string(), row.contains("table:visibility=\"filter\""))
(range[..range.find('"').expect("closed")].to_string(), hidden(&xml, "6"))
}
#[test]
@@ -104,3 +127,28 @@ fn a_saved_autofilter_range_keeps_its_rows_when_reopened() {
assert_eq!(keep_one(&mut worker, &doc, dir.path()), ("S.A1:S.A4".into(), false), "{format}");
}
}
#[test]
fn one_cell_in_another_block_filters_that_block() {
if install().is_none() {
eprintln!("SKIP: no pinned LOK build");
return;
}
let (dir, mut worker, doc) = open_book(TWO);
worker.run(&doc, "cell.goto", &["A1:A4"]).expect("marks the block");
worker.run(&doc, "data.autofilter", &[]).expect("filters A");
worker.run(&doc, "cell.goto", &["D1"]).expect("D1");
let nine = r#"{"conditions":[{"field":3,"op":"equals","value":"9"}]}"#;
worker.run(&doc, "data.filter", &[nine]).expect("keeps 9");
let xml = saved(&mut worker, &doc, dir.path());
assert!(hidden(&xml, "10") && !hidden(&xml, "9"), "D keeps 9 alone");
assert!(!hidden(&xml, "3") && !hidden(&xml, "6"), "A's rows past D stay");
let (_dir, mut worker, doc) = open_book(TWO);
worker.run(&doc, "cell.goto", &["A1:A4"]).expect("marks the block");
worker.run(&doc, "data.autofilter", &[]).expect("filters A");
worker.run(&doc, "cell.goto", &["D1"]).expect("D1");
worker.run(&doc, "data.autofilter", &[]).expect("filters D");
let json = worker.values(&doc, &sheet::cell_query(sheet::FILTER, 3, 0)).expect("D's entries");
let entries: Vec<String> = sheet::filter_values(&json).expect("parses").entries.into_iter().map(|e| e.text).collect();
assert_eq!(entries, ["9", "10"], "an AutoFilter from D1 is D's");
}
@@ -7,8 +7,8 @@ appended rows join) and whenever a command finds it from a single cell
its undo). A totals row right under a marked block joined the filter,
showed in its list and hid with the data; a saved range reopened grew
the same way. Under LOK the range keeps the rows it was set on, as in
Excel. A single cell still takes the data area when the filter is set.
Excel, while the cursor is inside it; a cell in another block finds
that block. A single cell still takes the data area when the filter is set.
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index b1d714c55..9a01847ee 100644
--- a/sc/source/core/tool/dbdata.cxx
@@ -41,7 +41,7 @@ index b1d714c55..9a01847ee 100644
SCCOL nOldCol1 = nStartCol, nOldCol2 = nEndCol;
SCROW nOldEndRow = nEndRow;
diff --git a/sc/source/ui/docshell/docsh5.cxx b/sc/source/ui/docshell/docsh5.cxx
index d154597c3..54a2344af 100644
index d154597c3..5099dbe6f 100644
--- a/sc/source/ui/docshell/docsh5.cxx
+++ b/sc/source/ui/docshell/docsh5.cxx
@@ -19,6 +19,7 @@
@@ -52,13 +52,14 @@ index d154597c3..54a2344af 100644
#include <cassert>
@@ -147,7 +148,9 @@ ScDBData* ScDocShell::GetDBData( const ScRange& rMarked, ScGetDBMode eMode, ScGe
@@ -147,7 +148,10 @@ ScDBData* ScDocShell::GetDBData( const ScRange& rMarked, ScGetDBMode eMode, ScGe
if (!bSelected)
{
bUseThis = true;
- if ( bIsNoName && (eMode == SC_DB_MAKE || eMode == SC_DB_AUTOFILTER) )
+ // LOK: an AutoFilter range keeps its rows, as in Excel.
+ const bool bKeep = comphelper::LibreOfficeKit::isActive() && pData->HasAutoFilter();
+ const bool bKeep = comphelper::LibreOfficeKit::isActive() && pData->HasAutoFilter()
+ && nCol >= nOldCol1 && nCol <= nOldCol2 && nRow >= nOldRow1 && nRow <= nOldRow2;
+ if ( bIsNoName && !bKeep && (eMode == SC_DB_MAKE || eMode == SC_DB_AUTOFILTER) )
{
// If nothing marked or only one row marked, adapt