Support keyboard search selection
This commit is contained in:
parent
a324a3b771
commit
8587b0c040
10 changed files with 181 additions and 28 deletions
|
@ -45,6 +45,7 @@ add_executable(cutefish-filemanager
|
||||||
helper/fm.cpp
|
helper/fm.cpp
|
||||||
helper/shortcut.cpp
|
helper/shortcut.cpp
|
||||||
helper/filelauncher.cpp
|
helper/filelauncher.cpp
|
||||||
|
helper/keyboardsearchmanager.cpp
|
||||||
|
|
||||||
mimetype/mimeappmanager.cpp
|
mimetype/mimeappmanager.cpp
|
||||||
mimetype/xdgdesktopfile.cpp
|
mimetype/xdgdesktopfile.cpp
|
||||||
|
|
56
helper/keyboardsearchmanager.cpp
Normal file
56
helper/keyboardsearchmanager.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: Reion Wong <reion@cutefishos.com>
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or
|
||||||
|
* 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "keyboardsearchmanager.h"
|
||||||
|
|
||||||
|
KeyboardSearchManager *KEYBORDSRARCH_MANAGER_SELF = nullptr;
|
||||||
|
|
||||||
|
KeyboardSearchManager *KeyboardSearchManager::self()
|
||||||
|
{
|
||||||
|
if (!KEYBORDSRARCH_MANAGER_SELF)
|
||||||
|
KEYBORDSRARCH_MANAGER_SELF = new KeyboardSearchManager;
|
||||||
|
|
||||||
|
return KEYBORDSRARCH_MANAGER_SELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardSearchManager::KeyboardSearchManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_timeout(500)
|
||||||
|
{
|
||||||
|
// m_timer.setInterval(m_timeout);
|
||||||
|
// connect(&m_timer, &QTimer::timeout, this, [=] {
|
||||||
|
// m_searchText.clear();
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardSearchManager::addKeys(const QString &keys)
|
||||||
|
{
|
||||||
|
if (!keys.isEmpty()) {
|
||||||
|
// m_timer.stop();
|
||||||
|
// m_searchText.append(keys);
|
||||||
|
|
||||||
|
// const QChar firstKey = m_searchText.length() > 0 ? m_searchText.at(0) : QChar();
|
||||||
|
// const bool sameKey = m_searchText.length() > 1 && m_searchText.count(firstKey) == m_searchText.length();
|
||||||
|
|
||||||
|
// emit searchTextChanged(sameKey ? firstKey : m_searchText, false);
|
||||||
|
emit searchTextChanged(keys, false);
|
||||||
|
|
||||||
|
// m_timer.start();
|
||||||
|
}
|
||||||
|
}
|
46
helper/keyboardsearchmanager.h
Normal file
46
helper/keyboardsearchmanager.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: Reion Wong <reion@cutefishos.com>
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or
|
||||||
|
* 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef KEYBOARDSEARCHMANAGER_H
|
||||||
|
#define KEYBOARDSEARCHMANAGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class KeyboardSearchManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static KeyboardSearchManager *self();
|
||||||
|
explicit KeyboardSearchManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void addKeys(const QString &keys);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void searchTextChanged(const QString &string, bool searchFromNextItem);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_searchText;
|
||||||
|
qint64 m_timeout;
|
||||||
|
// QTimer m_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEYBOARDSEARCHMANAGER_H
|
|
@ -1,5 +1,5 @@
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Copyright 2021 Reion Wong <aj@cutefishos.com> *
|
* Copyright 2021 Reion Wong <reion@cutefishos.com> *
|
||||||
* Copyright Ken <https://stackoverflow.com/users/1568857/ken> *
|
* Copyright Ken <https://stackoverflow.com/users/1568857/ken> *
|
||||||
* Copyright 2016 Leslie Zhai <xiangzhai83@gmail.com> *
|
* Copyright 2016 Leslie Zhai <xiangzhai83@gmail.com> *
|
||||||
* *
|
* *
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include "shortcut.h"
|
#include "shortcut.h"
|
||||||
|
#include "keyboardsearchmanager.h"
|
||||||
|
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
@ -67,6 +68,10 @@ bool ShortCut::eventFilter(QObject *obj, QEvent *e)
|
||||||
emit deleteFile();
|
emit deleteFile();
|
||||||
} else if (keyEvent->key() == Qt::Key_F5) {
|
} else if (keyEvent->key() == Qt::Key_F5) {
|
||||||
emit refresh();
|
emit refresh();
|
||||||
|
} else if (keyEvent->key() >= Qt::Key_A && keyEvent->key() <= Qt::Key_Z) {
|
||||||
|
// Handle select
|
||||||
|
KeyboardSearchManager::self()->addKeys(keyEvent->text());
|
||||||
|
keyEvent->ignore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Copyright 2021 Reion Wong <aj@cutefishos.com> *
|
* Copyright 2021 Reion Wong <reion@cutefishos.com> *
|
||||||
* Copyright Ken <https://stackoverflow.com/users/1568857/ken> *
|
* Copyright Ken <https://stackoverflow.com/users/1568857/ken> *
|
||||||
* Copyright 2016 Leslie Zhai <xiangzhai83@gmail.com> *
|
* Copyright 2016 Leslie Zhai <xiangzhai83@gmail.com> *
|
||||||
* *
|
* *
|
||||||
|
@ -24,17 +24,6 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
class ShortCut : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -90,6 +90,7 @@ FolderModel::FolderModel(QObject *parent)
|
||||||
, m_viewAdapter(nullptr)
|
, m_viewAdapter(nullptr)
|
||||||
, m_mimeAppManager(MimeAppManager::self())
|
, m_mimeAppManager(MimeAppManager::self())
|
||||||
, m_sizeJob(nullptr)
|
, m_sizeJob(nullptr)
|
||||||
|
, m_keyboardSearchManager(KeyboardSearchManager::self())
|
||||||
{
|
{
|
||||||
QSettings settings("cutefishos", qApp->applicationName());
|
QSettings settings("cutefishos", qApp->applicationName());
|
||||||
m_showHiddenFiles = settings.value("showHiddenFiles", false).toBool();
|
m_showHiddenFiles = settings.value("showHiddenFiles", false).toBool();
|
||||||
|
@ -119,6 +120,9 @@ FolderModel::FolderModel(QObject *parent)
|
||||||
connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)), SIGNAL(countChanged()));
|
connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)), SIGNAL(countChanged()));
|
||||||
connect(this, SIGNAL(rowsRemoved(QModelIndex, int, int)), SIGNAL(countChanged()));
|
connect(this, SIGNAL(rowsRemoved(QModelIndex, int, int)), SIGNAL(countChanged()));
|
||||||
connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
|
connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
|
||||||
|
|
||||||
|
connect(m_keyboardSearchManager, &KeyboardSearchManager::searchTextChanged,
|
||||||
|
this, &FolderModel::keyboardSearchChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
FolderModel::~FolderModel()
|
FolderModel::~FolderModel()
|
||||||
|
@ -232,11 +236,39 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
|
||||||
return QSortFilterProxyModel::data(index, role);
|
return QSortFilterProxyModel::data(index, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int FolderModel::indexForKeyboardSearch(const QString &text, int startFromIndex) const
|
||||||
|
{
|
||||||
|
startFromIndex = qMax(0, startFromIndex);
|
||||||
|
|
||||||
|
for (int i = startFromIndex; i < rowCount(); ++i) {
|
||||||
|
if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < startFromIndex; ++i) {
|
||||||
|
if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
KFileItem FolderModel::itemForIndex(const QModelIndex &index) const
|
KFileItem FolderModel::itemForIndex(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
return m_dirModel->itemForIndex(mapToSource(index));
|
return m_dirModel->itemForIndex(mapToSource(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KFileItem FolderModel::fileItem(int index) const
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < count()) {
|
||||||
|
return itemForIndex(FolderModel::index(index, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return KFileItem();
|
||||||
|
}
|
||||||
|
|
||||||
QList<QUrl> FolderModel::selectedUrls() const
|
QList<QUrl> FolderModel::selectedUrls() const
|
||||||
{
|
{
|
||||||
const auto indexes = m_selectionModel->selectedIndexes();
|
const auto indexes = m_selectionModel->selectedIndexes();
|
||||||
|
@ -1241,6 +1273,37 @@ void FolderModel::dragSelectedInternal(int x, int y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderModel::keyboardSearchChanged(const QString &text, bool searchFromNextItem)
|
||||||
|
{
|
||||||
|
Q_UNUSED(searchFromNextItem);
|
||||||
|
|
||||||
|
if (rowCount() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index;
|
||||||
|
int currentIndex = -1;
|
||||||
|
|
||||||
|
if (m_selectionModel->hasSelection()) {
|
||||||
|
currentIndex = m_selectionModel->selectedIndexes().first().row();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (searchFromNextItem) {
|
||||||
|
// index = indexForKeyboardSearch(text, (currentIndex + 1) % rowCount());
|
||||||
|
// } else {
|
||||||
|
// index = indexForKeyboardSearch(text, 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
index = indexForKeyboardSearch(text, (currentIndex + 1) % rowCount());
|
||||||
|
|
||||||
|
if (index < 0 || currentIndex == index)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
clearSelection();
|
||||||
|
setSelected(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool FolderModel::isSupportThumbnails(const QString &mimeType) const
|
bool FolderModel::isSupportThumbnails(const QString &mimeType) const
|
||||||
{
|
{
|
||||||
const QStringList supportsMimetypes = {"image/bmp", "image/png", "image/gif", "image/jpeg", "image/web",
|
const QStringList supportsMimetypes = {"image/bmp", "image/png", "image/gif", "image/jpeg", "image/web",
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define FOLDERMODEL_H
|
#define FOLDERMODEL_H
|
||||||
|
|
||||||
#include "../widgets/itemviewadapter.h"
|
#include "../widgets/itemviewadapter.h"
|
||||||
|
#include "../helper/keyboardsearchmanager.h"
|
||||||
#include "../helper/pathhistory.h"
|
#include "../helper/pathhistory.h"
|
||||||
#include "../mimetype/mimeappmanager.h"
|
#include "../mimetype/mimeappmanager.h"
|
||||||
|
|
||||||
|
@ -109,8 +110,11 @@ public:
|
||||||
static QHash<int, QByteArray> staticRoleNames();
|
static QHash<int, QByteArray> staticRoleNames();
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
int indexForKeyboardSearch(const QString &text, int startFromIndex = 0) const;
|
||||||
KFileItem itemForIndex(const QModelIndex &index) const;
|
KFileItem itemForIndex(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
KFileItem fileItem(int index) const;
|
||||||
|
|
||||||
QList<QUrl> selectedUrls() const;
|
QList<QUrl> selectedUrls() const;
|
||||||
|
|
||||||
QString url() const;
|
QString url() const;
|
||||||
|
@ -238,6 +242,7 @@ signals:
|
||||||
private slots:
|
private slots:
|
||||||
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||||
void dragSelectedInternal(int x, int y);
|
void dragSelectedInternal(int x, int y);
|
||||||
|
void keyboardSearchChanged(const QString &text, bool searchFromNextItem);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void invalidateIfComplete();
|
void invalidateIfComplete();
|
||||||
|
@ -294,6 +299,8 @@ private:
|
||||||
MimeAppManager *m_mimeAppManager;
|
MimeAppManager *m_mimeAppManager;
|
||||||
|
|
||||||
CFileSizeJob *m_sizeJob;
|
CFileSizeJob *m_sizeJob;
|
||||||
|
|
||||||
|
KeyboardSearchManager *m_keyboardSearchManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FOLDERMODEL_H
|
#endif // FOLDERMODEL_H
|
||||||
|
|
|
@ -111,7 +111,7 @@ Item {
|
||||||
anchors.fill: _icon
|
anchors.fill: _icon
|
||||||
source: _icon
|
source: _icon
|
||||||
color: "white"
|
color: "white"
|
||||||
opacity: FishUI.Theme.darkMode ? 0.3 : 0.4
|
opacity: 0.3
|
||||||
visible: control.hovered && !control.selected
|
visible: control.hovered && !control.selected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,6 @@ GridView {
|
||||||
property bool ctrlPressed: false
|
property bool ctrlPressed: false
|
||||||
property bool shiftPressed: false
|
property bool shiftPressed: false
|
||||||
|
|
||||||
property int previouslySelectedItemIndex: -1
|
|
||||||
property variant cPress: null
|
property variant cPress: null
|
||||||
property Item editor: null
|
property Item editor: null
|
||||||
property int anchorIndex: 0
|
property int anchorIndex: 0
|
||||||
|
@ -138,7 +137,6 @@ GridView {
|
||||||
function reset() {
|
function reset() {
|
||||||
currentIndex = -1
|
currentIndex = -1
|
||||||
anchorIndex = 0
|
anchorIndex = 0
|
||||||
previouslySelectedItemIndex = -1
|
|
||||||
cancelRename()
|
cancelRename()
|
||||||
hoveredItem = null
|
hoveredItem = null
|
||||||
pressedItem = null
|
pressedItem = null
|
||||||
|
@ -174,7 +172,6 @@ GridView {
|
||||||
}
|
}
|
||||||
Keys.onEscapePressed: {
|
Keys.onEscapePressed: {
|
||||||
if (!editor || !editor.targetItem) {
|
if (!editor || !editor.targetItem) {
|
||||||
previouslySelectedItemIndex = -1
|
|
||||||
dirModel.clearSelection()
|
dirModel.clearSelection()
|
||||||
event.accepted = false
|
event.accepted = false
|
||||||
}
|
}
|
||||||
|
@ -602,9 +599,6 @@ GridView {
|
||||||
} else {
|
} else {
|
||||||
dirModel.clearSelection()
|
dirModel.clearSelection()
|
||||||
dirModel.setSelected(currentIndex)
|
dirModel.setSelected(currentIndex)
|
||||||
if (currentIndex == -1)
|
|
||||||
previouslySelectedItemIndex = -1
|
|
||||||
previouslySelectedItemIndex = currentIndex
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@ ListView {
|
||||||
property bool ctrlPressed: false
|
property bool ctrlPressed: false
|
||||||
property bool shiftPressed: false
|
property bool shiftPressed: false
|
||||||
|
|
||||||
property int previouslySelectedItemIndex: -1
|
|
||||||
property variant cPress: null
|
property variant cPress: null
|
||||||
property Item editor: null
|
property Item editor: null
|
||||||
property int anchorIndex: 0
|
property int anchorIndex: 0
|
||||||
|
@ -86,7 +85,6 @@ ListView {
|
||||||
function reset() {
|
function reset() {
|
||||||
currentIndex = -1
|
currentIndex = -1
|
||||||
anchorIndex = 0
|
anchorIndex = 0
|
||||||
previouslySelectedItemIndex = -1
|
|
||||||
cancelRename()
|
cancelRename()
|
||||||
hoveredItem = null
|
hoveredItem = null
|
||||||
pressedItem = null
|
pressedItem = null
|
||||||
|
@ -119,7 +117,6 @@ ListView {
|
||||||
|
|
||||||
Keys.onEscapePressed: {
|
Keys.onEscapePressed: {
|
||||||
if (!editor || !editor.targetItem) {
|
if (!editor || !editor.targetItem) {
|
||||||
previouslySelectedItemIndex = -1
|
|
||||||
dirModel.clearSelection()
|
dirModel.clearSelection()
|
||||||
event.accepted = false
|
event.accepted = false
|
||||||
}
|
}
|
||||||
|
@ -215,7 +212,6 @@ ListView {
|
||||||
if (!hoveredItem || hoveredItem.blank) {
|
if (!hoveredItem || hoveredItem.blank) {
|
||||||
if (!control.ctrlPressed) {
|
if (!control.ctrlPressed) {
|
||||||
control.currentIndex = -1
|
control.currentIndex = -1
|
||||||
control.previouslySelectedItemIndex = -1
|
|
||||||
dirModel.clearSelection()
|
dirModel.clearSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +227,6 @@ ListView {
|
||||||
dirModel.setRangeSelected(control.anchorIndex, hoveredItem.index)
|
dirModel.setRangeSelected(control.anchorIndex, hoveredItem.index)
|
||||||
} else {
|
} else {
|
||||||
if (!control.ctrlPressed && !dirModel.isSelected(hoveredItem.index)) {
|
if (!control.ctrlPressed && !dirModel.isSelected(hoveredItem.index)) {
|
||||||
previouslySelectedItemIndex = -1
|
|
||||||
dirModel.clearSelection()
|
dirModel.clearSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,9 +390,6 @@ ListView {
|
||||||
} else {
|
} else {
|
||||||
dirModel.clearSelection()
|
dirModel.clearSelection()
|
||||||
dirModel.setSelected(currentIndex)
|
dirModel.setSelected(currentIndex)
|
||||||
if (currentIndex == -1)
|
|
||||||
previouslySelectedItemIndex = -1
|
|
||||||
previouslySelectedItemIndex = currentIndex
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue