SecondHalfGames / yakui

yakui is a declarative Rust UI library for games
Apache License 2.0
222 stars 18 forks source link

Clipping issues with layer+reflow+scrolling dropdown #149

Open msparkles opened 2 months ago

msparkles commented 2 months ago

image

The expected strategy of a layer+reflow+scrolling container should work. If not, there's a bug in one of those components that we gotta look at

msparkles commented 2 months ago

image

msparkles commented 2 months ago

reprod:


use yakui::widgets::{Layer, Scrollable};
use yakui::{button, column, reflow, use_state, widgets::Pad, Alignment, Dim2};
use yakui::{constrained, row, Constraints, Vec2};
use yakui_core::Pivot;

pub fn run() {
    let open = use_state(|| false);
    let options = ["Hello", "World", "Foobar", "Foobar", "Foobar", "Foobar"];
    let selected = use_state(|| 0);

    row(|| {
        button("bbbbbbbbbbbbb");

        constrained(Constraints::loose(Vec2::new(f32::INFINITY, 100.0)), || {
            Scrollable::vertical().show(|| {
                column(|| {
                    if button(options[selected.get()]).clicked {
                        open.modify(|x| !x);
                    }

                    if open.get() {
                        Pad::ZERO.show(|| {
                            Layer::new().show(|| {
                                reflow(Alignment::BOTTOM_LEFT, Pivot::TOP_LEFT, Dim2::ZERO, || {
                                    constrained(
                                        Constraints::loose(Vec2::new(f32::INFINITY, 80.0)),
                                        || {
                                            Scrollable::vertical().show(|| {
                                                column(|| {
                                                    let current = selected.get();
                                                    for (i, option) in options.iter().enumerate() {
                                                        if i != current {
                                                            if button(*option).clicked {
                                                                selected.set(i);
                                                                open.set(false);
                                                            }
                                                        }
                                                    }
                                                });
                                            });
                                        },
                                    );
                                });
                            });
                        });
                    }
                });
            });
        });
    });
}

fn main() {
    bootstrap::start(run as fn());
}
msparkles commented 2 months ago

simpler version:


pub fn run() {
    let open = use_state(|| false);
    let options = ["Hello", "World", "Foobar", "Foobar", "Foobar", "Foobar"];
    let selected = use_state(|| 0);

    row(|| {
        button("abcde");

        Scrollable::vertical().show(|| {
            reflow(Alignment::BOTTOM_LEFT, Pivot::TOP_LEFT, Dim2::ZERO, || {
                constrained(Constraints::loose(Vec2::new(f32::INFINITY, 80.0)), || {
                    Scrollable::vertical().show(|| {
                        column(|| {
                            let current = selected.get();
                            for (i, option) in options.iter().enumerate() {
                                if i != current {
                                    if button(*option).clicked {
                                        selected.set(i);
                                        open.set(false);
                                    }
                                }
                            }
                        });
                    });
                });
            });
        });
    });
}

fn main() {
    bootstrap::start(run as fn());
}
msparkles commented 2 months ago

We figured out the issue. The current clip rect getting clipped by the previous clip produces bogus result.

Previous: Rect { pos: Vec2(0.0, 0.0), size: Vec2(125.0, 50.0) } Unscaled: Rect { pos: Vec2(0.0, 50.0), size: Vec2(84.0, 0.0) }

Because the clip rects inherently don't intersect correctly (it being on a new layer, reflowed, etc...) this breaks the clipping

msparkles commented 2 months ago

@LPGhatguy We're not sure how to fix this, do you have any ideas?

msparkles commented 2 months ago

We've successfully implemented a fix for this, after the discussions on rust gamedev discord! please look at our fork for if it's a suitable fix, then we'll extract it into a separate PR