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

Select rows by dragging #128

Open
jpovixwm opened this issue Dec 26, 2023 · 15 comments
Open

Select rows by dragging #128

jpovixwm opened this issue Dec 26, 2023 · 15 comments

Comments

@jpovixwm
Copy link
Contributor

This feature comes in handy when you need to perform an action on multiple rows but don't have a keyboard around.
Using only the mouse, I don't think it's currently possible to select more than a single row, because that requires the use of the Ctrl or Shift keys.
Drag to select is present in transmission-qt and qBittorrent.

I am going to experiment a bit with how this could potentially be implemented, but I can't provide any time estimates, so it's still up for grabs for anyone else willing to give it a go.

@qu1ck
Copy link
Member

qu1ck commented Dec 27, 2023

This would likely conflict with scrolling gesture on touchscreens. If there is a way to implement it without breaking scroll then I'm not opposed to it.

jpovixwm added a commit to jpovixwm/TrguiNG that referenced this issue Dec 27, 2023
@jpovixwm
Copy link
Contributor Author

It looks like we can check event.target.hasPointerCapture() inside of a pointerdown event handler in order to disable this feature on touchscreens. I've pushed a rudimentary POC branch to my fork where I use this approach.
I've tested it on Windows in Tauri, Brave and Firefox using a mouse - drag-to-select works, and on Android in Brave and Firefox:

  • using the touchscreen, the feature is disabled and swiping up/down simply scrolls the table.
  • using a USB mouse, the feature works same as on Windows and doesn't interfere with scrolling because in this case there's no drag-to-pan behavior

In principle, this works due to "Implicit pointer capture", as described in the Pointer Events spec: https://w3c.github.io/pointerevents/#dfn-implicit-pointer-capture

Can you check if it behaves correctly on your touch enabled devices?

Other than that, there's also the question of how to handle row reordering during drag-to-select. This could be tricky if the table is sorted by highly dynamic columns, such as UL/DL speeds.
If a row gets selected during drag-to-select, but is then reordered somewhere further away from the implicit "selection rectangle" defined by initial and current pointer positions, should the row stay selected, or should we only select rows contained within this rectangle? I think the latter makes more sense, even if the rectangle is never drawn.

Or should the rectangle be drawn, as a form of feedback to the user? Transmission-qt and qBittorrent don't draw the rectangle.

@qu1ck
Copy link
Member

qu1ck commented Dec 28, 2023

I don't have any non android touch devices so my testing will not expand the coverage. I'm mostly curious about windows touch laptops and the like.

If a row gets selected during drag-to-select, but is then reordered somewhere further away from the implicit "selection rectangle" defined by initial and current pointer positions, should the row stay selected, or should we only select rows contained within this rectangle? I think the latter makes more sense, even if the rectangle is never drawn.

Yes, latter makes sense, it would be consistent with shift clicks.

Drawing a rectangle is relevant for selecting objects on 2d plane, we only have one dimension here so highlighted rows give enough feedback.

@qu1ck
Copy link
Member

qu1ck commented Dec 29, 2023

I think we can make it an opt-in feature with a setting.

@jpovixwm
Copy link
Contributor Author

I guess that could work as a last resort, though I'm not a big fan of this solution, because when using a mouse, this feature will always be useful (can't think of a scenario where it isn't) and when using a touchscreen it will likely never be useful (unless we come up with some kludge solution involving long-tapping to initiate the drag-to-select behavior or whatever, which would then conflict with the context menu which is also triggered by a long tap).
In this case I think it would be better to just check event.pointerType and only enable the behavior for events explicitly generated by a mouse.
I initially went with event.target.hasPointerCapture() because I think it provides a more universal way of telling whether the user can or cannot scroll the list by moving the pointer after pointerdown is fired. (Which now makes me wonder how does it behave if the list doesn't have enough entries for a scrollbar to appear, is the implicit pointer capture still carried out then?)

Anyway, I'm currently a bit stuck trying to find a sane approach to scrolling the list automatically when the pointer device comes near the edges of the container.
This is implemented internally in react-beautiful-dnd, so I think the implementation for drag-to-select should match that to make the UX consistent. But their implementation is far from simple: https://github.com/atlassian/react-beautiful-dnd/tree/v13.1.1/src/state/auto-scroller and I'd really prefer to avoid having to re-implement that just for a seemingly basic feature of auto-scrolling a container.

@qu1ck
Copy link
Member

qu1ck commented Dec 30, 2023

Drag to select is not really something I've seen used in UI for lists. I've seen it in tables where cells are selectable, but those are 2D, it makes sense there.
It feels weird to me but I accept that others may be used to it.

Scrolling behavior would likely be a pain to implement. In fact it's likely generally impossible to implement ergonomically with scrolling speed varying by how much you extend the pointer out of the container. If you get past the window border you won't get any events at all.

I'm not sure it's worth putting a lot of effort into auto scrolling.

@jpovixwm
Copy link
Contributor Author

I revisited this problem today, and it occurred to me that keyboard-less multi selection could be facilitated via a dedicated column:

The way I see it, it would work like this:

  • Clicking or tapping the circle would select/deselect the row
  • "Long clicking" or tapping and holding for ~500 ms would do range selection (based on the most recently touched row, similar to how shift+click works) (inspired by X-plore File Manager)
  • Optionally, the column's header could also be a clickable circle to support selecting/deselecting all rows

In general, this should be far easier to implement than drag-to-select and would likely work better on mobile devices/touch screens.
Before I put any serious effort into it, I'd like to ask for your opinion on this approach.

@qu1ck
Copy link
Member

qu1ck commented Mar 21, 2024

This looks like a better idea, assuming it can be made intuitive. Also I'm not sold on checkmark look maybe just a filled blue circle like a radio button.

Clicking or tapping the circle would select/deselect the row

Like ctrl-click, right? I.e. not affecting selected status of other rows.

"Long clicking" or tapping and holding for ~500 ms would do range selection

For this to be intuitive it needs visual feedback, for example the circle gradually filling while you are holding mouse button. That will give user the hint that it's different from a simple click.

Optionally, the column's header could also be a clickable circle

I like this too (although needs a bit more work to have custom header cell rendering).

@jpovixwm
Copy link
Contributor Author

Like ctrl-click, right? I.e. not affecting selected status of other rows.

Yes, like Ctrl-click.

I'll play with it when I get the time to see if I can come up with something that looks and feels good. I wonder if it would be better to have the entire cell's rectangular area act as the selection button, though, so that you don't have to precisely aim for the smallish circle. With a mouse it's not that bad, but with a touch screen it could be annoying if you missed the circle and your meticulously crafted multi-selection got replaced with the tapped row in an instant 😂

@qu1ck
Copy link
Member

qu1ck commented Mar 21, 2024

You will be limited by row height anyway, it should be at least 4-5em for comfortable touch area but that is too sparse for desktop UI.
But for the hypothetical future mobile mode of the table where the torrrents table is replaced with a list of thick rows with most useful fields packed into multi line element, it would work better.

@jpovixwm
Copy link
Contributor Author

I ended up with this look and feel for now:

What do you think?

@qu1ck
Copy link
Member

qu1ck commented Mar 23, 2024

Looks good.

@jpovixwm
Copy link
Contributor Author

Unfortunately, due to a bug in Mantine's Transition component, this will need to wait until TrguiNG is updated to Mantine 7.

@qu1ck
Copy link
Member

qu1ck commented Mar 23, 2024

Mantine 7 changed the styling system and has other breaking changes. I'm not sure when I'll get around to updating to v7.

jpovixwm added a commit to jpovixwm/TrguiNG that referenced this issue Mar 23, 2024
- allows for multi-selection without using a keyboard
- covers the use case of Issue openscopeproject#128
@jpovixwm
Copy link
Contributor Author

jpovixwm commented Mar 23, 2024

I managed to find a workaround for that bug, seems to work fine in Win 10 Edge webview (Tauri) - other browsers not tested. I've pushed a branch in my fork, feel free to take a look and provide feedback.
The implementation is still not complete though:

  • The column's accessor function needs to be implemented so that sorting by selection state works
  • It doesn't look good with the light theme
  • Column header component is not implemented as of now, didn't look into it. Should probably be an empty circle if nothing is selected, a circle with a dash if some rows are selected, and a filled circle if all rows are selected

There's also a small issue I've found with the multi-selection behavior, but it may be out of scope here because it can also be reproduced with the existing keyboard-based multi-select:

  1. Start up TrguiNG fresh, so you don't have any existing selection.
  2. Ctrl+shift+click some row.
  3. Ctrl+shift+click another row (at least one row apart from the first one)
  4. -> The rows in-between are not selected, but I think they should be selected because shift+ctrl+click is supposed to select all rows between the clicked row and the most recently selected row.

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

No branches or pull requests

2 participants