BListView: More auto-scroll fixes from hrev57439

Only auto-scroll if button clicked inside view, not if clicked outside
and then dragged in. Save MouseDown() button state to compare against
in MouseMoved().

Don't alter selection on first click of selected item on multi-select
lists. This eats a click on multi-select lists when you click on a
selected item with no modifiers held down to allow for drag and drop.
It will update the selection on the second click (and there-after).

https://www.haiku-os.org/legacy-docs/bebook/BListView.html InitiateDrag()

"... derived classes typically permit users to drag items only if
they're already selected (if wasSelected is true). In other words, it
takes *two* mouse-down events to drag an item—one to select it and one
to begin dragging it." (emphasis mine)

Technically this should only happen on draggable multi-select lists, but
it doesn't hurt (much) on non-draggable multi-select lists and I can't
easily tell if a list view is draggable or not until InitiateDrag() is
called. By then it's too late to eat the click in MouseDown().

BeOS R5 solved this problem by selecting on mouse up insted of down but
we don't want to do this so we'll just have to accept that the first
click in this case doesn't count.

Simplify invalid and disabled item logic. If clicked on invalid
(somehow) don't alter selection, if clicked on disabled deselect all.

Change-Id: I6bf40de85da442ee7acd86ab6d91ff0cac7ab106
Reviewed-on: https://review.haiku-os.org/c/haiku/+/7279
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
John Scipione 2023-12-29 17:21:48 -05:00 committed by waddlesplash
parent ec595170fb
commit 879e5f5a80

View File

@ -30,6 +30,8 @@
struct track_data {
BPoint drag_start;
int32 item_index;
int32 buttons;
uint32 selected_click_count;
bool was_selected;
bool try_drag;
bool is_dragging;
@ -625,6 +627,13 @@ BListView::MouseDown(BPoint where)
Window()->UpdateIfNeeded();
}
int32 buttons = 0;
if (Window() != NULL) {
BMessage* currentMessage = Window()->CurrentMessage();
if (currentMessage != NULL)
currentMessage->FindInt32("buttons", &buttons);
}
int32 index = IndexOf(where);
// If the user double (or more) clicked within the current selection,
@ -663,6 +672,13 @@ BListView::MouseDown(BPoint where)
SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
}
// increment/reset selected click count
fTrack->buttons = buttons;
if (fTrack->buttons > 0 && fTrack->was_selected)
fTrack->selected_click_count++;
else
fTrack->selected_click_count = 0;
_DoSelection(index);
BView::MouseDown(where);
@ -673,6 +689,7 @@ void
BListView::MouseUp(BPoint where)
{
// drag is over
fTrack->buttons = 0;
fTrack->try_drag = false;
fTrack->is_dragging = false;
@ -719,14 +736,6 @@ BListView::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
}
}
// get mouse buttons from current message in case of change
int32 buttons = 0;
if (Window() != NULL) {
BMessage* currentMessage = Window()->CurrentMessage();
if (currentMessage != NULL)
currentMessage->FindInt32("buttons", &buttons);
}
int32 index = IndexOf(where);
if (index == -1) {
// If where is above top, scroll to the first item,
@ -739,7 +748,7 @@ BListView::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
// don't scroll if button not pressed or index is invalid
int32 lastIndex = fFirstSelected;
if (buttons == 0 || index == -1)
if (fTrack->buttons == 0 || index == -1)
return BView::MouseMoved(where, code, dragMessage);
// don't scroll if mouse is left or right of the view
@ -1598,6 +1607,8 @@ BListView::_InitObject(list_view_type type)
fTrack = new track_data;
fTrack->drag_start = B_ORIGIN;
fTrack->item_index = -1;
fTrack->buttons = 0;
fTrack->selected_click_count = 0;
fTrack->was_selected = false;
fTrack->try_drag = false;
fTrack->is_dragging = false;
@ -2109,40 +2120,43 @@ void
BListView::_DoSelection(int32 index)
{
BListItem* item = ItemAt(index);
if (index >= 0 && item != NULL) {
if (fListType == B_MULTIPLE_SELECTION_LIST) {
// multiple-selection list
if ((modifiers() & B_SHIFT_KEY) != 0) {
// extend or contract selection
if (index >= fFirstSelected && index < fLastSelected) {
// clicked inside of selected items block, deselect all
// except from the first selected index to item index
DeselectExcept(fFirstSelected, index);
} else {
// extend selection up or down
Select(std::min(index, fFirstSelected),
std::max(index, fLastSelected));
}
// don't alter selection if invalid item clicked
if (index < 0 || item == NULL)
return;
// deselect all if clicked on disabled
if (!item->IsEnabled())
return DeselectAll();
if (fListType == B_MULTIPLE_SELECTION_LIST) {
// multiple-selection list
if ((modifiers() & B_SHIFT_KEY) != 0) {
if (index >= fFirstSelected && index < fLastSelected) {
// clicked inside of selected items block, deselect all
// except from the first selected index to item index
DeselectExcept(fFirstSelected, index);
} else {
if ((modifiers() & B_COMMAND_KEY) != 0) {
// toggle selection state (like in Tracker)
if (item->IsSelected())
Deselect(index);
else
Select(index, true);
} else if (item->IsEnabled()) // only select enabled item
Select(index);
// extend or contract selection
Select(std::min(index, fFirstSelected),
std::max(index, fLastSelected));
}
} else {
// single-selection list
} else if ((modifiers() & B_COMMAND_KEY) != 0) {
// toggle selection state
if ((modifiers() & B_COMMAND_KEY) != 0 && item->IsSelected())
if (item->IsSelected())
Deselect(index);
else if (item->IsEnabled()) // only select enabled item
Select(index);
}
} else if ((modifiers() & B_COMMAND_KEY) == 0)
DeselectAll();
else
Select(index, true);
} else if (fTrack->selected_click_count != 1)
Select(index); // eat a click on selected for drag and drop
} else {
// single-selection list
// toggle selection state
if ((modifiers() & B_COMMAND_KEY) != 0 && item->IsSelected())
Deselect(index);
else
Select(index);
}
}