diff --git a/CMakeLists.txt b/CMakeLists.txt index c465110..eabe7bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable(cutefish-filemanager helper/thumbnailer.cpp helper/pathhistory.cpp helper/fm.cpp + helper/shortcut.cpp desktopiconprovider.cpp diff --git a/desktop/desktopview.cpp b/desktop/desktopview.cpp index 5743949..58f6d14 100644 --- a/desktop/desktopview.cpp +++ b/desktop/desktopview.cpp @@ -44,7 +44,7 @@ DesktopView::DesktopView(QQuickView *parent) setTitle(tr("Desktop")); setScreen(qApp->primaryScreen()); setResizeMode(QQuickView::SizeRootObjectToView); - setSource(QStringLiteral("qrc:/qml/Desktop/main.qml")); + setSource(QStringLiteral("qrc:/qml/Desktop/Main.qml")); onGeometryChanged(); @@ -67,7 +67,7 @@ QRect DesktopView::screenAvailableRect() void DesktopView::onGeometryChanged() { m_screenRect = qApp->primaryScreen()->geometry(); - setGeometry(qApp->primaryScreen()->geometry()); + setGeometry(m_screenRect); emit screenRectChanged(); } diff --git a/helper/shortcut.cpp b/helper/shortcut.cpp new file mode 100644 index 0000000..66de69e --- /dev/null +++ b/helper/shortcut.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright 2021 Reion Wong * + * Copyright Ken * + * Copyright 2016 Leslie Zhai * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "shortcut.h" + +#include + +ShortCut::ShortCut(QObject *parent) + : QObject(parent) + , m_object(parent) +{ +} + +void ShortCut::install(QObject *target) +{ + if (m_object) { + m_object->removeEventFilter(this); + } + + if (target) { + target->installEventFilter(this); + m_object = target; + } +} + +bool ShortCut::eventFilter(QObject *obj, QEvent *e) +{ + if (e->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(e); + // int keyInt = keyEvent->modifiers() + keyEvent->key(); + + if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { + emit open(); + } else if (keyEvent->key() == Qt::Key_C && keyEvent->modifiers() & Qt::ControlModifier) { + emit copy(); + } else if (keyEvent->key() == Qt::Key_X && keyEvent->modifiers() & Qt::ControlModifier) { + emit cut(); + } else if (keyEvent->key() == Qt::Key_V && keyEvent->modifiers() & Qt::ControlModifier) { + emit paste(); + } else if (keyEvent->key() == Qt::Key_F2) { + emit rename(); + } else if (keyEvent->key() == Qt::Key_L && keyEvent->modifiers() & Qt::ControlModifier) { + emit openPathEditor(); + } else if (keyEvent->key() == Qt::Key_A && keyEvent->modifiers() & Qt::ControlModifier) { + emit selectAll(); + } else if (keyEvent->key() == Qt::Key_Backspace) { + emit backspace(); + } else if (keyEvent->key() == Qt::Key_Delete) { + emit deleteFile(); + } + } + + return QObject::eventFilter(obj, e); +} diff --git a/helper/shortcut.h b/helper/shortcut.h new file mode 100644 index 0000000..d4c9c80 --- /dev/null +++ b/helper/shortcut.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright 2021 Reion Wong * + * Copyright Ken * + * Copyright 2016 Leslie Zhai * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef SHORTCUT_H +#define SHORTCUT_H + +#include + +/** + * TODO: ShortCut is a stopgap solution and should be dropped when Qt's StandardKey + * gains support for these actions. QTBUG-54926 https://bugreports.qt.io/browse/QTBUG-54926 + * And it is *NOT* encouraged registering C++ types with the QML by using EventFilter + * but for special case QTBUG-40327 https://bugreports.qt.io/browse/QTBUG-40327 + * + * ShortCut was copied from Ken's answer. + * https://stackoverflow.com/questions/12192780/assigning-keyboard-shortcuts-to-qml-components + * it uses cc by-sa 3.0 license by default compatible with GPL. + * https://www.gnu.org/licenses/license-list.en.html#ccbysa + */ +class ShortCut : public QObject +{ + Q_OBJECT + +public: + explicit ShortCut(QObject *parent = nullptr); + + Q_INVOKABLE void install(QObject *target = nullptr); + +signals: + void open(); + void copy(); + void cut(); + void paste(); + void rename(); + void openPathEditor(); + void selectAll(); + void backspace(); + void deleteFile(); + +protected: + bool eventFilter(QObject *obj, QEvent *e) override; + +private: + QObject *m_object; +}; + +#endif // SHORTCUT_H diff --git a/main.cpp b/main.cpp index 0cc0b13..e5da074 100644 --- a/main.cpp +++ b/main.cpp @@ -36,6 +36,7 @@ #include "helper/thumbnailer.h" #include "helper/datehelper.h" #include "helper/fm.h" +#include "helper/shortcut.h" int main(int argc, char *argv[]) { @@ -66,6 +67,7 @@ int main(int argc, char *argv[]) qmlRegisterType(uri, 1, 0, "ItemViewAdapter"); qmlRegisterType(uri, 1, 0, "DesktopSettings"); qmlRegisterType(uri, 1, 0, "Fm"); + qmlRegisterType(uri, 1, 0, "ShortCut"); qmlRegisterAnonymousType(uri, 1); QCommandLineParser parser; diff --git a/model/foldermodel.cpp b/model/foldermodel.cpp index 1801f24..91ff07f 100644 --- a/model/foldermodel.cpp +++ b/model/foldermodel.cpp @@ -665,11 +665,25 @@ void FolderModel::copy() void FolderModel::paste() { - if (QAction *action = m_actionCollection.action("paste")) - if (!action->isEnabled()) - return; + const QMimeData *mimeData = QApplication::clipboard()->mimeData(); + bool enable = false; - KIO::paste(QApplication::clipboard()->mimeData(), m_dirModel->dirLister()->url()); + // Update paste action + if (QAction *paste = m_actionCollection.action(QStringLiteral("paste"))) { + QList urls = KUrlMimeData::urlsFromMimeData(mimeData); + + if (!urls.isEmpty()) { + if (!rootItem().isNull()) { + enable = rootItem().isWritable(); + } + } + + paste->setEnabled(enable); + } + + if (enable) { + KIO::paste(mimeData, m_dirModel->dirLister()->url()); + } } void FolderModel::cut() @@ -1135,10 +1149,13 @@ void FolderModel::updateActions() if (QAction *paste = m_actionCollection.action(QStringLiteral("paste"))) { bool enable = false; - QList urls = KUrlMimeData::urlsFromMimeData(QApplication::clipboard()->mimeData()); + const QMimeData *mimeData = QApplication::clipboard()->mimeData(); + QList urls = KUrlMimeData::urlsFromMimeData(mimeData); - if (!urls.isEmpty() && rootItem().isWritable()) { - enable = rootItem().isWritable(); + if (!urls.isEmpty()) { + if (!rootItem().isNull()) { + enable = rootItem().isWritable(); + } } paste->setEnabled(enable); diff --git a/qml.qrc b/qml.qrc index f6eed3f..62d532c 100644 --- a/qml.qrc +++ b/qml.qrc @@ -16,7 +16,7 @@ images/light/grid.svg images/light/list.svg qml/PathBar.qml - qml/Desktop/main.qml + qml/Desktop/Main.qml qml/FolderGridView.qml qml/GlobalSettings.qml qml/FolderGridItem.qml diff --git a/qml/Desktop/main.qml b/qml/Desktop/Main.qml similarity index 76% rename from qml/Desktop/main.qml rename to qml/Desktop/Main.qml index 5fed855..8f22052 100644 --- a/qml/Desktop/main.qml +++ b/qml/Desktop/Main.qml @@ -23,14 +23,14 @@ import QtQuick.Layouts 1.12 import QtQuick.Window 2.12 import QtGraphicalEffects 1.0 -import Cutefish.FileManager 1.0 +import Cutefish.FileManager 1.0 as FM import FishUI 1.0 as FishUI import "../" Item { id: rootItem - DesktopSettings { + FM.DesktopSettings { id: settings } @@ -81,14 +81,14 @@ Item { } } - FolderModel { + FM.FolderModel { id: dirModel url: desktopPath() isDesktop: true viewAdapter: viewAdapter } - ItemViewAdapter { + FM.ItemViewAdapter { id: viewAdapter adapterView: _folderView adapterModel: dirModel @@ -97,6 +97,11 @@ Item { _folderView.contentWidth, _folderView.contentHeight) } + MouseArea { + anchors.fill: parent + onClicked: _folderView.forceActiveFocus() + } + FolderGridView { id: _folderView anchors.fill: parent @@ -118,19 +123,6 @@ Item { rightMargin: desktopView.screenRect.width - (desktopView.screenAvailableRect.x + desktopView.screenAvailableRect.width) bottomMargin: desktopView.screenRect.height - (desktopView.screenAvailableRect.y + desktopView.screenAvailableRect.height) - Behavior on anchors.topMargin { - NumberAnimation { duration: 125; easing.type: Easing.Linear } - } - Behavior on leftMargin { - NumberAnimation { duration: 125; easing.type: Easing.Linear } - } - Behavior on rightMargin { - NumberAnimation { duration: 125; easing.type: Easing.Linear } - } - Behavior on bottomMargin { - NumberAnimation { duration: 125; easing.type: Easing.Linear } - } - flow: GridView.FlowTopToBottom delegate: FolderGridItem {} @@ -151,31 +143,46 @@ Item { } } - Connections { - target: _folderView + FM.ShortCut { + id: shortCut - function onKeyPress(event) { - if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) - dirModel.openSelected() - else if (event.key === Qt.Key_C && event.modifiers & Qt.ControlModifier) - dirModel.copy() - else if (event.key === Qt.Key_X && event.modifiers & Qt.ControlModifier) - dirModel.cut() - else if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) - dirModel.paste() - else if (event.key === Qt.Key_F2) - dirModel.requestRename() - else if (event.key === Qt.Key_A && event.modifiers & Qt.ControlModifier) - dirModel.selectAll() - else if (event.key === Qt.Key_Delete) - dirModel.keyDeletePress() + Component.onCompleted: { + shortCut.install(_folderView) + } + + onOpen: { + dirModel.openSelected() + } + onCopy: { + dirModel.copy() + } + onCut: { + dirModel.cut() + } + onPaste: { + dirModel.paste() + } + onRename: { + dirModel.requestRename() + } + onOpenPathEditor: { + folderPage.requestPathEditor() + } + onSelectAll: { + dirModel.selectAll() + } + onBackspace: { + dirModel.up() + } + onDeleteFile: { + dirModel.keyDeletePress() } } Component { id: rubberBandObject - RubberBand { + FM.RubberBand { id: rubberBand width: 0 diff --git a/qml/FolderPage.qml b/qml/FolderPage.qml index f37e4b9..ac9083a 100644 --- a/qml/FolderPage.qml +++ b/qml/FolderPage.qml @@ -21,7 +21,7 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 -import Cutefish.FileManager 1.0 +import Cutefish.FileManager 1.0 as FM import FishUI 1.0 as FishUI import "./Dialogs" @@ -57,7 +57,7 @@ Item { visible: false } - FolderModel { + FM.FolderModel { id: dirModel viewAdapter: viewAdapter @@ -69,7 +69,7 @@ Item { } } - ItemViewAdapter { + FM.ItemViewAdapter { id: viewAdapter adapterView: _viewLoader.item adapterModel: _viewLoader.item.positioner ? _viewLoader.item.positioner : dirModel @@ -107,8 +107,11 @@ Item { } onSourceComponentChanged: { - // 焦点 + // Focus _viewLoader.item.forceActiveFocus() + + // ShortCut + shortCut.install(_viewLoader.item) } } @@ -218,7 +221,7 @@ Item { Component { id: rubberBandObject - RubberBand { + FM.RubberBand { id: rubberBand width: 0 @@ -251,28 +254,35 @@ Item { } } - Connections { - target: _viewLoader.item + FM.ShortCut { + id: shortCut - function onKeyPress(event) { - if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) - dirModel.openSelected() - else if (event.key === Qt.Key_C && event.modifiers & Qt.ControlModifier) - dirModel.copy() - else if (event.key === Qt.Key_X && event.modifiers & Qt.ControlModifier) - dirModel.cut() - else if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) - dirModel.paste() - else if (event.key === Qt.Key_F2) - 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) - dirModel.selectAll() - else if (event.key === Qt.Key_Backspace) - dirModel.up() - else if (event.key === Qt.Key_Delete) - dirModel.keyDeletePress() + onOpen: { + dirModel.openSelected() + } + onCopy: { + dirModel.copy() + } + onCut: { + dirModel.cut() + } + onPaste: { + dirModel.paste() + } + onRename: { + dirModel.requestRename() + } + onOpenPathEditor: { + folderPage.requestPathEditor() + } + onSelectAll: { + dirModel.selectAll() + } + onBackspace: { + dirModel.up() + } + onDeleteFile: { + dirModel.keyDeletePress() } }