Commit 1b8295b
Phase 5: merged cells (#125)
* Add sheet tab state with cross-sheet validation
xl_sheet() gains active, selected, visible and first_tab, reaching
worksheet_activate, worksheet_select, worksheet_hide and
worksheet_set_first_sheet.
These are the first worksheet settings whose rules span the whole workbook, so
unlike everything else in the sheet plan they cannot be checked one sheet at a
time. Excel requires that a hidden sheet is neither active nor selected, that
at most one sheet is active, that the first sheet is not hidden unless another
is made active, and that at least one sheet stays visible. libxlsxwriter
enforces none of them -- hide, activate and select all return void and simply
set a flag -- so a bad combination would produce a workbook Excel cannot open,
with no diagnostic.
.resolve_sheet_visibility() checks all four rules in write_xlsx(), where every
sheet is in scope, and names the sheet at fault. Plain data frames carry no tab
settings and are treated as unset throughout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Add hide_zero, right_to_left, selection and top_left
xl_sheet() gains four more view settings, reaching worksheet_hide_zero,
worksheet_right_to_left, worksheet_set_selection and
worksheet_set_top_left_cell. selection and top_left go through the shared range
resolver, so they take an Excel reference or a list(rows =, cols =) spec like
every other range argument.
Excel encodes which cell of a selection is active by the order its corners are
given, so worksheet_set_selection(6, 6, 3, 3) means "G7 to D4". writexl does
not expose that: the shared resolver rejects an inverted range, which is right
for every other caller, and one strict range parser is worth more than a niche
capability. The active cell is therefore always the range's top-left corner,
which is documented and covered by a test that pins the error.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Add split panes, converting from a cell reference
worksheet_split_panes() does not take a row and a column. Both arguments are
distances, in the units Excel uses for row height and column width, and the two
units differ from each other -- 15 means one default row, 8.43 one default
column. Passing 1 for "one row" would put the split a fifteenth of the way down
the first row, silently.
xl_sheet(split =) therefore takes a cell reference, as freeze does, and
converts: the distance above the split is the summed height of the rows before
it, the distance to its left the summed width of the columns before it. The
sums use the sheet's real geometry -- header row height, xl_row_spec() heights,
xl_col_spec() widths and auto_colwidth results -- so a split lands where it was
asked for on a sheet whose rows or columns have been resized, rather than only
on a default one. Tests pin that sensitivity in both directions.
list(vertical =, horizontal =) still accepts the raw units. split and freeze
together is an error, since Excel supports one or the other.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Document sheet view and navigation in the vignette and NEWS
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Add merged cells
xl_merge() merges a rectangle of cells into one; xl_sheet(merge =) takes one or
a list of them.
worksheet_merge_range() does more than record the merge: it writes the top-left
cell's text itself and blanks the rest of the range with the same format. So a
merge carries its own text rather than reusing whatever the data frame put
there, and it also settles when merges run. They are applied after the sheet's
rows, which makes a merge over filled cells keep only the merged value --
exactly what merging does in Excel. Applying them first would instead have the
row loop overwrite the merge's own blanks.
Writing back over emitted rows is not possible while libxlsxwriter streams rows
to disk, so any merge turns constant memory off. This is the second real
trigger for the resolver added in Phase 0.
Excel has no single-cell merge and libxlsxwriter returns a bare
parameter-validation error for one, so that case is caught in R where the
message can name the merge.
Note this contradicts the roadmap's stated reason for needing the memory flip:
worksheet_merge_range() has no optimize guard and streams correctly on its own.
The flip is needed because of when writexl applies merges, not because merging
cannot stream.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>1 parent 1b4d90a commit 1b8295b
27 files changed
Lines changed: 402 additions & 8 deletions
File tree
- R
- man
- src
- tests/testthat
- vignettes
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| 37 | + | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
39 | 43 | | |
40 | 44 | | |
41 | 45 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
71 | | - | |
| 71 | + | |
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
165 | 170 | | |
166 | 171 | | |
167 | 172 | | |
| |||
200 | 205 | | |
201 | 206 | | |
202 | 207 | | |
203 | | - | |
| 208 | + | |
| 209 | + | |
204 | 210 | | |
205 | 211 | | |
206 | 212 | | |
| |||
236 | 242 | | |
237 | 243 | | |
238 | 244 | | |
239 | | - | |
| 245 | + | |
| 246 | + | |
240 | 247 | | |
241 | 248 | | |
242 | 249 | | |
| |||
500 | 507 | | |
501 | 508 | | |
502 | 509 | | |
| 510 | + | |
| 511 | + | |
503 | 512 | | |
504 | 513 | | |
505 | 514 | | |
| |||
515 | 524 | | |
516 | 525 | | |
517 | 526 | | |
518 | | - | |
| 527 | + | |
| 528 | + | |
519 | 529 | | |
520 | 530 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
188 | 188 | | |
189 | 189 | | |
190 | 190 | | |
191 | | - | |
| 191 | + | |
192 | 192 | | |
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
197 | 197 | | |
198 | 198 | | |
| 199 | + | |
| 200 | + | |
199 | 201 | | |
200 | 202 | | |
201 | 203 | | |
202 | 204 | | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
203 | 211 | | |
204 | 212 | | |
205 | 213 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
0 commit comments