summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBe <be@mixxx.org>2020-10-29 18:07:48 -0500
committerGitHub <noreply@github.com>2020-10-29 18:07:48 -0500
commit524c41a79679ca169f28f315fd34ca129a133171 (patch)
tree67f463d95fdfa1da004ee89a048011ce520ff956
parent26d27fb116f06d6d19b1d4e158ab4a8cbd85351a (diff)
parent9e29613ef50f544159cbd31a0142aef49d038472 (diff)
Merge pull request #3171 from poelzi/searchbox-combo
Use a combobox instead of a lineedit widget for library search history
-rw-r--r--res/skins/LateNight/style.qss2
-rw-r--r--src/controllers/controlpickermenu.cpp24
-rw-r--r--src/library/librarycontrol.cpp57
-rw-r--r--src/library/librarycontrol.h5
-rw-r--r--src/preferences/dialog/dlgpreflibrary.cpp2
-rw-r--r--src/widget/wsearchlineedit.cpp233
-rw-r--r--src/widget/wsearchlineedit.h22
7 files changed, 299 insertions, 46 deletions
diff --git a/res/skins/LateNight/style.qss b/res/skins/LateNight/style.qss
index 1bf52d253e..c8257c4527 100644
--- a/res/skins/LateNight/style.qss
+++ b/res/skins/LateNight/style.qss
@@ -15,7 +15,6 @@ WTrackText,
WTrackProperty,
WBeatSpinBox,
QSpinBox,
-QComboBox,
WLibrary QHeaderView,
WLibrary QHeaderView::item,
QToolTip,
@@ -27,6 +26,7 @@ WCoverArtMenu,
WTrackMenu,
WTrackMenu QMenu,
WOverview /* Hotcue labels in the overview */,
+WBeatSpinBox, #spinBoxTransition,
WEffectSelector,
WEffectSelector QAbstractScrollArea,
#fadeModeCombobox,
diff --git a/src/controllers/controlpickermenu.cpp b/src/controllers/controlpickermenu.cpp
index 79075386e1..912240f12e 100644
--- a/src/controllers/controlpickermenu.cpp
+++ b/src/controllers/controlpickermenu.cpp
@@ -645,6 +645,30 @@ ControlPickerMenu::ControlPickerMenu(QWidget* pParent)
tr("Replace Auto DJ Queue with selected tracks"),
libraryMenu, false, m_libraryStr);
libraryMenu->addSeparator();
+ addControl("[Library]",
+ "search_history_next",
+ tr("Select next search history"),
+ tr("Selects the next search history entry"),
+ libraryMenu,
+ false,
+ m_libraryStr);
+ addControl("[Library]",
+ "search_history_prev",
+ tr("Select previous search history"),
+ tr("Selects the previous search history entry"),
+ libraryMenu,
+ false,
+ m_libraryStr);
+ addControl("[Library]",
+ "search_history_selector",
+ tr("Move selected search entry"),
+ tr("Moves the selected search history item into given direction "
+ "and steps"),
+ libraryMenu,
+ false,
+ m_libraryStr);
+
+ libraryMenu->addSeparator();
addControl("[Recording]", "toggle_recording",
tr("Record Mix"),
tr("Toggle mix recording"),
diff --git a/src/library/librarycontrol.cpp b/src/library/librarycontrol.cpp
index a4a71b45ed..8bd0df6116 100644
--- a/src/library/librarycontrol.cpp
+++ b/src/library/librarycontrol.cpp
@@ -211,6 +211,47 @@ LibraryControl::LibraryControl(Library* pLibrary)
this,
&LibraryControl::slotTrackColorNext);
+ // Control to navigate between widgets (tab/shit+tab button)
+ m_pSelectHistoryNext = std::make_unique<ControlPushButton>(
+ ConfigKey("[Library]", "search_history_next"));
+ m_pSelectHistoryPrev = std::make_unique<ControlPushButton>(
+ ConfigKey("[Library]", "search_history_prev"));
+ m_pSelectHistorySelect = std::make_unique<ControlEncoder>(
+ ConfigKey("[Library]", "search_history_selector"), false);
+ connect(m_pSelectHistoryNext.get(),
+ &ControlPushButton::valueChanged,
+ this,
+ [this](double value) {
+ VERIFY_OR_DEBUG_ASSERT(m_pSearchbox) {
+ return;
+ }
+ if (value >= 1.0) {
+ m_pSearchbox->slotMoveSelectedHistory(1);
+ }
+ });
+ connect(m_pSelectHistoryPrev.get(),
+ &ControlPushButton::valueChanged,
+ this,
+ [this](double value) {
+ VERIFY_OR_DEBUG_ASSERT(m_pSearchbox) {
+ return;
+ }
+ if (value >= 1.0) {
+ m_pSearchbox->slotMoveSelectedHistory(-1);
+ }
+ });
+ connect(m_pSelectHistorySelect.get(),
+ &ControlEncoder::valueChanged,
+ this,
+ [this](double steps) {
+ VERIFY_OR_DEBUG_ASSERT(m_pSearchbox) {
+ return;
+ }
+ if (steps >= 1.0 || steps <= -1.0) {
+ m_pSearchbox->slotMoveSelectedHistory(static_cast<int>(steps));
+ }
+ });
+
/// Deprecated controls
m_pSelectNextTrack = std::make_unique<ControlPushButton>(ConfigKey("[Playlist]", "SelectNextTrack"));
connect(m_pSelectNextTrack.get(),
@@ -537,19 +578,26 @@ void LibraryControl::emitKeyEvent(QKeyEvent&& event) {
VERIFY_OR_DEBUG_ASSERT(m_pLibraryWidget) {
return;
}
+ VERIFY_OR_DEBUG_ASSERT(m_pSearchbox) {
+ return;
+ }
if (!QApplication::focusWindow()) {
qDebug() << "Mixxx window is not focused, don't send key events";
return;
}
- bool keyIsTab = event.key() == static_cast<int>(Qt::Key_Tab);
+ bool keyIsTab = event.key() == Qt::Key_Tab;
+ bool keyIsUpDown = event.key() == Qt::Key_Up || event.key() == Qt::Key_Down;
// If the main window has focus, any widget can receive Tab.
// Other keys should be sent to library widgets only to not
// accidentally alter spinboxes etc.
+ // If the searchbox has focus allow only Up/Down to select previous queries.
if (!keyIsTab && !m_pSidebarWidget->hasFocus()
&& !m_pLibraryWidget->getActiveView()->hasFocus()) {
- setLibraryFocus();
+ if (keyIsUpDown && !m_pSearchbox->hasFocus()) {
+ setLibraryFocus();
+ }
}
if (keyIsTab && !QApplication::focusWidget()){
setLibraryFocus();
@@ -652,6 +700,11 @@ void LibraryControl::slotGoToItem(double v) {
return activeView->loadSelectedTrack();
}
+ // If searchbox has focus jump to the tracks table
+ if (m_pSearchbox->hasFocus()) {
+ return setLibraryFocus();
+ }
+
// Clear the search if the searchbox has focus
emit clearSearchIfClearButtonHasFocus();
diff --git a/src/library/librarycontrol.h b/src/library/librarycontrol.h
index dd8046c89e..84d70811c9 100644
--- a/src/library/librarycontrol.h
+++ b/src/library/librarycontrol.h
@@ -144,6 +144,11 @@ class LibraryControl : public QObject {
std::unique_ptr<ControlPushButton> m_pTrackColorPrev;
std::unique_ptr<ControlPushButton> m_pTrackColorNext;
+ // Controls to navigate search history
+ std::unique_ptr<ControlPushButton> m_pSelectHistoryNext;
+ std::unique_ptr<ControlPushButton> m_pSelectHistoryPrev;
+ std::unique_ptr<ControlEncoder> m_pSelectHistorySelect;
+
// Font sizes
std::unique_ptr<ControlPushButton> m_pFontSizeIncrement;
std::unique_ptr<ControlPushButton> m_pFontSizeDecrement;
diff --git a/src/preferences/dialog/dlgpreflibrary.cpp b/src/preferences/dialog/dlgpreflibrary.cpp
index fad1406ef0..26f4ad356a 100644
--- a/src/preferences/dialog/dlgpreflibrary.cpp
+++ b/src/preferences/dialog/dlgpreflibrary.cpp
@@ -278,6 +278,8 @@ void DlgPrefLibrary::slotRemoveDir() {
} else if (removeMsgBox.clickedButton() == deleteAllButton) {
removalType = Library::RemovalType::PurgeTracks;
} else {
+ // Only used in DEBUG_ASSERT
+ Q_UNUSED(leaveUnchangedButton);
DEBUG_ASSERT(removeMsgBox.clickedButton() == leaveUnchangedButton);
removalType = Library::RemovalType::KeepTracks;
}
diff --git a/src/widget/wsearchlineedit.cpp b/src/widget/wsearchlineedit.cpp
index ca004c5600..70c627fba5 100644
--- a/src/widget/wsearchlineedit.cpp
+++ b/src/widget/wsearchlineedit.cpp
@@ -1,15 +1,17 @@
+#include "wsearchlineedit.h"
+
+#include <QAbstractItemView>
#include <QFont>
+#include <QLineEdit>
#include <QShortcut>
+#include <QSizePolicy>
#include <QStyle>
-#include "wsearchlineedit.h"
-#include "wskincolor.h"
-#include "wwidget.h"
-
#include "skin/skincontext.h"
-
#include "util/assert.h"
#include "util/logger.h"
+#include "wskincolor.h"
+#include "wwidget.h"
#define ENABLE_TRACE_LOG false
@@ -23,11 +25,19 @@ const QString kEmptySearch = QStringLiteral("");
const QString kDisabledText = QStringLiteral("- - -");
-inline QString clearButtonStyleSheet(int pxPaddingRight) {
- DEBUG_ASSERT(pxPaddingRight >= 0);
- return QString(
- QStringLiteral("QLineEdit { padding-right: %1px; }"))
- .arg(pxPaddingRight);
+constexpr int kClearButtonClearence = 1;
+
+inline QString clearButtonStyleSheet(int pxPadding, Qt::LayoutDirection direction) {
+ DEBUG_ASSERT(pxPadding >= 0);
+ if (direction == Qt::RightToLeft) {
+ return QString(
+ QStringLiteral("WSearchLineEdit { padding-left: %1px; }"))
+ .arg(pxPadding);
+ } else {
+ return QString(
+ QStringLiteral("WSearchLineEdit { padding-right: %1px; }"))
+ .arg(pxPadding);
+ }
}
int verifyDebouncingTimeoutMillis(int debouncingTimeoutMillis) {
@@ -52,6 +62,12 @@ constexpr int WSearchLineEdit::kDefaultDebouncingTimeoutMillis;
constexpr int WSearchLineEdit::kMaxDebouncingTimeoutMillis;
//static
+constexpr int WSearchLineEdit::kSaveTimeoutMillis;
+
+//static
+constexpr int WSearchLineEdit::kMaxSearchEntries;
+
+//static
int WSearchLineEdit::s_debouncingTimeoutMillis = kDefaultDebouncingTimeoutMillis;
//static
@@ -60,31 +76,36 @@ void WSearchLineEdit::setDebouncingTimeoutMillis(int debouncingTimeoutMillis) {
}
WSearchLineEdit::WSearchLineEdit(QWidget* pParent)
- : QLineEdit(pParent),
- WBaseWidget(this),
- m_clearButton(make_parented<QToolButton>(this)) {
+ : QComboBox(pParent),
+ WBaseWidget(this),
+ m_clearButton(make_parented<QToolButton>(this)) {
DEBUG_ASSERT(kEmptySearch.isEmpty());
DEBUG_ASSERT(!kEmptySearch.isNull());
setAcceptDrops(false);
+ setEditable(true);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ setIconSize(QSize(0, 0));
+ setInsertPolicy(QComboBox::InsertAtTop);
+ setMinimumSize(0, 0);
+ setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon);
//: Shown in the library search bar when it is empty.
- setPlaceholderText(tr("Search..."));
+ lineEdit()->setPlaceholderText(tr("Search..."));
+ installEventFilter(this);
+ view()->installEventFilter(this);
m_clearButton->setCursor(Qt::ArrowCursor);
m_clearButton->setObjectName(QStringLiteral("SearchClearButton"));
- // Assume the qss border is at least 1px wide
- m_frameWidth = 1;
+ // Query style for arrow width and frame border
+ updateStyleMetrics();
+
m_clearButton->hide();
connect(m_clearButton,
&QAbstractButton::clicked,
this,
&WSearchLineEdit::slotClearSearch);
- // This prevents the searchbox from being focused by Tab key (real or emulated)
- // so it is skipped when using the library controls 'MoveFocus[...]'
- // The Clear button can still be focused by Tab.
- setFocusPolicy(Qt::ClickFocus);
QShortcut* setFocusShortcut = new QShortcut(QKeySequence(tr("Ctrl+F", "Search|Focus")), this);
connect(setFocusShortcut,
&QShortcut::activated,
@@ -94,17 +115,26 @@ WSearchLineEdit::WSearchLineEdit(QWidget* pParent)
// Set up a timer to search after a few hundred milliseconds timeout. This
// stops us from thrashing the database if you type really fast.
m_debouncingTimer.setSingleShot(true);
+ m_saveTimer.setSingleShot(true);
connect(&m_debouncingTimer,
&QTimer::timeout,
this,
&WSearchLineEdit::slotTriggerSearch);
+ connect(&m_saveTimer,
+ &QTimer::timeout,
+ this,
+ &WSearchLineEdit::slotSaveSearch);
connect(this,
- &QLineEdit::textChanged,
+ &QComboBox::currentTextChanged,
this,
&WSearchLineEdit::slotTextChanged);
+ connect(this,
+ QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this,
+ &WSearchLineEdit::slotIndexChanged);
// When you hit enter, it will trigger or clear the search.
- connect(this,
+ connect(this->lineEdit(),
&QLineEdit::returnPressed,
this,
[this] {
@@ -116,7 +146,9 @@ WSearchLineEdit::WSearchLineEdit(QWidget* pParent)
QSize clearButtonSize = m_clearButton->sizeHint();
// Ensures the text does not obscure the clear image.
- setStyleSheet(clearButtonStyleSheet(clearButtonSize.width() + m_frameWidth + 1));
+ setStyleSheet(clearButtonStyleSheet(
+ clearButtonSize.width() + m_frameWidth + kClearButtonClearence,
+ layoutDirection()));
refreshState();
}
@@ -189,17 +221,33 @@ void WSearchLineEdit::setup(const QDomNode& node, const SkinContext& context) {
setToolTip(tr("Search", "noun") + "\n" +
tr("Enter a string to search for") + "\n" +
- tr("Use operators like bpm:115-128, artist:BooFar, -year:1990") + "\n" +
- tr("For more information see User Manual > Mixxx Library") + "\n\n" +
-
- tr("Shortcut") + ": \n" +
- tr("Ctrl+F") + " " + tr("Focus", "Give search bar input focus") + "\n" +
- tr("Ctrl+Backspace") + " " + tr("Clear input", "Clear the search bar input field") + "\n" +
+ tr("Use operators like bpm:115-128, artist:BooFar, -year:1990") +
+ "\n" + tr("For more information see User Manual > Mixxx Library") +
+ "\n\n" +
+ tr("Shortcut") + ": \n" + tr("Ctrl+F") + " " +
+ tr("Focus", "Give search bar input focus") + "\n" +
+ tr("Ctrl+Backspace") + " " +
+ tr("Clear input", "Clear the search bar input field") + "\n" +
+ tr("Ctrl+Space") + " " +
+ tr("Toggle search history",
+ "Shows/hides the search history entries") +
+ "\n" +
tr("Esc") + " " + tr("Exit search", "Exit search bar and leave focus"));
}
+void WSearchLineEdit::updateStyleMetrics() {
+ QStyleOptionComboBox styleArrow;
+ styleArrow.initFrom(this);
+ QRect rectArrow(style()->subControlRect(
+ QStyle::CC_ComboBox, &styleArrow, QStyle::SC_ComboBoxArrow, this));
+
+ m_dropButtonWidth = rectArrow.width() + 1;
+ m_frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this);
+}
+
void WSearchLineEdit::resizeEvent(QResizeEvent* e) {
- QLineEdit::resizeEvent(e);
+ QComboBox::resizeEvent(e);
+ updateStyleMetrics();
m_innerHeight = this->height() - 2 * m_frameWidth;
// Test if this is a vertical resize due to changed library font.
// Assuming current button height is innerHeight from last resize,
@@ -214,27 +262,64 @@ void WSearchLineEdit::resizeEvent(QResizeEvent* e) {
}
int top = rect().top() + m_frameWidth;
if (layoutDirection() == Qt::LeftToRight) {
- m_clearButton->move(rect().right() - m_innerHeight - m_frameWidth, top);
+ m_clearButton->move(rect().right() - m_innerHeight - m_frameWidth - m_dropButtonWidth, top);
} else {
- m_clearButton->move(m_frameWidth, top);
+ m_clearButton->move(m_frameWidth + m_dropButtonWidth, top);
}
}
QString WSearchLineEdit::getSearchText() const {
if (isEnabled()) {
- DEBUG_ASSERT(!text().isNull());
- return text();
+ DEBUG_ASSERT(!currentText().isNull());
+ return currentText();
} else {
return QString();
}
}
+bool WSearchLineEdit::eventFilter(QObject* obj, QEvent* event) {
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+ if (keyEvent->key() == Qt::Key_Down) {
+ // after clearing the text field the down key is expected to
+ // show the last entry
+ if (currentText().isEmpty()) {
+ setCurrentIndex(0);
+ return true;
+ }
+ // in case the user entered a new search query
+ // und presses the down key, save the query for later recall
+ if (findCurrentTextIndex() == -1) {
+ slotSaveSearch();
+ }
+ } else if (keyEvent->key() == Qt::Key_Enter) {
+ if (findCurrentTextIndex() == -1) {
+ slotSaveSearch();
+ }
+ // The default handler will add the entry to the list,
+ // this already happened in slotSaveSearch
+ slotTriggerSearch();
+ return true;
+ } else if (keyEvent->key() == Qt::Key_Space &&
+ keyEvent->modifiers() == Qt::ControlModifier) {
+ // open popup on ctrl + space
+ if (view()->isVisible()) {
+ hidePopup();
+ } else {
+ showPopup();
+ }
+ return true;
+ }
+ }
+ return QComboBox::eventFilter(obj, event);
+}
+
void WSearchLineEdit::focusInEvent(QFocusEvent* event) {
#if ENABLE_TRACE_LOG
kLogger.trace()
<< "focusInEvent";
#endif // ENABLE_TRACE_LOG
- QLineEdit::focusInEvent(event);
+ QComboBox::focusInEvent(event);
}
void WSearchLineEdit::focusOutEvent(QFocusEvent* event) {
@@ -242,7 +327,7 @@ void WSearchLineEdit::focusOutEvent(QFocusEvent* event) {
kLogger.trace()
<< "focusOutEvent";
#endif // ENABLE_TRACE_LOG
- QLineEdit::focusOutEvent(event);
+ QComboBox::focusOutEvent(event);
if (m_debouncingTimer.isActive()) {
// Trigger a pending search before leaving the edit box.
// Otherwise the entered text might be ignored and get lost
@@ -258,7 +343,7 @@ void WSearchLineEdit::setTextBlockSignals(const QString& text) {
<< text;
#endif // ENABLE_TRACE_LOG
blockSignals(true);
- setText(text);
+ setCurrentText(text);
blockSignals(false);
}
@@ -294,6 +379,8 @@ void WSearchLineEdit::slotRestoreSearch(const QString& text) {
if (text.isNull()) {
slotDisableSearch();
} else {
+ // we save the current search before we switch to a new text
+ slotSaveSearch();
enableSearch(text);
}
}
@@ -309,6 +396,42 @@ void WSearchLineEdit::slotTriggerSearch() {
emit search(getSearchText());
}
+/// saves the current query as selection
+void WSearchLineEdit::slotSaveSearch() {
+ int cIndex = findCurrentTextIndex();
+#if ENABLE_TRACE_LOG
+ kLogger.trace()
+ << "save search. Index: "
+ << cIndex;
+#endif // ENABLE_TRACE_LOG
+ m_saveTimer.stop();
+ // entry already exists and is on top
+ if (cIndex == 0) {
+ return;
+ }
+ if (!currentText().isEmpty() && isEnabled()) {
+ // we remove the existing item and add a new one at the top
+ if (cIndex != -1) {
+ removeItem(cIndex);
+ }
+ insertItem(0, currentText());
+ setCurrentIndex(0);
+ while (count() > kMaxSearchEntries) {
+ removeItem(kMaxSearchEntries);
+ }
+ }
+}
+
+void WSearchLineEdit::slotMoveSelectedHistory(int steps) {
+ int nIndex = currentIndex() + steps;
+ // we wrap around to the last entry on backwards direction
+ if (nIndex < -1) {
+ nIndex = count() - 1;
+ }
+ setCurrentIndex(nIndex);
+ m_saveTimer.stop();
+}
+
void WSearchLineEdit::refreshState() {
#if ENABLE_TRACE_LOG
kLogger.trace()
@@ -321,6 +444,17 @@ void WSearchLineEdit::refreshState() {
}
}
+void WSearchLineEdit::showPopup() {
+ int cIndex = findCurrentTextIndex();
+ if (cIndex == -1) {
+ slotSaveSearch();
+ } else {
+ m_saveTimer.stop();
+ setCurrentIndex(cIndex);
+ }
+ QComboBox::showPopup();
+}
+
void WSearchLineEdit::updateEditBox(const QString& text) {
#if ENABLE_TRACE_LOG
kLogger.trace()
@@ -352,12 +486,15 @@ void WSearchLineEdit::updateClearButton(const QString& text) {
// Disable while placeholder is shown
m_clearButton->setVisible(false);
// no right padding
- setStyleSheet(clearButtonStyleSheet(0));
+ setStyleSheet(clearButtonStyleSheet(0, layoutDirection()));
} else {
// Enable otherwise
m_clearButton->setVisible(true);
// make sure the text won't be drawn behind the Clear button icon
- setStyleSheet(clearButtonStyleSheet(m_innerHeight + m_frameWidth));
+ setStyleSheet(clearButtonStyleSheet(
+ m_innerHeight + m_dropButtonWidth +
+ m_frameWidth + kClearButtonClearence,
+ layoutDirection()));
}
}
@@ -365,7 +502,7 @@ bool WSearchLineEdit::event(QEvent* pEvent) {
if (pEvent->type() == QEvent::ToolTip) {
updateTooltip();
}
- return QLineEdit::event(pEvent);
+ return QComboBox::event(pEvent);
}
void WSearchLineEdit::slotClearSearch() {
@@ -374,12 +511,15 @@ void WSearchLineEdit::slotClearSearch() {
<< "slotClearSearch";
#endif // ENABLE_TRACE_LOG
DEBUG_ASSERT(isEnabled());
+ // select the last entry as current before cleaning the text field
+ // so arrow keys will work as expected
+ setCurrentIndex(-1);
// Clearing the edit field will engage the debouncing timer
// and gives the user the chance for entering a new search
// before returning the whole (and probably huge) library.
// No need to manually trigger a search at this point!
// See also: https://bugs.launchpad.net/mixxx/+bug/1635087
- setText(kEmptySearch);
+ setCurrentText(kEmptySearch);
// Refocus the edit field
setFocus(Qt::OtherFocusReason);
}
@@ -392,6 +532,12 @@ bool WSearchLineEdit::slotClearSearchIfClearButtonHasFocus() {
return true;
}
+void WSearchLineEdit::slotIndexChanged(int index) {
+ if (index != -1) {
+ m_saveTimer.stop();
+ }
+}
+
void WSearchLineEdit::slotTextChanged(const QString& text) {
#if ENABLE_TRACE_LOG
kLogger.trace()
@@ -413,6 +559,7 @@ void WSearchLineEdit::slotTextChanged(const QString& text) {
// to an invalid value is an expected and valid use case.
DEBUG_ASSERT(!m_debouncingTimer.isActive());
}
+ m_saveTimer.start(kSaveTimeoutMillis);
}
void WSearchLineEdit::slotSetShortcutFocus() {
@@ -421,5 +568,9 @@ void WSearchLineEdit::slotSetShortcutFocus() {
// Use the same font as the library table and the sidebar
void WSearchLineEdit::slotSetFont(const QFont& font) {
+ updateStyleMetrics();
setFont(font);
+ if (lineEdit()) {
+ lineEdit()->setFont(font);
+ }
}
diff --git a/src/widget/wsearchlineedit.h b/src/widget/wsearchlineedit.h
index f3807ce003..9709cc8a57 100644
--- a/src/widget/wsearchlineedit.h
+++ b/src/widget/wsearchlineedit.h
@@ -1,8 +1,8 @@
#pragma once
+#include <QComboBox>
#include <QDomNode>
#include <QEvent>
-#include <QLineEdit>
#include <QTimer>
#include <QToolButton>
@@ -11,16 +11,19 @@
class SkinContext;
-class WSearchLineEdit : public QLineEdit, public WBaseWidget {
+class WSearchLineEdit : public QComboBox, public WBaseWidget {
Q_OBJECT
public:
// Delay for triggering a search while typing
static constexpr int kMinDebouncingTimeoutMillis = 100;
static constexpr int kDefaultDebouncingTimeoutMillis = 300;
static constexpr int kMaxDebouncingTimeoutMillis = 9999;
+ static constexpr int kSaveTimeoutMillis = 5000;
+ static constexpr int kMaxSearchEntries = 50;
// TODO(XXX): Replace with a public slot
static void setDebouncingTimeoutMillis(int debouncingTimeoutMillis);
+ virtual void showPopup() override;
explicit WSearchLineEdit(QWidget* pParent);
~WSearchLineEdit() override = default;
@@ -32,6 +35,7 @@ class WSearchLineEdit : public QLineEdit, public WBaseWidget {
void focusInEvent(QFocusEvent*) override;
void focusOutEvent(QFocusEvent*) override;
bool event(QEvent*) override;
+ bool eventFilter(QObject* obj, QEvent* event) override;
signals:
void search(const QString& text);
@@ -45,11 +49,18 @@ class WSearchLineEdit : public QLineEdit, public WBaseWidget {
void slotClearSearch();
bool slotClearSearchIfClearButtonHasFocus();
+ /// The function selects an entry relative to the currently selected
+ /// entry in the history and executes the search.
+ /// The parameter specifies the distance in steps (positive/negative = downward/upward)
+ void slotMoveSelectedHistory(int steps);
+
private slots:
void slotSetShortcutFocus();
void slotTextChanged(const QString& text);
+ void slotIndexChanged(int index);
void slotTriggerSearch();
+ void slotSaveSearch();
private:
// TODO(XXX): This setting shouldn't be static and the widget
@@ -64,6 +75,11 @@ class WSearchLineEdit : public QLineEdit, public WBaseWidget {
void enableSearch(const QString& text);
void updateEditBox(const QString& text);
void updateClearButton(const QString& text);
+ void updateStyleMetrics();
+
+ inline int findCurrentTextIndex() {
+ return findData(currentText(), Qt::DisplayRole);
+ }
QString getSearchText() const;
@@ -74,6 +90,8 @@ class WSearchLineEdit : public QLineEdit, public WBaseWidget {
int m_frameWidth;
int m_innerHeight;
+ int m_dropButtonWidth;
QTimer m_debouncingTimer;
+ QTimer m_saveTimer;
};