Compare commits

...
Author SHA1 Message Date
Dylan M. Taylor 61f36a78c8 refactor: split complex functions for Sonar S3776
Extract group_bracket from outline layout and move_marks from data
outline_settings. No behaviour change.
2026-10-25 21:32:31 -04:00
2 changed files with 59 additions and 49 deletions
+29 -25
View File
@@ -1454,6 +1454,34 @@ fn show_levels(s: &mut Session, p: &Json) -> Result<Json> {
/// Data ▸ Outline ▸ Settings: whether summary rows sit below their detail and summary columns /// Data ▸ Outline ▸ Settings: whether summary rows sit below their detail and summary columns
/// right of it. A changed direction moves each collapsed group's mark (+) to its new summary line. /// right of it. A changed direction moves each collapsed group's mark (+) to its new summary line.
/// Moves the collapsed marks of a line axis to the summary line on its new side (`on` is the new
/// setting, `was_before` the old one). A line that nested groups share keeps its mark on the outermost
/// group whose detail is all hidden, or the outermost when none is.
fn move_marks(map: &mut std::collections::BTreeMap<u32, LineInfo>, was_before: bool, on: bool) {
let mut marked: Vec<outline::OutlineGroup> =
outline::groups_with(map, was_before).into_iter().filter(|g| g.has_summary() && map.get(&g.summary()).is_some_and(|e| e.collapsed)).collect();
// A mark on a line that nested groups share moves with the outermost of them whose
// detail is all hidden, or with the outermost when none is.
let mut owner: std::collections::BTreeMap<u32, (bool, u8)> = std::collections::BTreeMap::new();
for g in &marked {
let key = (!g.collapsed, g.level);
let best = owner.entry(g.summary()).or_insert(key);
*best = (*best).min(key);
}
marked.retain(|g| owner.get(&g.summary()) == Some(&(!g.collapsed, g.level)));
for g in &marked {
if let Some(e) = map.get_mut(&g.summary()) {
e.collapsed = false;
}
}
for g in marked {
let g = outline::OutlineGroup { summary_before: !on, ..g };
if g.has_summary() {
map.entry(g.summary()).or_default().collapsed = true;
}
}
}
fn outline_settings(s: &mut Session, p: &Json) -> Result<Json> { fn outline_settings(s: &mut Session, p: &Json) -> Result<Json> {
let si = target_sheet(s, p)?; let si = target_sheet(s, p)?;
edit(s, |cx| { edit(s, |cx| {
@@ -1469,31 +1497,7 @@ fn outline_settings(s: &mut Session, p: &Json) -> Result<Json> {
if was_before != on { if was_before != on {
continue; continue;
} }
let map = if rows { &mut sh.rows } else { &mut sh.cols }; move_marks(if rows { &mut sh.rows } else { &mut sh.cols }, was_before, on);
let mut marked: Vec<outline::OutlineGroup> = outline::groups_with(map, was_before)
.into_iter()
.filter(|g| g.has_summary() && map.get(&g.summary()).is_some_and(|e| e.collapsed))
.collect();
// A mark on a line that nested groups share moves with the outermost of them whose
// detail is all hidden, or with the outermost when none is.
let mut owner: std::collections::BTreeMap<u32, (bool, u8)> = std::collections::BTreeMap::new();
for g in &marked {
let key = (!g.collapsed, g.level);
let best = owner.entry(g.summary()).or_insert(key);
*best = (*best).min(key);
}
marked.retain(|g| owner.get(&g.summary()) == Some(&(!g.collapsed, g.level)));
for g in &marked {
if let Some(e) = map.get_mut(&g.summary()) {
e.collapsed = false;
}
}
for g in marked {
let g = outline::OutlineGroup { summary_before: !on, ..g };
if g.has_summary() {
map.entry(g.summary()).or_default().collapsed = true;
}
}
} }
Ok(json!({"summaryBelow": sh.summary_below, "summaryRight": sh.summary_right})) Ok(json!({"summaryBelow": sh.summary_below, "summaryRight": sh.summary_right}))
}) })
+30 -24
View File
@@ -165,6 +165,35 @@ impl Dir<'_> {
} }
/// The band's buttons and brackets for this frame. /// The band's buttons and brackets for this frame.
/// The bracket for one group's detail, and its tick, on the lane `across`; `button` is the group's
/// summary button when it is on screen.
fn group_bracket(out: &mut Layout, dir: &Dir, grp: &OutlineGroup, button: Option<f32>, across: f32, side: f32) {
let first = dir.first_visible(grp.start, grp.end);
let Some((first_line, (a0, _))) = first else { return };
if grp.summary_before {
// From the button down (or across) to the last detail line, with the tick there.
let a0 = button.map_or(a0, |mid| mid + side / 2.0);
let end = dir.start(grp.end) + dir.size(grp.end);
let a1 = end.min(dir.hi);
if a1 <= a0 {
return;
}
let tick = (end <= dir.hi).then(|| [dir.point(a1 - 0.5, across), dir.point(a1 - 0.5, across + side / 2.0)]);
out.brackets.push(Bracket { line: [dir.point(a0, across), dir.point(a1 - 0.5, across)], tick });
return;
}
let a1 = match button {
Some(mid) => mid - side / 2.0,
None => dir.start(grp.summary()).min(dir.hi),
};
if a1 <= a0 {
return;
}
let start_shown = first_line == grp.start && (a0 - dir.start(grp.start)).abs() < 0.5;
let tick = start_shown.then(|| [dir.point(a0 + 0.5, across), dir.point(a0 + 0.5, across + side / 2.0)]);
out.brackets.push(Bracket { line: [dir.point(a0 + 0.5, across), dir.point(a1, across)], tick });
}
pub fn layout(geo: &Geo, sh: &Sheet) -> Layout { pub fn layout(geo: &Geo, sh: &Sheet) -> Layout {
let mut out = Layout::default(); let mut out = Layout::default();
let zoom = (geo.z / DISPLAY_SCALE).max(0.1); let zoom = (geo.z / DISPLAY_SCALE).max(0.1);
@@ -212,30 +241,7 @@ pub fn layout(geo: &Geo, sh: &Sheet) -> Layout {
continue; continue;
} }
// The bracket, over the part of the detail that is on screen. // The bracket, over the part of the detail that is on screen.
let first = dir.first_visible(grp.start, grp.end); group_bracket(&mut out, dir, grp, button, across, side);
let Some((first_line, (a0, _))) = first else { continue };
if grp.summary_before {
// From the button down (or across) to the last detail line, with the tick there.
let a0 = button.map_or(a0, |mid| mid + side / 2.0);
let end = dir.start(grp.end) + dir.size(grp.end);
let a1 = end.min(dir.hi);
if a1 <= a0 {
continue;
}
let tick = (end <= dir.hi).then(|| [dir.point(a1 - 0.5, across), dir.point(a1 - 0.5, across + side / 2.0)]);
out.brackets.push(Bracket { line: [dir.point(a0, across), dir.point(a1 - 0.5, across)], tick });
continue;
}
let a1 = match button {
Some(mid) => mid - side / 2.0,
None => dir.start(grp.summary()).min(dir.hi),
};
if a1 <= a0 {
continue;
}
let start_shown = first_line == grp.start && (a0 - dir.start(grp.start)).abs() < 0.5;
let tick = start_shown.then(|| [dir.point(a0 + 0.5, across), dir.point(a0 + 0.5, across + side / 2.0)]);
out.brackets.push(Bracket { line: [dir.point(a0 + 0.5, across), dir.point(a1, across)], tick });
} }
} }
out out