* Charts: draw every doughnut series as a ring (#1827)
* Charts: submit doughnut hole size and first slice angle when the drag ends (#1827)
---------
Co-authored-by: alosslessdev <alosslessdev@users.noreply.github.com>
* About: credits tables fit without side-scrolling and their columns resize
The Contributors table and the Models table were egui Grids inside a
two-way scroll area: they opened wider than the dialog, so they scrolled
sideways, and Company and Model sat almost touching. Both are now
egui_extras tables. Each column opens at the width of its header and widest
cell, the spare width goes to the text columns (Name; Company and Model),
and every column edge can be dragged. Widening a column grows the table
like a spreadsheet, and only then does it scroll sideways; the header stays
put while the rows scroll. Counts and dates are right-aligned and every
cell is vertically centred.
The sorted column stays highlighted and its direction shows on the
toolbar's sort-order button, so headers no longer reserve room for an
arrow. The opening widths come from fit_widths, a pure function with unit
tests that a fitted table fills its room exactly and that only the text
columns give way when room is short.
The About dialog opens 820 points wide, so every column fits at its natural width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Let every credits column be dragged, and refit changed tables
The last column was egui's remainder column, which has no drag handle, so
it could only be resized from the edge before it. It is now an ordinary
column with its own handle.
A table keeps its column widths, so switching to a longer name left the
Name column at its old width and clipped the text. The table now goes
back to the opening fit whenever that fit changes: a longer widest cell
or a resized window. Dragged widths are dropped at that point.
fit_widths could overflow the room when one column hit the minimum,
because the other columns did not take up its share. Columns now give
way in proportion to their width above the minimum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Refit credits tables on what they measure, not the fitted widths
Comparing the fitted widths missed the case it was meant for. Name is the
only growing column, so a longer name takes its extra width from the
spare room and the fit comes out the same: a dragged Name width stayed,
clipping the longer name.
It could also drop dragged widths by accident. The fit comes from the
available width inside the scroll area, which scrolling can nudge, so
scrolling a widened table could reset it.
The table now compares the natural widths and the clip rect's width,
which scrolling does not move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts:
- crates/xlsx/src/read.rs: main's names.unique() kept with this branch's
(sheet, read_controls) result from read_sheet.
- crates/ui-egui/tests/it/keytips_generated.rs: regenerated with
cargo xtask keytips from the merged keytips.json (413 sequences).
- docs/file-format-parity.md, docs/ui-parity.md: header and revision
history keep main's entries and this branch's form-controls entries.
The 10,000-character header spec was bounded, but each &A, &F or &Z
added the whole sheet name or path again, and a JSON workbook's sheet
name has no length limit. A field that would take a section past
64 KiB is now left out.
edit.fillSeries with dateUnit "weekday" stepped one calendar day at a
time, so a large step cost work proportional to its size for every
cell. It now uses add_weekdays, which moves whole weeks arithmetically.
* functions: match TEXTSPLIT/TEXTBEFORE/TEXTAFTER delimiters through an index, within a work limit
The delimiter matcher compared every delimiter, duplicates included, at
every position of the text. Delimiters are now deduplicated and grouped
by the character a match is anchored on, and a search that would
compare more than 100 million characters returns #VALUE!.
* functions: pass float literals in the delimiter test
The importer kept every cell of a row and only charged their text to
the 64 MiB allowance when the row finished, so one row of long cells
could hold up to 16,384 MiB before being refused. Text is now charged
as each cell finishes; the row charges only its repeats.
* functions: MODE counts through an index, FREQUENCY bins by binary search
MODE/MODE.SNGL/MODE.MULT searched the list of distinct numbers seen so far
for every input, and FREQUENCY scanned the sorted bins for every data value,
so both were quadratic in computed arrays. MODE now keys counts by value
(-0 folded into 0, NaN never merged) and keeps first-occurrence order;
FREQUENCY finds the first bin at or above each value with partition_point
over the order it already sorts. Results are unchanged.
* address review: FREQUENCY keeps NaN data in the overflow bin and NaN bins empty
* calc: list each dependent once when a change spreads
* calc: check the dependency closure against a walk of every cell
Random graphs of single-cell, narrow and wide range reads (some removed or unsettled), random seeds and changed areas: the closure with shared taken records and area lookups equals the one listing every dependent of every changed cell.
* calc: cap the text one & over arrays may build in all
* address review: concatenation over arrays stops at the byte budget without reserving the whole broadcast
Every source record scanned the filter's whole selection, so a large
selection multiplied by many records was slow even when nothing passed.
Each filter's labels are lowercased into a set once per computation.
wrap_lines split an over-long word by re-measuring and copying the
rest of the word for every piece it cut, and only checked its line
limit once the word was done, so one long word in a narrow text box
cost word length x pieces. Each new word also re-measured the whole
line built so far.
Line and piece widths are now running sums of glyph widths (the same
sum text_width makes), so every character is measured once, and the
line limit is checked while a word is being split. The lines produced
are unchanged; a test compares them with the old wrapper.
Importing a workbook whose sheets share a name rescanned every sheet for
every suffix tried, so a file of repeated names opened in cubic time.
SheetNames keeps the assigned names and the next suffix per stem.
Upstream folded cmd/insert/*.rs back into insert.rs, so the merge left
the policy check in a file that is no longer compiled. Restore that file
to upstream and apply the check in the live open_link.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- #3223: sniff the first 64 KiB for a web page and skip JSON and files over the text-import cap, so
every CSV/TSV is no longer decoded in full before the size check.
- #3253: SYLK relative offsets use checked arithmetic (no overflow on R[9223372036854775807]); at
most 1024 column-width records are kept; regression test.
- #3254: a single selected merged cell searches the whole sheet, as one selected cell does.
- #3252: the formula bar's height cap is half the room below the ribbon, not half the window, so
the worksheet stays visible in a short window (CodeRabbit Major).
- #3239, #3244, #3252, #3268: es/de/cs/zh/ko/ru/pt entries for the new interface strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses review comment 4456174694: the drag/expand cap was half the
window, so in a 400 pt window a ~195 pt bar left the grid about 1.6 pt
under the status bar and sheet tabs. The cap is now also limited by
the room left under the ribbon, keeping the status bar, sheet tabs,
column headings and two rows below the bar.
Test: ui-egui formula_bar_expand::an_expanded_formula_bar_leaves_room_for_the_worksheet_in_a_short_window.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>