mirror of
https://github.com/YACReader/yacreader
synced 2025-05-28 03:10:27 -04:00
Debounce input from the search edit
This makes writing there a little bit more pleasant
This commit is contained in:
parent
c30e1c6829
commit
16faacec65
@ -264,6 +264,7 @@ include(./comic_vine/comic_vine.pri)
|
|||||||
include(../third_party/QsLog/QsLog.pri)
|
include(../third_party/QsLog/QsLog.pri)
|
||||||
include(../shortcuts_management/shortcuts_management.pri)
|
include(../shortcuts_management/shortcuts_management.pri)
|
||||||
include(../third_party/QrCode/QrCode.pri)
|
include(../third_party/QrCode/QrCode.pri)
|
||||||
|
include(../third_party/KDToolBox/KDToolBox.pri)
|
||||||
|
|
||||||
RESOURCES += images.qrc files.qrc
|
RESOURCES += images.qrc files.qrc
|
||||||
win32:RESOURCES += images_win.qrc
|
win32:RESOURCES += images_win.qrc
|
||||||
|
@ -95,6 +95,8 @@ extern YACReaderHttpServer *httpServer;
|
|||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <KDSignalThrottler.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template<class Remover>
|
template<class Remover>
|
||||||
void moveAndConnectRemoverToThread(Remover *remover, QThread *thread)
|
void moveAndConnectRemoverToThread(Remover *remover, QThread *thread)
|
||||||
@ -1281,11 +1283,20 @@ void LibraryWindow::createConnections()
|
|||||||
connect(optionsDialog, &YACReaderOptionsDialog::optionsChanged, this, &LibraryWindow::reloadOptions);
|
connect(optionsDialog, &YACReaderOptionsDialog::optionsChanged, this, &LibraryWindow::reloadOptions);
|
||||||
connect(optionsDialog, &YACReaderOptionsDialog::editShortcuts, editShortcutsDialog, &QWidget::show);
|
connect(optionsDialog, &YACReaderOptionsDialog::editShortcuts, editShortcutsDialog, &QWidget::show);
|
||||||
|
|
||||||
|
auto searchDebouncer = new KDToolBox::KDSignalDebouncer(this);
|
||||||
|
searchDebouncer->setTimeout(400);
|
||||||
|
|
||||||
// Search filter
|
// Search filter
|
||||||
#ifdef Y_MAC_UI
|
#ifdef Y_MAC_UI
|
||||||
connect(searchEdit, &YACReaderMacOSXSearchLineEdit::filterChanged, this, &LibraryWindow::setSearchFilter);
|
connect(searchEdit, &YACReaderMacOSXSearchLineEdit::textChanged, searchDebouncer, &KDToolBox::KDSignalThrottler::throttle);
|
||||||
|
connect(searchDebouncer, &KDToolBox::KDSignalThrottler::triggered, this, [=] {
|
||||||
|
setSearchFilter(searchEdit->text());
|
||||||
|
});
|
||||||
#else
|
#else
|
||||||
connect(searchEdit, &YACReaderSearchLineEdit::filterChanged, this, &LibraryWindow::setSearchFilter);
|
connect(searchEdit, &YACReaderSearchLineEdit::textChanged, searchDebouncer, &KDToolBox::KDSignalThrottler::throttle);
|
||||||
|
connect(searchDebouncer, &KDToolBox::KDSignalThrottler::triggered, this, [=] {
|
||||||
|
setSearchFilter(searchEdit->text());
|
||||||
|
});
|
||||||
#endif
|
#endif
|
||||||
connect(&comicQueryResultProcessor, &ComicQueryResultProcessor::newData, this, &LibraryWindow::setComicSearchFilterData);
|
connect(&comicQueryResultProcessor, &ComicQueryResultProcessor::newData, this, &LibraryWindow::setComicSearchFilterData);
|
||||||
qRegisterMetaType<FolderItem *>("FolderItem *");
|
qRegisterMetaType<FolderItem *>("FolderItem *");
|
||||||
|
183
third_party/KDToolBox/KDSignalThrottler.cpp
vendored
Normal file
183
third_party/KDToolBox/KDSignalThrottler.cpp
vendored
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
** MIT License
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020-2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo
|
||||||
|
*<giuseppe.dangelo@kdab.com>
|
||||||
|
**
|
||||||
|
** This file is part of KDToolBox (https://github.com/KDAB/KDToolBox).
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
** of this software and associated documentation files (the "Software"), to deal
|
||||||
|
** in the Software without restriction, including without limitation the rights
|
||||||
|
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
** copies of the Software, ** and to permit persons to whom the Software is
|
||||||
|
** furnished to do so, subject to the following conditions:
|
||||||
|
**
|
||||||
|
** The above copyright notice and this permission notice (including the next paragraph)
|
||||||
|
** shall be included in all copies or substantial portions of the Software.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
** LIABILITY, WHETHER IN AN ACTION OF ** CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "KDSignalThrottler.h"
|
||||||
|
|
||||||
|
namespace KDToolBox
|
||||||
|
{
|
||||||
|
|
||||||
|
KDGenericSignalThrottler::KDGenericSignalThrottler(Kind kind, EmissionPolicy emissionPolicy, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_timer(this)
|
||||||
|
, m_kind(kind)
|
||||||
|
, m_emissionPolicy(emissionPolicy)
|
||||||
|
, m_hasPendingEmission(false)
|
||||||
|
{
|
||||||
|
// For leading throttlers we use a repeated timer. This is in order
|
||||||
|
// to catch the case where a signal is received by the throttler
|
||||||
|
// just after it emitted a throttled/debounced signal. Even if leading,
|
||||||
|
// it shouldn't re-emit immediately, as it would be too close to the previous one.
|
||||||
|
// So we keep the timer running, and stop it later if it times out
|
||||||
|
// with no intervening timeout() emitted by it.
|
||||||
|
switch (m_emissionPolicy)
|
||||||
|
{
|
||||||
|
case EmissionPolicy::Leading:
|
||||||
|
m_timer.setSingleShot(false);
|
||||||
|
break;
|
||||||
|
case EmissionPolicy::Trailing:
|
||||||
|
m_timer.setSingleShot(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
connect(&m_timer, &QTimer::timeout, this, &KDGenericSignalThrottler::maybeEmitTriggered);
|
||||||
|
}
|
||||||
|
|
||||||
|
KDGenericSignalThrottler::~KDGenericSignalThrottler()
|
||||||
|
{
|
||||||
|
maybeEmitTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
KDGenericSignalThrottler::Kind KDGenericSignalThrottler::kind() const
|
||||||
|
{
|
||||||
|
return m_kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
KDGenericSignalThrottler::EmissionPolicy KDGenericSignalThrottler::emissionPolicy() const
|
||||||
|
{
|
||||||
|
return m_emissionPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KDGenericSignalThrottler::timeout() const
|
||||||
|
{
|
||||||
|
return m_timer.interval();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDGenericSignalThrottler::setTimeout(int timeout)
|
||||||
|
{
|
||||||
|
if (m_timer.interval() == timeout)
|
||||||
|
return;
|
||||||
|
m_timer.setInterval(timeout);
|
||||||
|
Q_EMIT timeoutChanged(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDGenericSignalThrottler::setTimeout(std::chrono::milliseconds timeout)
|
||||||
|
{
|
||||||
|
setTimeout(int(timeout.count()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::TimerType KDGenericSignalThrottler::timerType() const
|
||||||
|
{
|
||||||
|
return m_timer.timerType();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDGenericSignalThrottler::setTimerType(Qt::TimerType timerType)
|
||||||
|
{
|
||||||
|
if (m_timer.timerType() == timerType)
|
||||||
|
return;
|
||||||
|
m_timer.setTimerType(timerType);
|
||||||
|
Q_EMIT timerTypeChanged(timerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDGenericSignalThrottler::throttle()
|
||||||
|
{
|
||||||
|
m_hasPendingEmission = true;
|
||||||
|
|
||||||
|
switch (m_emissionPolicy)
|
||||||
|
{
|
||||||
|
case EmissionPolicy::Leading:
|
||||||
|
// Emit only if we haven't emitted already. We know if that's
|
||||||
|
// the case by checking if the timer is running.
|
||||||
|
if (!m_timer.isActive())
|
||||||
|
emitTriggered();
|
||||||
|
break;
|
||||||
|
case EmissionPolicy::Trailing:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The timer is started in all cases. If we got a signal,
|
||||||
|
// and we're Leading, and we did emit because of that,
|
||||||
|
// then we don't re-emit when the timer fires (unless we get ANOTHER
|
||||||
|
// signal).
|
||||||
|
switch (m_kind)
|
||||||
|
{
|
||||||
|
case Kind::Throttler:
|
||||||
|
if (!m_timer.isActive())
|
||||||
|
m_timer.start(); // = actual start, not restart
|
||||||
|
break;
|
||||||
|
case Kind::Debouncer:
|
||||||
|
m_timer.start(); // = restart
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(m_timer.isActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDGenericSignalThrottler::maybeEmitTriggered()
|
||||||
|
{
|
||||||
|
if (m_hasPendingEmission)
|
||||||
|
emitTriggered();
|
||||||
|
else
|
||||||
|
m_timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDGenericSignalThrottler::emitTriggered()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_hasPendingEmission);
|
||||||
|
m_hasPendingEmission = false;
|
||||||
|
Q_EMIT triggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience
|
||||||
|
|
||||||
|
KDSignalThrottler::KDSignalThrottler(QObject *parent)
|
||||||
|
: KDGenericSignalThrottler(Kind::Throttler, EmissionPolicy::Trailing, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KDSignalThrottler::~KDSignalThrottler() = default;
|
||||||
|
|
||||||
|
KDSignalLeadingThrottler::KDSignalLeadingThrottler(QObject *parent)
|
||||||
|
: KDGenericSignalThrottler(Kind::Throttler, EmissionPolicy::Leading, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KDSignalLeadingThrottler::~KDSignalLeadingThrottler() = default;
|
||||||
|
|
||||||
|
KDSignalDebouncer::KDSignalDebouncer(QObject *parent)
|
||||||
|
: KDGenericSignalThrottler(Kind::Debouncer, EmissionPolicy::Trailing, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KDSignalDebouncer::~KDSignalDebouncer() = default;
|
||||||
|
|
||||||
|
KDSignalLeadingDebouncer::KDSignalLeadingDebouncer(QObject *parent)
|
||||||
|
: KDGenericSignalThrottler(Kind::Debouncer, EmissionPolicy::Leading, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KDSignalLeadingDebouncer::~KDSignalLeadingDebouncer() = default;
|
||||||
|
|
||||||
|
} // namespace KDToolBox
|
130
third_party/KDToolBox/KDSignalThrottler.h
vendored
Normal file
130
third_party/KDToolBox/KDSignalThrottler.h
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
** MIT License
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020-2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo
|
||||||
|
*<giuseppe.dangelo@kdab.com>
|
||||||
|
**
|
||||||
|
** This file is part of KDToolBox (https://github.com/KDAB/KDToolBox).
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
** of this software and associated documentation files (the "Software"), to deal
|
||||||
|
** in the Software without restriction, including without limitation the rights
|
||||||
|
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
** copies of the Software, ** and to permit persons to whom the Software is
|
||||||
|
** furnished to do so, subject to the following conditions:
|
||||||
|
**
|
||||||
|
** The above copyright notice and this permission notice (including the next paragraph)
|
||||||
|
** shall be included in all copies or substantial portions of the Software.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
** LIABILITY, WHETHER IN AN ACTION OF ** CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef KDSIGNALTHROTTLER_H
|
||||||
|
#define KDSIGNALTHROTTLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace KDToolBox
|
||||||
|
{
|
||||||
|
|
||||||
|
class KDGenericSignalThrottler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(Kind kind READ kind CONSTANT)
|
||||||
|
Q_PROPERTY(EmissionPolicy emissionPolicy READ emissionPolicy CONSTANT)
|
||||||
|
Q_PROPERTY(int timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged)
|
||||||
|
Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType NOTIFY timerTypeChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class Kind
|
||||||
|
{
|
||||||
|
Throttler,
|
||||||
|
Debouncer,
|
||||||
|
};
|
||||||
|
Q_ENUM(Kind)
|
||||||
|
|
||||||
|
enum class EmissionPolicy
|
||||||
|
{
|
||||||
|
Trailing,
|
||||||
|
Leading,
|
||||||
|
};
|
||||||
|
Q_ENUM(EmissionPolicy)
|
||||||
|
|
||||||
|
explicit KDGenericSignalThrottler(Kind kind, EmissionPolicy emissionPolicy, QObject *parent = nullptr);
|
||||||
|
~KDGenericSignalThrottler() override;
|
||||||
|
|
||||||
|
Kind kind() const;
|
||||||
|
EmissionPolicy emissionPolicy() const;
|
||||||
|
|
||||||
|
int timeout() const;
|
||||||
|
void setTimeout(int timeout);
|
||||||
|
void setTimeout(std::chrono::milliseconds timeout);
|
||||||
|
|
||||||
|
Qt::TimerType timerType() const;
|
||||||
|
void setTimerType(Qt::TimerType timerType);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void throttle();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void triggered();
|
||||||
|
void timeoutChanged(int timeout);
|
||||||
|
void timerTypeChanged(Qt::TimerType timerType);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void maybeEmitTriggered();
|
||||||
|
void emitTriggered();
|
||||||
|
|
||||||
|
QTimer m_timer;
|
||||||
|
Kind m_kind;
|
||||||
|
EmissionPolicy m_emissionPolicy;
|
||||||
|
bool m_hasPendingEmission;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convenience subclasses, e.g. for registering into QML
|
||||||
|
|
||||||
|
class KDSignalThrottler : public KDGenericSignalThrottler
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit KDSignalThrottler(QObject *parent = nullptr);
|
||||||
|
~KDSignalThrottler() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KDSignalLeadingThrottler : public KDGenericSignalThrottler
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit KDSignalLeadingThrottler(QObject *parent = nullptr);
|
||||||
|
~KDSignalLeadingThrottler() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KDSignalDebouncer : public KDGenericSignalThrottler
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit KDSignalDebouncer(QObject *parent = nullptr);
|
||||||
|
~KDSignalDebouncer() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KDSignalLeadingDebouncer : public KDGenericSignalThrottler
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit KDSignalLeadingDebouncer(QObject *parent = nullptr);
|
||||||
|
~KDSignalLeadingDebouncer() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace KDToolBox
|
||||||
|
|
||||||
|
#endif // KDSIGNALTHROTTLER_H
|
4
third_party/KDToolBox/KDToolBox.pri
vendored
Normal file
4
third_party/KDToolBox/KDToolBox.pri
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
SOURCES += $$PWD/KDSignalThrottler.cpp
|
||||||
|
HEADERS += $$PWD/KDSignalThrottler.h
|
Loading…
Reference in New Issue
Block a user