///|
/// A display container whose membership crosses the primary layout hierarchy.
/// The caller supplies inner containers before their outer containers.
pub struct Overlay {
id : String
members : ReadOnlyArray[String]
parent : String?
}
///|
pub fn Overlay::new(
id : String,
members : ArrayView[String],
parent? : String,
) -> Overlay {
{ id, members: ReadOnlyArray::from_array(members), parent, }
}
///|
/// Reframe crossing containers, then route using the same bounded geometry engine.
/// Ordinary layouts do not use this path and retain their canonical geometry.
pub fn Geometry::overlay(
self : Geometry,
definitions : ArrayView[Overlay],
limits~ : Limits = Limits::new(),
direction_hints~ : ArrayView[(String, @model.Direction)] = [],
) -> Result {
let budget = Budget::{ limits, diagnostics: [], work: 0, failed: false, }
let shapes = self.shapes.iter().to_array()
let by_id : Map[String, Int32] = Map([])
for i, shape in shapes {
if !budget.charge(shape.id.length() + 1) {
return layout_failure(budget.diagnostics)
}
by_id[shape.id] = i
}
for definition in definitions {
if !budget.charge(definition.members.length() + definition.id.length() + 1) {
return layout_failure(budget.diagnostics)
}
guard by_id.get(definition.id) is Some(index) else {
budget.error(
"swimlane/ownership",
"Overlay group is absent: " + definition.id,
)
return layout_failure(budget.diagnostics)
}
let shape = shapes[index]
if !shape.expanded {
continue
}
let mut bounds : Rect? = None
for id in definition.members {
if !budget.charge(id.length() + 1) {
return layout_failure(budget.diagnostics)
}
if by_id.get(id) is Some(member_index) {
let box = shapes[member_index].box
bounds = Some(
match bounds {
None => box
Some(old) =>
{
x: Double::min(old.x, box.x),
y: Double::min(old.y, box.y),
width: Double::max(old.x + old.width, box.x + box.width) -
Double::min(old.x, box.x),
height: Double::max(old.y + old.height, box.y + box.height) -
Double::min(old.y, box.y),
}
},
)
}
}
if bounds is Some(body) {
let title = shape.label.map(l => l.text).unwrap_or("")
let (w, h) = measure(title, weight=700)
let mut box : Rect = {
x: body.x - 24.0,
y: body.y - h - 32.0,
width: Double::max(body.width + 48.0, w + 40.0),
height: body.height + h + 56.0,
}
let mut label = make_label(title, shape.id, box, true, None)
let mut changed = true
while changed {
changed = false
if label is Some(candidate) {
for other in shapes {
if !budget.charge(1) {
return layout_failure(budget.diagnostics)
}
if other.id != shape.id &&
other.label is Some(other_label) &&
candidate.box.overlaps(other_label.box) {
let top = other_label.box.y - h - 24.0
let bottom = box.y + box.height
box = {
..box,
y: Double::min(box.y - 1.0, top),
height: bottom - Double::min(box.y - 1.0, top),
}
label = make_label(title, shape.id, box, true, None)
changed = true
break
}
}
}
}
shapes[index] = {
..shape,
box,
outline_box: box,
parent: definition.parent,
label,
}
}
}
let mut low_x = 32.0
let mut low_y = 32.0
for shape in shapes {
low_x = Double::min(low_x, shape.box.x)
low_y = Double::min(low_y, shape.box.y)
}
let dx = 32.0 - low_x
let dy = 32.0 - low_y
if dx != 0.0 || dy != 0.0 {
for i, shape in shapes {
fn shifted(box : Rect) -> Rect {
{ ..box, x: box.x + dx, y: box.y + dy, }
}
shapes[i] = {
..shape,
box: shifted(shape.box),
outline_box: shifted(shape.outline_box),
label: shape.label.map(l => { ..l, box: shifted(l.box), }),
}
}
}
let positions : Map[String, Int32] = Map([])
let count = self.view.nodes.length() +
self.view.groups.length() +
self.view.lanes.length() +
1
let parents = Array::make(count, -1)
for shape in shapes {
positions[shape.id] = shape.index
}
for shape in shapes {
if shape.parent.bind(id => positions.get(id)) is Some(parent) {
parents[shape.index] = parent
}
}
let selected : Array[(Int32, Int32, Int32)] = []
for edge in self.connections {
if positions.get(edge.source) is Some(a) &&
positions.get(edge.target) is Some(b) {
selected.push((edge.index, a, b))
}
}
let connections = route_connections(
self.view,
selected,
shapes,
parents,
budget,
direction_hints~,
)
if budget.failed {
return layout_failure(budget.diagnostics)
}
let mut width = 64.0
let mut height = 64.0
for shape in shapes {
width = Double::max(width, shape.box.x + shape.box.width + 32.0)
height = Double::max(height, shape.box.y + shape.box.height + 32.0)
}
for edge in connections {
for p in edge.line {
width = Double::max(width, p.x + 32.0)
height = Double::max(height, p.y + 32.0)
}
if edge.label is Some(label) {
width = Double::max(width, label.box.x + label.box.width + 32.0)
height = Double::max(height, label.box.y + label.box.height + 32.0)
}
}
if width > 1000000000.0 || height > 1000000000.0 {
budget.error(
"swimlane/layout-limit", "Overlay dimensions exceed numeric bounds",
)
return layout_failure(budget.diagnostics)
}
{
geometry: Some({
view: self.view,
shapes: ReadOnlyArray::from_array(shapes),
connections: ReadOnlyArray::from_array(connections),
width: width.ceil().to_int(),
height: height.ceil().to_int(),
}),
diagnostics: ReadOnlyArray::from_array(budget.diagnostics),
}
}