slint
slint copied to clipboard
Drop shadow rendering in `for` elements does not always render underneath sibling elements
The following test-case renders the green drop-shadow over the sibling rectangle, when it should not:
export Test := Window {
background: white;
Rectangle {
width: 50%;
y: 50% * parent.height + 5px;
background: black;
}
Rectangle {
width: 50%;
height: 50%;
background: gray;
drop-shadow-offset-x: 10px;
drop-shadow-offset-y: 25px;
drop-shadow-color: green;
drop-shadow-blur: 10px;
}
}
The same also happens in a repeater, when applying a negative offset:
export Test := Window {
background: white;
VerticalLayout {
spacing: 15px;
for i in 10: Rectangle {
background: gray;
drop-shadow-offset-x: 10px;
drop-shadow-offset-y: -25px;
drop-shadow-color: green;
drop-shadow-blur: 10px;
}
}
}
One way to solve that would be with z properies: https://github.com/sixtyfpsui/sixtyfps/issues/221
Edit: Actually that's not going to help much because the for only yield to one element.
We discussed this a bit and came up with a possible plan:
- A shadow applied to a
for-repeated element would generate a secondfor, which mirrors the originalforand is placed before the originalforin the item tree - to ensure that shadows are rendered below. - The item rendering needs to be extended to support rendering an item and its sub-tree into a texture that persists, so that it can be used for the initial shadow generation & rendering and later for the actual item rendering.
The mirroring of the original for could be done like this:
The original for is a Repeater, which holds a Rc<RefCell<RepeaterInner<C>>>. Its vector of components, etc. are kept up-to-date with the original model. We could implement the Model trait for RepeaterInner (aka ShadowModel 🕶️) and use that as input for the for that's dedicated to creating the Shadow elements. The model can provide the individual fields needed or perhaps simply an ItemRef (or equivalent).
In pseudo code this is how the lowering transformation could look like:
repeater := for foo in model: SomeElement {
..-shadow-..: something;
MoreStuff { }
}
// lower to:
for item_ref in ShadowModel(repeater): Shadow {
x: item_ref.x;
y: item_ref.y;
render() {
apply_shadow_to(item_ref.texture)
}
}
repeater := for foo in model: SomeElement {
// without the shadow
MoreStuff { }
}
Another possibility we discussed is to lower the above code to:
for [idx] in model: Shadow {
x: repeater[idx].x;
y: repeater[idx].y;
...
}
repeater := for foo in model: SomeElement {
// without the shadow
MoreStuff { }
}
Which is basically the same