mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-10 12:29:58 -06:00
Merge origin/main (25881a8e) into w/objects
ROADMAP.md, docs/chart-parity.md, docs/gaps.md, docs/target-app-parity.md, docs/ui-parity.md: the header change notes combine this branch's note with main's, dated today; main's revision and progress rows stay and this branch's rows are re-dated on top. Code merged without conflict.
This commit is contained in:
@@ -10,7 +10,7 @@ mod pivot;
|
||||
mod print;
|
||||
|
||||
pub use pivot::{PivotCalcField, PivotDateGroup, PivotField, PivotFilter, PivotFunc, PivotLayout, PivotShowAs, PivotSort, PivotTable, PivotValue};
|
||||
pub use print::{EditRange, HfPage, LineInfo, Orientation, PrintSettings, SheetProtection, password_hash};
|
||||
pub use print::{EditRange, HfPage, LineInfo, Orientation, PageOrder, PrintSettings, SheetProtection, password_hash};
|
||||
|
||||
// ---------------------------------------------------------------- tables
|
||||
|
||||
@@ -540,6 +540,22 @@ impl Trendline {
|
||||
}
|
||||
}
|
||||
|
||||
/// A chart's data table (`c:dTable`), independent of the chart legend.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ChartDataTable {
|
||||
pub show_keys: bool,
|
||||
pub horizontal_border: bool,
|
||||
pub vertical_border: bool,
|
||||
pub outline: bool,
|
||||
}
|
||||
|
||||
impl Default for ChartDataTable {
|
||||
fn default() -> Self {
|
||||
Self { show_keys: true, horizontal_border: true, vertical_border: true, outline: true }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Chart {
|
||||
pub id: u32,
|
||||
@@ -549,6 +565,8 @@ pub struct Chart {
|
||||
pub series: Vec<Series>,
|
||||
pub legend: LegendPos,
|
||||
pub data_labels: bool,
|
||||
#[serde(default)]
|
||||
pub data_table: Option<ChartDataTable>,
|
||||
pub gridlines: bool,
|
||||
pub style: u32,
|
||||
pub x_title: Option<String>,
|
||||
@@ -562,6 +580,30 @@ pub struct Chart {
|
||||
pub pivot: Option<String>,
|
||||
}
|
||||
|
||||
impl Chart {
|
||||
/// Data tables belong to category-axis column, bar, line and area charts.
|
||||
pub fn supports_data_table(&self) -> bool {
|
||||
let eligible = |kind| {
|
||||
matches!(
|
||||
kind,
|
||||
ChartKind::ColumnClustered
|
||||
| ChartKind::ColumnStacked
|
||||
| ChartKind::ColumnStacked100
|
||||
| ChartKind::BarClustered
|
||||
| ChartKind::BarStacked
|
||||
| ChartKind::BarStacked100
|
||||
| ChartKind::Line
|
||||
| ChartKind::LineMarkers
|
||||
| ChartKind::LineStacked
|
||||
| ChartKind::Area
|
||||
| ChartKind::AreaStacked
|
||||
| ChartKind::AreaStacked100
|
||||
)
|
||||
};
|
||||
eligible(self.kind) || (self.kind == ChartKind::Combo && self.series.iter().all(|s| eligible(s.kind.unwrap_or(ChartKind::Line))))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Image {
|
||||
pub id: u32,
|
||||
|
||||
@@ -49,12 +49,52 @@ pub struct PrintSettings {
|
||||
pub hf_scale_with_doc: bool,
|
||||
#[serde(default = "yes")]
|
||||
pub hf_align_with_margins: bool,
|
||||
/// Page Setup › Sheet › Page order: the sequence pages are numbered and printed in when the
|
||||
/// sheet is more than one page wide and tall.
|
||||
#[serde(default)]
|
||||
pub page_order: PageOrder,
|
||||
}
|
||||
|
||||
fn yes() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// The sequence of a sheet's pages (XLSX `pageSetup pageOrder`, ECMA-376 `ST_PageOrder`).
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum PageOrder {
|
||||
/// Down the first column of pages, then down the next one (Excel's default).
|
||||
#[default]
|
||||
DownThenOver,
|
||||
/// Across the first row of pages, then across the next one.
|
||||
OverThenDown,
|
||||
}
|
||||
|
||||
impl PageOrder {
|
||||
/// The id `pageLayout.pageSetup` takes and XLSX writes.
|
||||
pub fn id(self) -> &'static str {
|
||||
match self {
|
||||
PageOrder::DownThenOver => "downThenOver",
|
||||
PageOrder::OverThenDown => "overThenDown",
|
||||
}
|
||||
}
|
||||
pub fn parse(id: &str) -> Option<PageOrder> {
|
||||
match id {
|
||||
"downThenOver" => Some(PageOrder::DownThenOver),
|
||||
"overThenDown" => Some(PageOrder::OverThenDown),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// The 0-based place in the printed sequence of the page in page row `row` and page column
|
||||
/// `col`, on a sheet `rows` pages tall and `cols` pages wide.
|
||||
pub fn index(self, row: usize, col: usize, rows: usize, cols: usize) -> usize {
|
||||
match self {
|
||||
PageOrder::DownThenOver => col.saturating_mul(rows).saturating_add(row),
|
||||
PageOrder::OverThenDown => row.saturating_mul(cols).saturating_add(col),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which header/footer pair a printed page uses.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum HfPage {
|
||||
@@ -146,6 +186,7 @@ impl Default for PrintSettings {
|
||||
even_footer: String::new(),
|
||||
hf_scale_with_doc: true,
|
||||
hf_align_with_margins: true,
|
||||
page_order: PageOrder::DownThenOver,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,3 +256,29 @@ pub struct LineInfo {
|
||||
#[serde(default)]
|
||||
pub custom: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn page_order_numbers_pages_and_old_settings_load_down_then_over() {
|
||||
// Two pages tall, three wide: (row, col) → place in the printed sequence.
|
||||
let place = |order: PageOrder| [(0, 0), (1, 0), (0, 1), (1, 1), (0, 2), (1, 2)].map(|(r, c)| order.index(r, c, 2, 3));
|
||||
assert_eq!(place(PageOrder::DownThenOver), [0, 1, 2, 3, 4, 5]);
|
||||
assert_eq!(place(PageOrder::OverThenDown), [0, 3, 1, 4, 2, 5]);
|
||||
assert_eq!(PageOrder::OverThenDown.index(usize::MAX, usize::MAX, usize::MAX, usize::MAX), usize::MAX);
|
||||
for order in [PageOrder::DownThenOver, PageOrder::OverThenDown] {
|
||||
assert_eq!(PageOrder::parse(order.id()), Some(order));
|
||||
}
|
||||
assert_eq!(PageOrder::parse("OverThenDown"), None);
|
||||
// Saved under the same id the command takes; settings saved before the field existed load
|
||||
// with Excel's default.
|
||||
let mut p = PrintSettings { page_order: PageOrder::OverThenDown, ..Default::default() };
|
||||
let mut saved = serde_json::to_value(&p).unwrap();
|
||||
assert_eq!(saved["page_order"], "overThenDown");
|
||||
assert!(saved.as_object_mut().unwrap().remove("page_order").is_some());
|
||||
p = serde_json::from_value(saved).unwrap();
|
||||
assert_eq!(p, PrintSettings::default());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user