Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5e215cd
Remove inHiddenDirectory
sakertooth Nov 13, 2023
602213f
Redesign searcher class
sakertooth Nov 14, 2023
f21e2dd
Search filesystem using BFS and batching
sakertooth Nov 15, 2023
accc299
Fix bugs
sakertooth Nov 15, 2023
86bf619
Tweak batch tunables
sakertooth Nov 15, 2023
9f92f88
Implement buildSearchTree (wip)
sakertooth Nov 15, 2023
86aff46
Use fileName instead of baseName
sakertooth Nov 15, 2023
33bb51d
Fix issues that caused most entries to be missed
sakertooth Nov 15, 2023
54e0e15
Do not make changes to original m_filter
sakertooth Nov 15, 2023
fbca473
Do not update unmatched parent directories
sakertooth Nov 15, 2023
e353b0a
Redesign FileBrowserSearcher to use futures
sakertooth Nov 16, 2023
1f62974
Add search indicator using a progress bar
sakertooth Nov 16, 2023
85a5ce0
Update view with 256 items after 250 milliseconds
sakertooth Nov 16, 2023
d1a28f4
Deactivate signal under worker lock
sakertooth Nov 16, 2023
ba66e25
Remove QUuid include
sakertooth Nov 16, 2023
aca79b1
Remove filterAndExpandItems
sakertooth Nov 16, 2023
5cfb4d6
Abort building search tree when cancelled
sakertooth Nov 16, 2023
fe95fa9
Add border radius for progress chunks
sakertooth Nov 16, 2023
d2513c4
Undo formatting changes
sakertooth Nov 16, 2023
634d1c7
Replace updateOnExpand with updatePixmaps
sakertooth Nov 16, 2023
7153b92
Tweak batch size and time interval again
sakertooth Nov 16, 2023
092f7bd
Fix issue with directory population
sakertooth Nov 16, 2023
1c06d24
Sort entries when doing BFS search
sakertooth Nov 16, 2023
d00a461
Only expand parent folders
sakertooth Nov 16, 2023
cc7aab0
Support filtering of user and factory content when searching
sakertooth Nov 16, 2023
58d2e1f
Do not run a search without any directories
sakertooth Nov 16, 2023
2db8c4f
Fix naming style of constants
sakertooth Nov 17, 2023
714291e
Undo progress bar styling
sakertooth Nov 17, 2023
1156829
Simplify processing of batches
sakertooth Nov 17, 2023
031770b
Tweak batch size and interval to 64 and 250
sakertooth Nov 17, 2023
efeb240
Add directory blacklist
sakertooth Nov 17, 2023
3d55993
Do not use braces
sakertooth Nov 17, 2023
63543d4
Do not use iterators in pushInBatches
sakertooth Nov 17, 2023
b5ddc4e
Use DFS properly within process function
sakertooth Nov 17, 2023
2b51e2a
Add single item every millisecond
sakertooth Nov 17, 2023
79a2e0e
Add documentation
sakertooth Nov 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions data/themes/classic/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar {
color: #e0e0e0;
}

QProgressBar::chunk {
background-color: #e0e0e0;
border-radius: 5px;
}

QMdiArea {
background-image: url("resources:background_artwork.png");
}
Expand Down
5 changes: 5 additions & 0 deletions data/themes/default/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar, QCheckBox {
color: #d1d8e4;
}

QProgressBar::chunk {
background-color: #d1d8e4;
border-radius: 5px;
}

QTreeView {
outline: none;
alternate-background-color: #111314;
Expand Down
66 changes: 10 additions & 56 deletions include/FileBrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,8 @@
#include <QDir>
#include <QMutex>

#ifdef __MINGW32__
#include <mingw.condition_variable.h>
#include <mingw.mutex.h>
#include <mingw.thread.h>
#else
#include <condition_variable>
#include <mutex>
#include <thread>
#endif
#include "FileBrowserSearcher.h"
#include <QProgressBar>

#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
#include <QRecursiveMutex>
Expand Down Expand Up @@ -83,12 +76,12 @@ class FileBrowser : public SideBarWidget

~FileBrowser() override = default;

static QDir::Filters dirFilters();
static QDir::Filters dirFilters() { return QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot; }
static QDir::SortFlags sortFlags() { return QDir::LocaleAware | QDir::DirsFirst | QDir::Name | QDir::IgnoreCase; }

private slots:
void reloadTree();
void expandItems( QTreeWidgetItem * item=nullptr, QList<QString> expandedDirs = QList<QString>() );
bool filterAndExpandItems(const QString & filter, QTreeWidgetItem * item = nullptr);
void giveFocusToFilter();

private:
Expand All @@ -99,7 +92,7 @@ private slots:
void saveDirectoriesStates();
void restoreDirectoriesStates();

void buildSearchTree(QStringList matches, QString id);
void buildSearchTree();
void onSearch(const QString& filter);
void toggleSearch(bool on);

Expand All @@ -108,6 +101,9 @@ private slots:

QLineEdit * m_filterEdit;

std::shared_ptr<FileBrowserSearcher::SearchFuture> m_currentSearch;
QProgressBar* m_searchIndicator = nullptr;

QString m_directories; //!< Directories to search, split with '*'
QString m_filter; //!< Filter as used in QDir::match()

Expand Down Expand Up @@ -183,54 +179,12 @@ private slots:

} ;

class FileBrowserSearcher : public QObject
{
Q_OBJECT
public:
struct SearchTask
{
QString directories;
QString userFilter;
QDir::Filters dirFilters;
QStringList nameFilters;
QString id;
};

FileBrowserSearcher();
~FileBrowserSearcher() noexcept override;

void search(SearchTask task);
void cancel();

bool inHiddenDirectory(const QString& path);

static FileBrowserSearcher* instance();

signals:
void searchComplete(QStringList matches, QString id);

private:
void run();
void filter();
SearchTask m_currentTask;
std::thread m_worker;
std::mutex m_runMutex;
std::mutex m_cancelMutex;
std::condition_variable m_runCond;
std::atomic<bool> m_cancel = false;
bool m_stopped = false;
bool m_run = false;
inline static std::unique_ptr<FileBrowserSearcher> s_instance = nullptr;
};




class Directory : public QTreeWidgetItem
{
public:
Directory( const QString & filename, const QString & path,
const QString & filter );
Directory(const QString& filename, const QString& path, const QString& filter, bool disableEntryPopulation = false);

void update();

Expand Down Expand Up @@ -275,7 +229,7 @@ class Directory : public QTreeWidgetItem
QString m_filter;

int m_dirCount;

bool m_disableEntryPopulation = false;
} ;


Expand Down
134 changes: 134 additions & 0 deletions include/FileBrowserSearcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* FileBrowserSearcher.h - Batch processor for searching the filesystem
*
* Copyright (c) 2023 saker <[email protected]>
*
* This file is part of LMMS - https://lmms.io
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#ifndef LMMS_FILE_BROWSER_SEARCHER_H
#define LMMS_FILE_BROWSER_SEARCHER_H

#include <QHash>
#include <QString>
#include <QStringList>
#include <optional>
#include <queue>

#ifdef __MINGW32__
#include <mingw.condition_variable.h>
#include <mingw.mutex.h>
#include <mingw.thread.h>
#else
#include <condition_variable>
#include <mutex>
#include <thread>
#endif

namespace lmms::gui {
class FileBrowserSearcher
{
public:
static constexpr int s_batchSize = 16;
static constexpr int s_millisecondsPerBatch = 500;

//! The future that will be returned to the requester, as well as read and modified by both the requester and
//! worker thread. Acessing the filter, paths to search, and valid file extensions can be done concurrently since
//! they do not change after construction. Access to the batch queue is protected under a mutex, while the state
//! variable is atomic.
class SearchFuture
{
public:
//! The state for this future. The state of this object can be read/written atomically. In a cancelled
//! state, the requester doesn't need to continue using this future and can abort any operations made using it.
//! Once in a completed or cancelled state, the worker thread no longer has shared ownership of this future
//! (i.e., all ownership will go to the requester).
enum class State
{
Idle,
Running,
Cancelled,
Completed
};

SearchFuture(const QString& filter, const QStringList& paths, const QStringList& extensions)
: m_filter(filter)
, m_paths(paths)
, m_extensions(extensions)
{
}

//! Read the current state of the future object. This function can run concurrently with the worker thread.
auto state() -> State { return m_state; }

//! Read a batch of search results. This function can run concurrently with the worker thread. Returns a empty
//! list if no batches are available.
auto batch() -> QStringList;

auto filter() -> const QString& { return m_filter; }
auto paths() -> const QStringList& { return m_paths; }
auto extensions() -> const QStringList& { return m_extensions; }

private:
auto addBatch(QStringList& matches) -> void;

QString m_filter;
QStringList m_paths;
QStringList m_extensions;

std::list<QStringList> m_batchQueue;
std::mutex m_batchQueueMutex;

std::atomic<State> m_state = State::Idle;

friend FileBrowserSearcher;
};

~FileBrowserSearcher();

//! Enqueues a search to be ran by the worker thread.
//! Returns a future that the caller can use to track state and results of the operation.
auto search(const QString& filter, const QStringList& paths, const QStringList& extensions)
-> std::shared_ptr<SearchFuture>;

//! Sends a signal to cancel a running search.
auto cancel() -> void { m_cancelRunningSearch = true; }

//! Returns the global instance of the searcher object.
static auto instance() -> FileBrowserSearcher*
{
static auto s_instance = FileBrowserSearcher{};
return &s_instance;
}

private:
auto run() -> void;
auto process(std::shared_ptr<SearchFuture> searchFuture) -> void;

std::queue<std::shared_ptr<SearchFuture>> m_searchQueue;
std::atomic<bool> m_cancelRunningSearch = false;

bool m_workerStopped = false;
std::mutex m_workerMutex;
std::condition_variable m_workerCond;
std::thread m_worker{[this] { run(); }};
};
} // namespace lmms::gui

#endif // LMMS_FILE_BROWSER_SEARCHER_H
1 change: 1 addition & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ SET(LMMS_SRCS
gui/EffectView.cpp
gui/embed.cpp
gui/FileBrowser.cpp
gui/FileBrowserSearcher.cpp
gui/GuiApplication.cpp
gui/LadspaControlView.cpp
gui/LfoControllerDialog.cpp
Expand Down
Loading