mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-11 16:34:47 -06:00
Conditional formatting: apply every matching rule's format
Only the first matching rule's format was used, so with a light red fill for >300 and a red border for >500, 510 showed only the border. Excel applies every matching rule; where two rules set the same part (fill, font colour, a border edge…), the rule with the higher priority wins, and Stop If True still ends the list.
This commit is contained in:
+66
-4
@@ -281,10 +281,12 @@ impl CfCache {
|
||||
};
|
||||
if let Some(st) = matched_style {
|
||||
any = true;
|
||||
out.style = Some(match out.style.take() {
|
||||
None => st.clone(),
|
||||
Some(prev) => prev,
|
||||
});
|
||||
// Every matching rule applies; where two set the same part, the rule with the
|
||||
// higher priority (seen first) wins.
|
||||
match &mut out.style {
|
||||
None => out.style = Some(st.clone()),
|
||||
Some(prev) => layer_under(prev, st),
|
||||
}
|
||||
if cf.stop_if_true {
|
||||
break;
|
||||
}
|
||||
@@ -294,6 +296,39 @@ impl CfCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the parts `lower` sets that `style` leaves unset (differential styles: default parts
|
||||
/// are unset).
|
||||
fn layer_under(style: &mut Style, lower: &Style) {
|
||||
if style.fill.pattern == PatternType::None {
|
||||
style.fill = lower.fill;
|
||||
}
|
||||
if style.font.color == Color::Auto {
|
||||
style.font.color = lower.font.color;
|
||||
}
|
||||
style.font.bold |= lower.font.bold;
|
||||
style.font.italic |= lower.font.italic;
|
||||
style.font.strike |= lower.font.strike;
|
||||
if style.font.underline == Underline::None {
|
||||
style.font.underline = lower.font.underline;
|
||||
}
|
||||
let b = &mut style.borders;
|
||||
for (dst, src) in [
|
||||
(&mut b.left, lower.borders.left),
|
||||
(&mut b.right, lower.borders.right),
|
||||
(&mut b.top, lower.borders.top),
|
||||
(&mut b.bottom, lower.borders.bottom),
|
||||
(&mut b.diag_down, lower.borders.diag_down),
|
||||
(&mut b.diag_up, lower.borders.diag_up),
|
||||
] {
|
||||
if dst.is_none() {
|
||||
*dst = src;
|
||||
}
|
||||
}
|
||||
if style.num_fmt.as_str() == "General" {
|
||||
style.num_fmt = lower.num_fmt.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// Shifts a rule formula written for the top-left cell of the range to `c`.
|
||||
fn relative(f: &str, first: CellRef, c: CellRef) -> String {
|
||||
if first == c {
|
||||
@@ -306,3 +341,30 @@ fn relative(f: &str, first: CellRef, c: CellRef) -> String {
|
||||
Err(_) => f.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn every_matching_rule_applies() {
|
||||
let mut s = crate::Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("range.setValues", json!({"range": "A1", "values": [[200], [400], [510]]})).unwrap();
|
||||
let rule = |op: &str, value: &str, preset: &str| json!({"range": "A1:A3", "rule": {"type": "cellIs", "operator": op, "value": value, "preset": preset}});
|
||||
s.execute("home.conditionalFormat", rule("greaterThan", "300", "lightRedFill")).unwrap();
|
||||
s.execute("home.conditionalFormat", rule("greaterThan", "500", "redBorder")).unwrap();
|
||||
let d = s.doc().unwrap();
|
||||
let mut cache = CfCache::new();
|
||||
let mut look = |a: &str| cache.look(&d.wb, 0, CellRef::parse(a).unwrap()).and_then(|l| l.style);
|
||||
assert!(look("A1").is_none());
|
||||
let only_fill = look("A2").unwrap();
|
||||
assert_eq!(only_fill.fill.pattern, PatternType::Solid);
|
||||
assert!(only_fill.borders.top.is_none());
|
||||
let both = look("A3").unwrap();
|
||||
assert_eq!(both.fill, only_fill.fill);
|
||||
assert!(!both.borders.top.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user