Implement shift+click/A-B message selection

This commit is contained in:
miruka 2020-03-26 23:24:37 -04:00
parent e696c16fc8
commit ba86414ddf
7 changed files with 80 additions and 5 deletions

View File

@ -2,7 +2,6 @@
- Long-press-drag to select multiple messages on touch - Long-press-drag to select multiple messages on touch
- Drag to select multiple messages on non-touch - Drag to select multiple messages on non-touch
- Shift+click to select everything in between
- Drag-scrolling in room pane a tiny bit activates the delegates - Drag-scrolling in room pane a tiny bit activates the delegates

View File

@ -64,22 +64,35 @@ ListView {
} }
} }
onSelectedCountChanged: if (! selectedCount) lastCheckedDelegateIndex = 0
property bool allowDragging: true property bool allowDragging: true
property alias cursorShape: mouseArea.cursorShape property alias cursorShape: mouseArea.cursorShape
property int currentItemHeight: currentItem ? currentItem.height : 0 property int currentItemHeight: currentItem ? currentItem.height : 0
property var checkedDelegates: ({}) property var checkedDelegates: ({})
property int lastCheckedDelegateIndex: 0
property int selectedCount: Object.keys(checkedDelegates).length property int selectedCount: Object.keys(checkedDelegates).length
function delegatesChecked(...indices) { function delegatesChecked(...indices) {
print( indices)
for (const i of indices) { for (const i of indices) {
const model = listView.model.get(i) const model = listView.model.get(i)
checkedDelegates[model.id] = model checkedDelegates[model.id] = model
} }
lastCheckedDelegateIndex = indices.slice(-1)[0]
checkedDelegatesChanged() checkedDelegatesChanged()
} }
function delegatesFromLastToHereChecked(here) {
const indices = utils.range(lastCheckedDelegateIndex, here)
eventList.delegatesChecked(...indices)
}
function delegatesUnchecked(...indices) { function delegatesUnchecked(...indices) {
for (const i of indices) { for (const i of indices) {
const model = listView.model.get(i) const model = listView.model.get(i)

View File

@ -183,6 +183,7 @@ HRowLayout {
PointHandler { PointHandler {
id: mousePointHandler id: mousePointHandler
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
acceptedModifiers: Qt.NoModifier
acceptedPointerTypes: acceptedPointerTypes:
PointerDevice.GenericPointer | PointerDevice.Eraser PointerDevice.GenericPointer | PointerDevice.Eraser
@ -206,6 +207,24 @@ HRowLayout {
property bool checkedNow: false property bool checkedNow: false
} }
PointHandler {
id: mouseShiftPointHandler
acceptedButtons: Qt.LeftButton
acceptedModifiers: Qt.ShiftModifier
acceptedPointerTypes:
PointerDevice.GenericPointer | PointerDevice.Eraser
onActiveChanged: {
if (active &&
! eventDelegate.checked &&
(! parent.hoveredLink ||
! parent.enableLinkActivation)) {
eventList.delegatesFromLastToHereChecked(model.index)
}
}
}
TapHandler { TapHandler {
id: touchTapHandler id: touchTapHandler
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
@ -234,7 +253,8 @@ HRowLayout {
z: -100 z: -100
color: eventDelegate.checked && color: eventDelegate.checked &&
! contentLabel.selectedText && ! contentLabel.selectedText &&
! mousePointHandler.active ? ! mousePointHandler.active &&
! mouseShiftPointHandler.active ?
theme.chat.message.checkedBackground : theme.chat.message.checkedBackground :
isOwn? isOwn?

View File

@ -57,8 +57,6 @@ HColumnLayout {
combine ? theme.spacing / (compact ? 4 : 2) : combine ? theme.spacing / (compact ? 4 : 2) :
theme.spacing * (compact ? 1 : 2) theme.spacing * (compact ? 1 : 2)
readonly property alias leftTapHandler: leftTapHandler
// Needed because of eventList's MouseArea which steals the // Needed because of eventList's MouseArea which steals the
// HSelectableLabel's MouseArea hover events // HSelectableLabel's MouseArea hover events
onCursorShapeChanged: eventList.cursorShape = cursorShape onCursorShapeChanged: eventList.cursorShape = cursorShape
@ -114,11 +112,17 @@ HColumnLayout {
} }
TapHandler { TapHandler {
id: leftTapHandler
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
acceptedModifiers: Qt.NoModifier
onTapped: toggleChecked() onTapped: toggleChecked()
} }
TapHandler {
acceptedButtons: Qt.LeftButton
acceptedModifiers: Qt.ShiftModifier
onTapped: eventList.delegatesFromLastToHereChecked(model.index)
}
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen
@ -151,6 +155,13 @@ HColumnLayout {
onTriggered: eventList.checkedDelegates = {} onTriggered: eventList.checkedDelegates = {}
} }
HMenuItem {
visible: model.index !== 0
icon.name: "select-until-here"
text: qsTr("Select until here")
onTriggered: eventList.delegatesFromLastToHereChecked(model.index)
}
HMenuItem { HMenuItem {
id: copyMedia id: copyMedia
icon.name: "copy-link" icon.name: "copy-link"

View File

@ -83,6 +83,7 @@ HMxcImage {
TapHandler { TapHandler {
acceptedModifiers: Qt.NoModifier
onTapped: onTapped:
eventList.selectedCount ? eventList.selectedCount ?
eventDelegate.toggleChecked() : getOpenUrl(Qt.openUrlExternally) eventDelegate.toggleChecked() : getOpenUrl(Qt.openUrlExternally)
@ -90,6 +91,14 @@ HMxcImage {
gesturePolicy: TapHandler.ReleaseWithinBounds gesturePolicy: TapHandler.ReleaseWithinBounds
} }
TapHandler {
acceptedModifiers: Qt.ShiftModifier
onTapped:
eventList.delegatesFromLastToHereChecked(singleMediaInfo.index)
gesturePolicy: TapHandler.ReleaseWithinBounds
}
HoverHandler { HoverHandler {
id: hover id: hover
onHoveredChanged: { onHoveredChanged: {

View File

@ -83,6 +83,26 @@ QtObject {
} }
function range(startOrEnd, end=null, ) {
// range(3) [0, 1, 2, 3]
// range(3, 6) [3, 4, 5, 6]
// range(3, -1) [3, 2, 1, 0, -1]
const numbers = []
let realStart = end ? startOrEnd : 0
let realEnd = end ? end : startOrEnd
if (realEnd < realStart)
for (let i = realStart; i >= realEnd; i--)
numbers.push(i)
else
for (let i = realStart; i <= realEnd; i++)
numbers.push(i)
return numbers
}
function isEmptyObject(obj) { function isEmptyObject(obj) {
return Object.entries(obj).length === 0 && obj.constructor === Object return Object.entries(obj).length === 0 && obj.constructor === Object
} }

View File

@ -0,0 +1,3 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="m11 24v-2h-4v2zm8-22h3v3h2v-5h-5zm-19 15h2v-4h-2zm0-6h2v-4h-2zm2-6v-3h3v-2h-5v5zm22 2h-2v4h2zm0 6h-2v4h2zm-2 6v3h-3v2h5v-5zm-17 3h-3v-3h-2v5h5zm12 2v-2h-4v2zm-6-22v-2h-4v2zm6 0v-2h-4v2zm0 11h-4v4h-2v-4h-4v-2h4v-4h2v4h4z"/>
</svg>

After

Width:  |  Height:  |  Size: 327 B