PistonDevelopers / conrod

An easy-to-use, 2D GUI library written entirely in Rust.
Other
3.35k stars 296 forks source link

Generalized scroll/view #956

Open suhr opened 7 years ago

suhr commented 7 years ago

Like in SFML.

I found conrod::widget::scroll, but it is not quite clear how to use it.

PS: it would be nice to have it scaleable, so when the view becomes smaller, displayed widgets become larger/more spaced.

suhr commented 7 years ago

Ping. If conrod doesn't have this, tell at least how should I do scrolling for now.

mitchmindtree commented 7 years ago

Calling Widget::scroll_kids, Widget::scroll_kids_vertically or Widget::scroll_kids_horizontally on a widget will make that widget into a scrollable view for all children of that widget. E.g.

widget::Canvas::new().scroll_kids_vertically().set(CANVAS, ui);
widget::Button::new().parent(CANVAS).set(BUTTON, ui);
// other child widgets

This would create a Canvas which acts as a scrollable container for the Button and other child widgets.

You can also instantiate a Scrollbar widget for manually scrolling widgets that have scrolling enabled:

widget::Scrollbar::y_axis(CANVAS).autohide(true).set(SCROLLBAR, ui);

The canvas.rs example demonstrates these parts of scrolling.

I'm still not sure if I'm happy with the current design for scrolling, as allowing any widget to opt-in to scrolling can seem quite ambiguous. I've been wondering if we should instead create a ScrollArea/ScrollView widget which returns a widget::Id for a surface upon which child widgets can be placed and scrolled. Something like this might allow scrolling to be moved out of conrod's core and into the widget space, and would also make the widget::CommonBuilder state a little lighter.

Anyway, hope this helps!

PS: it would be nice to have it scaleable, so when the view becomes smaller, displayed widgets become larger/more spaced.

This has crossed my mind too and would certainly be nice. I don't have any plans to implement this myself, but would be happy to see it implemented and will keep it in mind.

suhr commented 7 years ago

To be honest, I don't quite understand how exactly Scrollbar scrolls Canvas.

I want to scroll my piano roll by dragging it, not by using scrollbars.

mitchmindtree commented 7 years ago

@suhr when setting a Scrollbar, a widget::Id of a scrollable widget must be given. The scrollbar is then able to scroll that widget by calling the Ui::scroll_widget method.

If you'd prefer to scroll your timeline by dragging it, you could check for left mouse button drag events on your timeline and scroll the scrollable parent widget accordingly via the Ui::scroll_widget method.

Does this help?

suhr commented 7 years ago

Does this help?

Will try it out.

suhr commented 7 years ago

Well, for some reason this doesn't seem to work:

fn update(mut self, args: widget::UpdateArgs<Self>) -> Self::Event {
    let widget::UpdateArgs { id, state, rect, mut ui, style, .. } = args;

    widget::Canvas::new()
        .color(color::rgb_bytes(0x26, 0x32, 0x38))
        .scroll_kids()
        .set(state.ids.canvas, ui);

    let (y0, dy) = (rect.y.start, (rect.y.end - rect.y.start) / 93.0);
    let (x0, x1) = (rect.x.start, rect.x.end);

    if state.ids.lines.len() < 248 {
        state.update(|state| state.ids.lines.resize(248, &mut ui.widget_id_generator()));
    }

    for (i, &line_id) in state.ids.lines.iter().enumerate() {
        let y = y0 + (i as f64) * dy;
        widget::Line::new([x0, y], [x1, y])
            .solid()
            .thickness(1.0)
            .color(color::rgb_bytes(0x54, 0x6E, 0x7A))
            .middle_of(state.ids.canvas)
            .graphics_for(id)
            .set(line_id, ui);
    }

    let mut scroll_dxy = [0.0, 0.0];
    for ev in ui.widget_input(id).events() {
        use conrod::{event, input};

        match ev {
            event::Widget::Drag(drag) if drag.button == input::MouseButton::Left => {
                scroll_dxy[0] += drag.delta_xy[0];
                scroll_dxy[1] += drag.delta_xy[1];
            },
            _ => (),
        }

    }
    if scroll_dxy != [0.0, 0.0] {
        self.scroll[0] += scroll_dxy[0];
        self.scroll[1] += scroll_dxy[1];
        ui.scroll_widget(state.ids.canvas, self.scroll)
    }
}

Even though ui.scroll_widget is called, lines doesn't move on drag.