Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Add autoscroll option to scroll widget or create listbox and listrow widgets to implement with scroll widget #1154

Open
hypernova7 opened this issue Aug 13, 2024 · 1 comment
Labels
enhancement New feature or request

Comments

@hypernova7
Copy link
Contributor

hypernova7 commented Aug 13, 2024

Description of the requested feature

Currently the scroll widget works pretty well but only allows for manual scrolling to find the selected item. Investigating how to make ScrolledWindow scroll automatically to the selected item, I found that it can only be done using the ListBox and ListBoxRow widgets. And the idea is to add two more widgets to achieve this or if it is possible to add this feature to the scroll widget.

Example:

2024-08-12_18-33-57@1920x1080.mp4

Proposed configuration syntax

Currently the for widget can only be used inside a box widget, but it would be a good idea to change this to be able to use other types of widgets like listbox. This way

(scroll
  :hscroll false
  :vscroll true
  :height 120
  :width 350
  (listbox
    :position 15
    (for song in playlist
      (listrow
        (button
          :class "${song.id == current_song_id ? 'active' : ''}"
          :onclick "mpc play ${song.id}"
          (label
            :text "${song.name}"
          )
        )
      )
    )
  )
)

The listbox position option is to know the current position of the list and go to the current item, which internally would be handled with the listbox row_at_index(position) method

Additional context

In order to create the example in the video I had to create the listbox and listrow widgets.

const WIDGET_NAME_LISTBOX: &str = "listbox";
fn build_gtk_listbox(bargs: &mut BuilderArgs) -> Result<gtk::ListBox> {
    let gtk_widget = gtk::ListBox::new();

    let mut children = bargs.widget_use.children.iter().map(|child| {
        build_gtk_widget(
            bargs.scope_graph,
            bargs.widget_defs.clone(),
            bargs.calling_scope,
            child.clone(),
            bargs.custom_widget_invocation.clone(),
        )
    });

    let child = children.next().unwrap()?;
    gtk_widget.add(&child);

    let last_total_rows = 0;

    def_widget!(bargs, _g, gtk_widget, {
        prop(position: as_i32) {
            // II had to do this so I could get all the listrows inside the box widget,
            // since the box widget that contains the for widget is not able to realize that the content has changed or is dynamic, or so I think.
            if let Some(gbox) = child.dynamic_cast_ref::<gtk::Box>() {
                let current_total_rows = gbox.children().len();


                if current_total_rows > last_total_rows {
                    for (i, child) in gbox.children().iter().enumerate() {
                        // Remove all children from the box widget so that they can be added to the listbox widget without causing errors
                        gbox.remove(child);
                        // This is to avoid adding the first child, since it is empty.
                        if i > 0 {
                            gtk_widget.add(child);
                        }
                    }
                }
            }

            // Select ListBoxRow by position
            gtk_widget.select_row(gtk_widget.row_at_index(position).as_ref());

            // Autocroll handling
            if let Some(row) = gtk_widget.selected_row() {
                match row.translate_coordinates(&gtk_widget, 0, 0) {
                    Some((x, y)) => match gtk_widget.adjustment() {
                        Some(adjustment) => {
                            let page_size = adjustment.page_size();
                            let (_, height) = row.preferred_height();
                            let (_, width) = row.preferred_width();

                            // For horizontal scrolling
                            if x >= 0 {
                                adjustment.set_value(x as f64 - (page_size - width as f64) / 2.0);
                            }

                            // For vertical scrolling
                            if y >= 0 {
                                adjustment.set_value(y as f64 - (page_size - height as f64) / 2.0);
                            }
                        },
                        None => {}
                    }
                    None => {}
                }
            }
        }
    });

    Ok(gtk_widget)
}

const WIDGET_NAME_LISTROW: &str = "listrow";
fn build_gtk_listrow(bargs: &mut BuilderArgs) -> Result<gtk::ListBoxRow> {
    let gtk_widget = gtk::ListBoxRow::new();

    def_widget!(bargs, _g, gtk_widget, {
        prop(activatable: as_bool = true, selectable: as_bool = true) {
            gtk_widget.set_activatable(activatable);
            gtk_widget.set_selectable(selectable);
        }
    });

    Ok(gtk_widget)
}

Usage example:

(scroll
  :hscroll false
  :vscroll true
  :height 120
  :width 350
  (listbox
    :position 15
    (box
      :orientation "v"
      (for song in playlist
        (listrow
          (button
            :class "${song.id == current_song_id ? 'active' : ''}"
            :onclick "mpc play"
            (label
              :text "${song.name}"
            )
          )
        )
      )
    )
  )
)
@hypernova7 hypernova7 added the enhancement New feature or request label Aug 13, 2024
@hypernova7
Copy link
Contributor Author

Does this project have discord?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant