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/shortcut.cpp
|
||||
helper/filelauncher.cpp
|
||||
helper/keyboardsearchmanager.cpp
|
||||
|
||||
mimetype/mimeappmanager.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 2016 Leslie Zhai <xiangzhai83@gmail.com> *
|
||||
* *
|
||||
|
@ -20,6 +20,7 @@
|
|||
***************************************************************************/
|
||||
|
||||
#include "shortcut.h"
|
||||
#include "keyboardsearchmanager.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
|
@ -67,6 +68,10 @@ bool ShortCut::eventFilter(QObject *obj, QEvent *e)
|
|||
emit deleteFile();
|
||||
} else if (keyEvent->key() == Qt::Key_F5) {
|
||||
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 2016 Leslie Zhai <xiangzhai83@gmail.com> *
|
||||
* *
|
||||
|
@ -24,17 +24,6 @@
|
|||
|
||||
#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
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
@ -90,6 +90,7 @@ FolderModel::FolderModel(QObject *parent)
|
|||
, m_viewAdapter(nullptr)
|
||||
, m_mimeAppManager(MimeAppManager::self())
|
||||
, m_sizeJob(nullptr)
|
||||
, m_keyboardSearchManager(KeyboardSearchManager::self())
|
||||
{
|
||||
QSettings settings("cutefishos", qApp->applicationName());
|
||||
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(rowsRemoved(QModelIndex, int, int)), SIGNAL(countChanged()));
|
||||
connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
|
||||
|
||||
connect(m_keyboardSearchManager, &KeyboardSearchManager::searchTextChanged,
|
||||
this, &FolderModel::keyboardSearchChanged);
|
||||
}
|
||||
|
||||
FolderModel::~FolderModel()
|
||||
|
@ -232,11 +236,39 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
|
|||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
const QStringList supportsMimetypes = {"image/bmp", "image/png", "image/gif", "image/jpeg", "image/web",
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#define FOLDERMODEL_H
|
||||
|
||||
#include "../widgets/itemviewadapter.h"
|
||||
#include "../helper/keyboardsearchmanager.h"
|
||||
#include "../helper/pathhistory.h"
|
||||
#include "../mimetype/mimeappmanager.h"
|
||||
|
||||
|
@ -109,8 +110,11 @@ public:
|
|||
static QHash<int, QByteArray> staticRoleNames();
|
||||
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 fileItem(int index) const;
|
||||
|
||||
QList<QUrl> selectedUrls() const;
|
||||
|
||||
QString url() const;
|
||||
|
@ -238,6 +242,7 @@ signals:
|
|||
private slots:
|
||||
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||
void dragSelectedInternal(int x, int y);
|
||||
void keyboardSearchChanged(const QString &text, bool searchFromNextItem);
|
||||
|
||||
private:
|
||||
void invalidateIfComplete();
|
||||
|
@ -294,6 +299,8 @@ private:
|
|||
MimeAppManager *m_mimeAppManager;
|
||||
|
||||
CFileSizeJob *m_sizeJob;
|
||||
|
||||
KeyboardSearchManager *m_keyboardSearchManager;
|
||||
};
|
||||
|
||||
#endif // FOLDERMODEL_H
|
||||
|
|
|
@ -111,7 +111,7 @@ Item {
|
|||
anchors.fill: _icon
|
||||
source: _icon
|
||||
color: "white"
|
||||
opacity: FishUI.Theme.darkMode ? 0.3 : 0.4
|
||||
opacity: 0.3
|
||||
visible: control.hovered && !control.selected
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ GridView {
|
|||
property bool ctrlPressed: false
|
||||
property bool shiftPressed: false
|
||||
|
||||
property int previouslySelectedItemIndex: -1
|
||||
property variant cPress: null
|
||||
property Item editor: null
|
||||
property int anchorIndex: 0
|
||||
|
@ -138,7 +137,6 @@ GridView {
|
|||
function reset() {
|
||||
currentIndex = -1
|
||||
anchorIndex = 0
|
||||
previouslySelectedItemIndex = -1
|
||||
cancelRename()
|
||||
hoveredItem = null
|
||||
pressedItem = null
|
||||
|
@ -174,7 +172,6 @@ GridView {
|
|||
}
|
||||
Keys.onEscapePressed: {
|
||||
if (!editor || !editor.targetItem) {
|
||||
previouslySelectedItemIndex = -1
|
||||
dirModel.clearSelection()
|
||||
event.accepted = false
|
||||
}
|
||||
|
@ -602,9 +599,6 @@ GridView {
|
|||
} else {
|
||||
dirModel.clearSelection()
|
||||
dirModel.setSelected(currentIndex)
|
||||
if (currentIndex == -1)
|
||||
previouslySelectedItemIndex = -1
|
||||
previouslySelectedItemIndex = currentIndex
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ ListView {
|
|||
property bool ctrlPressed: false
|
||||
property bool shiftPressed: false
|
||||
|
||||
property int previouslySelectedItemIndex: -1
|
||||
property variant cPress: null
|
||||
property Item editor: null
|
||||
property int anchorIndex: 0
|
||||
|
@ -86,7 +85,6 @@ ListView {
|
|||
function reset() {
|
||||
currentIndex = -1
|
||||
anchorIndex = 0
|
||||
previouslySelectedItemIndex = -1
|
||||
cancelRename()
|
||||
hoveredItem = null
|
||||
pressedItem = null
|
||||
|
@ -119,7 +117,6 @@ ListView {
|
|||
|
||||
Keys.onEscapePressed: {
|
||||
if (!editor || !editor.targetItem) {
|
||||
previouslySelectedItemIndex = -1
|
||||
dirModel.clearSelection()
|
||||
event.accepted = false
|
||||
}
|
||||
|
@ -215,7 +212,6 @@ ListView {
|
|||
if (!hoveredItem || hoveredItem.blank) {
|
||||
if (!control.ctrlPressed) {
|
||||
control.currentIndex = -1
|
||||
control.previouslySelectedItemIndex = -1
|
||||
dirModel.clearSelection()
|
||||
}
|
||||
|
||||
|
@ -231,7 +227,6 @@ ListView {
|
|||
dirModel.setRangeSelected(control.anchorIndex, hoveredItem.index)
|
||||
} else {
|
||||
if (!control.ctrlPressed && !dirModel.isSelected(hoveredItem.index)) {
|
||||
previouslySelectedItemIndex = -1
|
||||
dirModel.clearSelection()
|
||||
}
|
||||
|
||||
|
@ -395,9 +390,6 @@ ListView {
|
|||
} else {
|
||||
dirModel.clearSelection()
|
||||
dirModel.setSelected(currentIndex)
|
||||
if (currentIndex == -1)
|
||||
previouslySelectedItemIndex = -1
|
||||
previouslySelectedItemIndex = currentIndex
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue