From cbd653013b3cdd300a0cd38f2d6e59cc771375d4 Mon Sep 17 00:00:00 2001 From: cutefishd Date: Mon, 5 Apr 2021 15:21:39 +0800 Subject: [PATCH] GridView supports up,down,left,right keys to move --- main.cpp | 2 + model/foldermodel.cpp | 18 ++--- qml/FolderGridItem.qml | 2 +- qml/FolderGridView.qml | 154 ++++++++++++++++++++++++++++++++++++----- qml/FolderListItem.qml | 16 +++++ qml/FolderListView.qml | 40 +++++------ qml/FolderPage.qml | 51 +++++++------- 7 files changed, 210 insertions(+), 73 deletions(-) diff --git a/main.cpp b/main.cpp index e5a1290..3989797 100644 --- a/main.cpp +++ b/main.cpp @@ -28,6 +28,7 @@ #include "model/placesmodel.h" #include "model/foldermodel.h" #include "model/pathbarmodel.h" +#include "model/positioner.h" #include "widgets/rubberband.h" #include "widgets/itemviewadapter.h" #include "desktop/desktopsettings.h" @@ -58,6 +59,7 @@ int main(int argc, char *argv[]) qmlRegisterType(uri, 1, 0, "PlacesModel"); qmlRegisterType(uri, 1, 0, "FolderModel"); qmlRegisterType(uri, 1, 0, "PathBarModel"); + qmlRegisterType(uri, 1, 0, "Positioner"); qmlRegisterType(uri, 1, 0, "RubberBand"); qmlRegisterType(uri, 1, 0, "ItemViewAdapter"); qmlRegisterType(uri, 1, 0, "DesktopSettings"); diff --git a/model/foldermodel.cpp b/model/foldermodel.cpp index 1b63781..6d7140f 100644 --- a/model/foldermodel.cpp +++ b/model/foldermodel.cpp @@ -144,15 +144,17 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const case IconNameRole: return item.iconName(); case ThumbnailRole: { - // Svg Image - if (item.mimetype() == "image/svg" || - item.mimetype() == "image/svg+xml") { - return item.url(); - } + if (item.isLocalFile()) { + // Svg Image + if (item.mimetype() == "image/svg" || + item.mimetype() == "image/svg+xml") { + return item.url(); + } - // Support - if (isSupportThumbnails(item.mimetype())) { - return "image://thumbnailer/" + item.url().toString(); + // Support + if (isSupportThumbnails(item.mimetype())) { + return "image://thumbnailer/" + item.url().toString(); + } } return QVariant(); diff --git a/qml/FolderGridItem.qml b/qml/FolderGridItem.qml index fa6967c..84f860a 100644 --- a/qml/FolderGridItem.qml +++ b/qml/FolderGridItem.qml @@ -81,7 +81,7 @@ Item { anchors.centerIn: parent width: Math.min(parent.width, _image.paintedWidth) height: Math.min(parent.height, _image.paintedHeight) - radius: Meui.Theme.smallRadius / 2 + radius: height * 0.1 } } } diff --git a/qml/FolderGridView.qml b/qml/FolderGridView.qml index 82c1541..ef96610 100644 --- a/qml/FolderGridView.qml +++ b/qml/FolderGridView.qml @@ -14,6 +14,9 @@ GridView { property Item hoveredItem: null property Item pressedItem: null + property alias positions: positioner.positions + property alias positioner: positioner + property int verticalDropHitscanOffset: 0 property int pressX: -1 @@ -43,6 +46,50 @@ GridView { signal keyPress(var event) + function effectiveNavDirection(flow, layoutDirection, direction) { + if (direction === Qt.LeftArrow) { + if (flow === GridView.FlowLeftToRight) { + if (layoutDirection === Qt.LeftToRight) { + return Qt.LeftArrow; + } else { + return Qt.RightArrow; + } + } else { + if (layoutDirection === Qt.LeftToRight) { + return Qt.UpArrow; + } else { + return Qt.DownArrow; + } + } + } else if (direction === Qt.RightArrow) { + if (flow === GridView.FlowLeftToRight) { + if (layoutDirection === Qt.LeftToRight) { + return Qt.RightArrow; + } else { + return Qt.LeftArrow; + } + } else { + if (layoutDirection === Qt.LeftToRight) { + return Qt.DownArrow; + } else { + return Qt.UpArrow; + } + } + } else if (direction === Qt.UpArrow) { + if (flow === GridView.FlowLeftToRight) { + return Qt.UpArrow; + } else { + return Qt.LeftArrow; + } + } else if (direction === Qt.DownArrow) { + if (flow === GridView.FlowLeftToRight) { + return Qt.DownArrow; + } else { + return Qt.RightArrow + } + } + } + function rename() { if (control.currentIndex != -1) { var renameAction = control.model.action("rename") @@ -103,10 +150,42 @@ GridView { Keys.onEscapePressed: { if (!editor || !editor.targetItem) { previouslySelectedItemIndex = -1 - folderModel.clearSelection() + dirModel.clearSelection() event.accepted = false } } + Keys.onUpPressed: { + var newIndex = positioner.nearestItem(currentIndex, + effectiveNavDirection(control.flow, control.effectiveLayoutDirection, Qt.UpArrow)) + if (newIndex !== -1) { + currentIndex = newIndex + updateSelection(event.modifiers) + } + } + Keys.onDownPressed: { + var newIndex = positioner.nearestItem(currentIndex, + effectiveNavDirection(control.flow, control.effectiveLayoutDirection, Qt.DownArrow)) + if (newIndex !== -1) { + currentIndex = newIndex + updateSelection(event.modifiers) + } + } + Keys.onLeftPressed: { + var newIndex = positioner.nearestItem(currentIndex, + effectiveNavDirection(control.flow, control.effectiveLayoutDirection, Qt.LeftArrow)) + if (newIndex !== -1) { + currentIndex = newIndex; + updateSelection(event.modifiers) + } + } + Keys.onRightPressed: { + var newIndex = positioner.nearestItem(currentIndex, + effectiveNavDirection(control.flow, control.effectiveLayoutDirection, Qt.RightArrow)) + if (newIndex !== -1) { + currentIndex = newIndex; + updateSelection(event.modifiers) + } + } cellHeight: { var iconHeight = iconSize + (Meui.Units.fontMetrics.height * 2) + Meui.Units.largeSpacing * 2 @@ -150,7 +229,32 @@ GridView { if (cachedRectangleSelection.length) control.currentIndex[0] - folderModel.updateSelection(cachedRectangleSelection, control.ctrlPressed) + dirModel.updateSelection(cachedRectangleSelection, control.ctrlPressed) + } + + Positioner { + id: positioner + enabled: true + folderModel: dirModel + perStripe: Math.floor(((control.flow == GridView.FlowLeftToRight) + ? control.width : control.height) / ((control.flow == GridView.FlowLeftToRight) + ? control.cellWidth : control.cellHeight)); + } + + DropArea { + id: _dropArea + anchors.fill: parent + + onDropped: { + var dropPos = mapToItem(control.contentItem, drop.x, drop.y) + var dropIndex = control.indexAt(drop.x, drop.y) + var dragPos = mapToItem(control.contentItem, control.dragX, control.dragY) + var dragIndex = control.indexAt(dragPos.x, dragPos.y) + + if (control.dragX == -1 || dragIndex !== dropIndex) { + + } + } } MouseArea { @@ -182,38 +286,38 @@ GridView { // Shift 处理, 选择区域 if (control.shiftPressed && control.currentIndex !== -1) { - folderModel.setRangeSelected(control.anchorIndex, hoveredItem.index) + dirModel.setRangeSelected(control.anchorIndex, hoveredItem.index) } else { // Ctrl 处理 - if (!control.ctrlPressed && !folderModel.isSelected(hoveredItem.index)) { - folderModel.clearSelection() + if (!control.ctrlPressed && !dirModel.isSelected(hoveredItem.index)) { + dirModel.clearSelection() } // Item 选择 if (control.ctrlPressed) { - folderModel.toggleSelected(hoveredItem.index) + dirModel.toggleSelected(hoveredItem.index) } else { - folderModel.setSelected(hoveredItem.index) + dirModel.setSelected(hoveredItem.index) } } // 弹出 Item 菜单 if (mouse.buttons & Qt.RightButton) { clearPressState() - folderModel.openContextMenu(null, mouse.modifiers) + dirModel.openContextMenu(null, mouse.modifiers) mouse.accepted = true } } else { // 处理空白区域点击 if (!control.ctrlPressed) { control.currentIndex = -1 - folderModel.clearSelection() + dirModel.clearSelection() } // 弹出文件夹菜单 if (mouse.buttons & Qt.RightButton) { clearPressState() - folderModel.openContextMenu(null, mouse.modifiers) + dirModel.openContextMenu(null, mouse.modifiers) mouse.accepted = true } } @@ -277,11 +381,11 @@ GridView { } if (pressX != -1) { - if (pressedItem != null && folderModel.isSelected(pressedItem.index)) { + if (pressedItem != null && dirModel.isSelected(pressedItem.index)) { control.dragX = mouse.x control.dragY = mouse.y control.verticalDropHitscanOffset = pressedItem.y + (pressedItem.height / 2) - folderModel.dragSelected(mouse.x, mouse.y) + dirModel.dragSelected(mouse.x, mouse.y) control.dragX = -1 control.dragY = -1 clearPressState() @@ -289,7 +393,7 @@ GridView { if (control.editor && control.editor.targetItem) return; - folderModel.pinSelection() + dirModel.pinSelection() control.rubberBand = rubberBandObject.createObject(control.contentItem, {x: cPress.x, y: cPress.y}) control.interactive = false } @@ -300,13 +404,13 @@ GridView { clearPressState() if (mouse.buttons & Qt.RightButton) { - folderModel.openContextMenu(null, mouse.modifiers) + dirModel.openContextMenu(null, mouse.modifiers) } } onDoubleClicked: { if (mouse.button === Qt.LeftButton && control.pressedItem) - folderModel.openSelected() + dirModel.openSelected() } onReleased: pressCanceled() @@ -337,7 +441,7 @@ GridView { control.interactive = true control.cachedRectangleSelection = null - folderModel.unpinSelection() + dirModel.unpinSelection() } clearPressState() @@ -363,7 +467,7 @@ GridView { break } - if (folderModel.isBlank(index)) { + if (dirModel.isBlank(index)) { continue } @@ -440,6 +544,18 @@ GridView { iconSize -= (iconSize * 0.1) } + function updateSelection(modifier) { + if (modifier & Qt.ShiftModifier) { + dirModel.setRangeSelected(anchorIndex, currentIndex) + } else { + dirModel.clearSelection() + dirModel.setSelected(currentIndex) + if (currentIndex == -1) + previouslySelectedItemIndex = -1 + previouslySelectedItemIndex = currentIndex + } + } + Component { id: editorComponent @@ -463,7 +579,7 @@ GridView { y = pos.y - Meui.Units.largeSpacing text = targetItem.labelArea.text targetItem.labelArea.visible = false - _editor.select(0, folderModel.fileExtensionBoundary(targetItem.index)) + _editor.select(0, dirModel.fileExtensionBoundary(targetItem.index)) visible = true control.interactive = false } else { @@ -500,7 +616,7 @@ GridView { function commit() { if (targetItem) { targetItem.labelArea.visible = true - folderModel.rename(targetItem.index, text) + dirModel.rename(targetItem.index, text) control.currentIndex = targetItem.index targetItem = null diff --git a/qml/FolderListItem.qml b/qml/FolderListItem.qml index 9690147..680c1f3 100644 --- a/qml/FolderListItem.qml +++ b/qml/FolderListItem.qml @@ -1,6 +1,7 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 +import QtGraphicalEffects 1.0 import MeuiKit 1.0 as Meui Item { @@ -78,6 +79,21 @@ Item { fillMode: Image.PreserveAspectFit asynchronous: true smooth: false + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Item { + width: _image.width + height: _image.height + + Rectangle { + anchors.centerIn: parent + width: Math.min(parent.width, _image.paintedWidth) + height: Math.min(parent.height, _image.paintedHeight) + radius: height * 0.1 + } + } + } } } diff --git a/qml/FolderListView.qml b/qml/FolderListView.qml index dcbdd53..c28b9b5 100644 --- a/qml/FolderListView.qml +++ b/qml/FolderListView.qml @@ -96,7 +96,7 @@ ListView { Keys.onEscapePressed: { if (!editor || !editor.targetItem) { previouslySelectedItemIndex = -1 - folderModel.clearSelection() + dirModel.clearSelection() event.accepted = false } } @@ -134,7 +134,7 @@ ListView { if (cachedRectangleSelection.length) control.currentIndex[0] - folderModel.updateSelection(cachedRectangleSelection, control.ctrlPressed) + dirModel.updateSelection(cachedRectangleSelection, control.ctrlPressed) } onContentXChanged: { @@ -165,7 +165,7 @@ ListView { onDoubleClicked: { if (mouse.button === Qt.LeftButton && control.pressedItem) - folderModel.openSelected() + dirModel.openSelected() } onPressed: { @@ -192,36 +192,36 @@ ListView { if (!control.ctrlPressed) { control.currentIndex = -1 control.previouslySelectedItemIndex = -1 - folderModel.clearSelection() + dirModel.clearSelection() } if (mouse.buttons & Qt.RightButton) { clearPressState() - folderModel.openContextMenu(null, mouse.modifiers) + dirModel.openContextMenu(null, mouse.modifiers) mouse.accepted = true } } else { pressedItem = hoveredItem if (control.shiftPressed && control.currentIndex !== -1) { - folderModel.setRangeSelected(control.anchorIndex, hoveredItem.index) + dirModel.setRangeSelected(control.anchorIndex, hoveredItem.index) } else { - if (!control.ctrlPressed && !folderModel.isSelected(hoveredItem.index)) { + if (!control.ctrlPressed && !dirModel.isSelected(hoveredItem.index)) { previouslySelectedItemIndex = -1 - folderModel.clearSelection() + dirModel.clearSelection() } if (control.ctrlPressed) - folderModel.toggleSelected(hoveredItem.index) + dirModel.toggleSelected(hoveredItem.index) else - folderModel.setSelected(hoveredItem.index) + dirModel.setSelected(hoveredItem.index) } control.currentIndex = hoveredItem.index if (mouse.buttons & Qt.RightButton) { clearPressState() - folderModel.openContextMenu(null, mouse.modifiers) + dirModel.openContextMenu(null, mouse.modifiers) mouse.accepted = true } } @@ -289,11 +289,11 @@ ListView { // Drag if (pressX != -1) { - if (pressedItem != null && folderModel.isSelected(pressedItem.index)) { + if (pressedItem != null && dirModel.isSelected(pressedItem.index)) { control.dragX = mouse.x control.dragY = mouse.y control.verticalDropHitscanOffset = pressedItem.y + (pressedItem.height / 2) - folderModel.dragSelected(mouse.x, mouse.y) + dirModel.dragSelected(mouse.x, mouse.y) control.dragX = -1 control.dragY = -1 clearPressState() @@ -301,7 +301,7 @@ ListView { if (control.editor && control.editor.targetItem) return; - folderModel.pinSelection() + dirModel.pinSelection() control.rubberBand = rubberBandObject.createObject(control.contentItem, {x: cPress.x, y: cPress.y}) control.interactive = false } @@ -329,7 +329,7 @@ ListView { control.interactive = true control.cachedRectangleSelection = null - folderModel.unpinSelection() + dirModel.unpinSelection() } clearPressState() @@ -354,10 +354,10 @@ ListView { function updateSelection(modifier) { if (modifier & Qt.ShiftModifier) { - folderModel.setRangeSelected(anchorIndex, currentIndex) + dirModel.setRangeSelected(anchorIndex, currentIndex) } else { - folderModel.clearSelection() - folderModel.setSelected(currentIndex) + dirModel.clearSelection() + dirModel.setSelected(currentIndex) if (currentIndex == -1) previouslySelectedItemIndex = -1 previouslySelectedItemIndex = currentIndex @@ -386,7 +386,7 @@ ListView { text = targetItem.labelArea.text targetItem.labelArea.visible = false targetItem.labelArea2.visible = false - _editor.select(0, folderModel.fileExtensionBoundary(targetItem.index)) + _editor.select(0, dirModel.fileExtensionBoundary(targetItem.index)) visible = true control.interactive = false } else { @@ -422,7 +422,7 @@ ListView { if (targetItem) { targetItem.labelArea.visible = true targetItem.labelArea2.visible = true - folderModel.rename(targetItem.index, text) + dirModel.rename(targetItem.index, text) control.currentIndex = targetItem.index targetItem = null diff --git a/qml/FolderPage.qml b/qml/FolderPage.qml index 2510dd8..f631ca3 100644 --- a/qml/FolderPage.qml +++ b/qml/FolderPage.qml @@ -10,7 +10,7 @@ import "./Dialogs" Item { id: folderPage - property alias currentUrl: folderModel.url + property alias currentUrl: dirModel.url property Item currentView: _viewLoader.item property int statusBarHeight: 30 @@ -39,21 +39,22 @@ Item { } FolderModel { - id: folderModel + id: dirModel viewAdapter: viewAdapter + Component.onCompleted: { if (arg) - folderModel.url = arg + dirModel.url = arg else - folderModel.url = folderModel.homePath() + dirModel.url = dirModel.homePath() } } ItemViewAdapter { id: viewAdapter adapterView: _viewLoader.item - adapterModel: folderModel + adapterModel: _viewLoader.item.positioner ? _viewLoader.item.positioner : dirModel adapterIconSize: 40 adapterVisibleArea: Qt.rect(_viewLoader.item.contentX, _viewLoader.item.contentY, _viewLoader.item.contentWidth, _viewLoader.item.contentHeight) @@ -90,7 +91,7 @@ Item { } Component.onCompleted: { - folderModel.requestRename.connect(rename) + dirModel.requestRename.connect(rename) } Component { @@ -109,14 +110,14 @@ Item { Label { Layout.alignment: Qt.AlignLeft - text: folderModel.count == 1 ? qsTr("%1 item").arg(folderModel.count) - : qsTr("%1 items").arg(folderModel.count) + text: dirModel.count == 1 ? qsTr("%1 item").arg(dirModel.count) + : qsTr("%1 items").arg(dirModel.count) } Label { Layout.alignment: Qt.AlignLeft - text: qsTr("%1 selected").arg(folderModel.selectionCound) - visible: folderModel.selectionCound >= 1 + text: qsTr("%1 selected").arg(dirModel.selectionCound) + visible: dirModel.selectionCound >= 1 } Item { @@ -127,8 +128,8 @@ Item { Layout.fillHeight: true Layout.alignment: Qt.AlignRight text: qsTr("Empty Trash") - onClicked: folderModel.emptyTrash() - visible: folderModel.url === "trash:/" + onClicked: dirModel.emptyTrash() + visible: dirModel.url === "trash:/" } } } @@ -139,7 +140,7 @@ Item { FolderGridView { id: _gridView - model: folderModel + model: dirModel delegate: FolderGridItem {} leftMargin: Meui.Units.largeSpacing @@ -163,7 +164,7 @@ Item { FolderListView { id: _folderListView - model: folderModel + model: dirModel topMargin: Meui.Units.largeSpacing leftMargin: Meui.Units.largeSpacing @@ -219,36 +220,36 @@ Item { function onKeyPress(event) { if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) - folderModel.openSelected() + dirModel.openSelected() else if (event.key === Qt.Key_C && event.modifiers & Qt.ControlModifier) - folderModel.copy() + dirModel.copy() else if (event.key === Qt.Key_X && event.modifiers & Qt.ControlModifier) - folderModel.cut() + dirModel.cut() else if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) - folderModel.paste() + dirModel.paste() else if (event.key === Qt.Key_F2) - folderModel.requestRename() + dirModel.requestRename() else if (event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier) folderPage.requestPathEditor() else if (event.key === Qt.Key_A && event.modifiers & Qt.ControlModifier) - folderModel.selectAll() + dirModel.selectAll() else if (event.key === Qt.Key_Backspace) - folderModel.up() + dirModel.up() else if (event.key === Qt.Key_Delete) - folderModel.keyDeletePress() + dirModel.keyDeletePress() } } function openUrl(url) { - folderModel.url = url + dirModel.url = url _viewLoader.item.forceActiveFocus() } function goBack() { - folderModel.goBack() + dirModel.goBack() } function goForward() { - folderModel.goForward() + dirModel.goForward() } }