mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge pull request #4955
TriKriSta (12): feat: add a button to search feat: add form for search feat: add text search feat: edit load history for search feat: remove search button and add line in context menu feat: add search in text in group chats feat: optimise search in history feat: add hot keys for search style: change code in searchInText fix: add search symbol ' in history refactor: rename hideButton to searchHideButton refactor: add/delete checks in search code
This commit is contained in:
commit
aeb501240f
|
@ -341,6 +341,8 @@ set(${PROJECT_NAME}_SOURCES
|
|||
src/widget/emoticonswidget.h
|
||||
src/widget/flowlayout.cpp
|
||||
src/widget/flowlayout.h
|
||||
src/widget/searchform.cpp
|
||||
src/widget/searchform.h
|
||||
src/widget/form/addfriendform.cpp
|
||||
src/widget/form/addfriendform.h
|
||||
src/widget/form/chatform.cpp
|
||||
|
|
6
res.qrc
6
res.qrc
|
@ -51,18 +51,18 @@
|
|||
<file>ui/chatArea/scrollBarDownArrow.svg</file>
|
||||
<file>ui/chatArea/scrollBarLeftArrow.svg</file>
|
||||
<file>ui/chatArea/scrollBarRightArrow.svg</file>
|
||||
|
||||
<file>ui/chatForm/buttons.css</file>
|
||||
<file>ui/chatForm/callButton.svg</file>
|
||||
<file>ui/chatForm/micButton.svg</file>
|
||||
<file>ui/chatForm/videoButton.svg</file>
|
||||
<file>ui/chatForm/volButton.svg</file>
|
||||
|
||||
<file>ui/chatForm/emoteButton.svg</file>
|
||||
<file>ui/chatForm/fileButton.svg</file>
|
||||
<file>ui/chatForm/screenshotButton.svg</file>
|
||||
<file>ui/chatForm/searchDownButton.svg</file>
|
||||
<file>ui/chatForm/searchHideButton.svg</file>
|
||||
<file>ui/chatForm/searchUpButton.svg</file>
|
||||
<file>ui/chatForm/sendButton.svg</file>
|
||||
|
||||
<file>ui/emoticonWidget/dot_page.svg</file>
|
||||
<file>ui/emoticonWidget/dot_page_current.svg</file>
|
||||
<file>ui/emoticonWidget/dot_page_hover.svg</file>
|
||||
|
|
|
@ -815,6 +815,8 @@ void ChatLog::onWorkerTimeout()
|
|||
|
||||
// hidden during busy screen
|
||||
verticalScrollBar()->show();
|
||||
|
||||
emit workerTimeoutFinished();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
|
||||
signals:
|
||||
void selectionChanged();
|
||||
void workerTimeoutFinished();
|
||||
|
||||
public slots:
|
||||
void forceRelayout();
|
||||
|
|
|
@ -58,6 +58,38 @@ void Text::setText(const QString& txt)
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
void Text::selectText(const QString &txt, const int index)
|
||||
{
|
||||
regenerate();
|
||||
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cursor = doc->find(txt, index);
|
||||
|
||||
if (!cursor.isNull()) {
|
||||
cursor.beginEditBlock();
|
||||
cursor.setPosition(index);
|
||||
cursor.setPosition(index + txt.size(), QTextCursor::KeepAnchor);
|
||||
cursor.endEditBlock();
|
||||
|
||||
QTextCharFormat format;
|
||||
format.setBackground(QBrush(QColor("#ff7626")));
|
||||
cursor.mergeCharFormat(format);
|
||||
|
||||
regenerate();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::deselectText()
|
||||
{
|
||||
dirty = true;
|
||||
regenerate();
|
||||
update();
|
||||
}
|
||||
|
||||
void Text::setWidth(qreal w)
|
||||
{
|
||||
width = w;
|
||||
|
|
|
@ -36,6 +36,8 @@ public:
|
|||
virtual ~Text();
|
||||
|
||||
void setText(const QString& txt);
|
||||
void selectText(const QString& txt, const int index);
|
||||
void deselectText();
|
||||
|
||||
virtual void setWidth(qreal width) final;
|
||||
|
||||
|
|
|
@ -326,6 +326,37 @@ QList<History::DateMessages> History::getChatHistoryCounts(const ToxPk& friendPk
|
|||
return counts;
|
||||
}
|
||||
|
||||
QDateTime History::getDateWhereFindPhrase(const QString& friendPk, const QDateTime& from, QString phrase)
|
||||
{
|
||||
QList<QDateTime> counts;
|
||||
auto rowCallback = [&counts](const QVector<QVariant>& row) {
|
||||
counts.append(QDateTime::fromMSecsSinceEpoch(row[0].toLongLong()));
|
||||
};
|
||||
|
||||
phrase.replace("'", "''");
|
||||
|
||||
QString queryText =
|
||||
QString("SELECT timestamp "
|
||||
"FROM history "
|
||||
"LEFT JOIN faux_offline_pending ON history.id = faux_offline_pending.id "
|
||||
"JOIN peers chat ON chat_id = chat.id "
|
||||
"WHERE chat.public_key='%1' "
|
||||
"AND message LIKE '%%2%' "
|
||||
"AND timestamp < '%3' ORDER BY timestamp DESC LIMIT 1;")
|
||||
.arg(friendPk)
|
||||
.arg(phrase)
|
||||
.arg(from.toMSecsSinceEpoch());
|
||||
|
||||
|
||||
db->execNow({queryText, rowCallback});
|
||||
|
||||
if (!counts.isEmpty()) {
|
||||
return counts[0];
|
||||
}
|
||||
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Marks a message as sent.
|
||||
* Removing message from the faux-offline pending messages list.
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
const QDateTime& to);
|
||||
|
||||
QList<DateMessages> getChatHistoryCounts(const ToxPk& friendPk, const QDate& from, const QDate& to);
|
||||
QDateTime getDateWhereFindPhrase(const QString& friendPk, const QDateTime& from, QString phrase);
|
||||
|
||||
void markAsSent(qint64 messageId);
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "src/widget/tool/screenshotgrabber.h"
|
||||
#include "src/widget/translator.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "src/widget/searchform.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFileDialog>
|
||||
|
@ -488,6 +489,52 @@ void ChatForm::onVolMuteToggle()
|
|||
updateMuteVolButton();
|
||||
}
|
||||
|
||||
void ChatForm::onSearchUp(const QString& phrase)
|
||||
{
|
||||
if (phrase.isEmpty()) {
|
||||
disableSearchText();
|
||||
}
|
||||
|
||||
QVector<ChatLine::Ptr> lines = chatWidget->getLines();
|
||||
int numLines = lines.size();
|
||||
|
||||
int startLine = numLines - searchPoint.x();
|
||||
|
||||
if (startLine == 0) {
|
||||
QString pk = f->getPublicKey().toString();
|
||||
QDateTime newBaseData = history->getDateWhereFindPhrase(pk, earliestMessage, phrase);
|
||||
|
||||
if (!newBaseData.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
searchAfterLoadHistory = true;
|
||||
loadHistory(newBaseData);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool isSearch = searchInText(phrase, true);
|
||||
|
||||
if (!isSearch) {
|
||||
QString pk = f->getPublicKey().toString();
|
||||
QDateTime newBaseData = history->getDateWhereFindPhrase(pk, earliestMessage, phrase);
|
||||
|
||||
if (!newBaseData.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
searchPoint.setX(numLines);
|
||||
searchAfterLoadHistory = true;
|
||||
loadHistory(newBaseData);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onSearchDown(const QString& phrase)
|
||||
{
|
||||
searchInText(phrase, false);
|
||||
}
|
||||
|
||||
void ChatForm::onFileSendFailed(uint32_t friendId, const QString& fname)
|
||||
{
|
||||
if (friendId != f->getId()) {
|
||||
|
@ -753,6 +800,10 @@ void ChatForm::loadHistory(const QDateTime& since, bool processUndelivered)
|
|||
chatWidget->insertChatlineOnTop(historyMessages);
|
||||
savedSliderPos = verticalBar->maximum() - savedSliderPos;
|
||||
verticalBar->setValue(savedSliderPos);
|
||||
|
||||
if (searchAfterLoadHistory && historyMessages.isEmpty()) {
|
||||
onContinueSearch();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onScreenshotClicked()
|
||||
|
|
|
@ -72,6 +72,10 @@ public slots:
|
|||
void onAvatarChange(uint32_t friendId, const QPixmap& pic);
|
||||
void onAvatarRemoved(uint32_t friendId);
|
||||
|
||||
protected slots:
|
||||
void onSearchUp(const QString& phrase) override;
|
||||
void onSearchDown(const QString& phrase) override;
|
||||
|
||||
private slots:
|
||||
void clearChatArea(bool notInForm) override final;
|
||||
void onSendTriggered() override;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "src/widget/tool/flyoutoverlaywidget.h"
|
||||
#include "src/widget/translator.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "src/widget/searchform.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFileDialog>
|
||||
|
@ -135,8 +136,10 @@ GenericChatForm::GenericChatForm(QWidget* parent)
|
|||
{
|
||||
curRow = 0;
|
||||
headWidget = new ChatFormHeader();
|
||||
searchForm = new SearchForm();
|
||||
chatWidget = new ChatLog(this);
|
||||
chatWidget->setBusyNotification(ChatMessage::createBusyNotification());
|
||||
searchForm->hide();
|
||||
|
||||
// settings
|
||||
const Settings& s = Settings::getInstance();
|
||||
|
@ -190,6 +193,7 @@ GenericChatForm::GenericChatForm(QWidget* parent)
|
|||
mainFootLayout->setSpacing(0);
|
||||
|
||||
QVBoxLayout* contentLayout = new QVBoxLayout(contentWidget);
|
||||
contentLayout->addWidget(searchForm);
|
||||
contentLayout->addWidget(chatWidget);
|
||||
contentLayout->addLayout(mainFootLayout);
|
||||
|
||||
|
@ -198,6 +202,12 @@ GenericChatForm::GenericChatForm(QWidget* parent)
|
|||
addAction(quoteAction);
|
||||
menu.addSeparator();
|
||||
|
||||
searchAction = menu.addAction(QIcon(), QString(), this, SLOT(searchFormShow()),
|
||||
QKeySequence(Qt::CTRL + Qt::Key_F));
|
||||
addAction(searchAction);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addActions(chatWidget->actions());
|
||||
menu.addSeparator();
|
||||
|
||||
|
@ -214,6 +224,13 @@ GenericChatForm::GenericChatForm(QWidget* parent)
|
|||
connect(chatWidget, &ChatLog::customContextMenuRequested, this,
|
||||
&GenericChatForm::onChatContextMenuRequested);
|
||||
|
||||
connect(searchForm, &SearchForm::searchInBegin, this, &GenericChatForm::searchInBegin);
|
||||
connect(searchForm, &SearchForm::searchUp, this, &GenericChatForm::onSearchUp);
|
||||
connect(searchForm, &SearchForm::searchDown, this, &GenericChatForm::onSearchDown);
|
||||
connect(searchForm, &SearchForm::visibleChanged, this, &GenericChatForm::onSearchTriggered);
|
||||
|
||||
connect(chatWidget, &ChatLog::workerTimeoutFinished, this, &GenericChatForm::onContinueSearch);
|
||||
|
||||
chatWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatArea.css"));
|
||||
headWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatHead.css"));
|
||||
|
||||
|
@ -231,6 +248,7 @@ GenericChatForm::GenericChatForm(QWidget* parent)
|
|||
GenericChatForm::~GenericChatForm()
|
||||
{
|
||||
Translator::unregister(this);
|
||||
delete searchForm;
|
||||
}
|
||||
|
||||
void GenericChatForm::adjustFileMenuPosition()
|
||||
|
@ -296,8 +314,13 @@ bool GenericChatForm::event(QEvent* e)
|
|||
if (e->type() == QEvent::KeyRelease && !msgEdit->hasFocus()) {
|
||||
QKeyEvent* ke = static_cast<QKeyEvent*>(e);
|
||||
if ((ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier)
|
||||
&& !ke->text().isEmpty())
|
||||
&& !ke->text().isEmpty()) {
|
||||
if (searchForm->isHidden()) {
|
||||
msgEdit->setFocus();
|
||||
} else {
|
||||
searchForm->setFocusEditor();
|
||||
}
|
||||
}
|
||||
}
|
||||
return QWidget::event(e);
|
||||
}
|
||||
|
@ -519,6 +542,108 @@ void GenericChatForm::addSystemDateMessage()
|
|||
insertChatMessage(ChatMessage::createChatInfoMessage(dateText, ChatMessage::INFO, QDateTime()));
|
||||
}
|
||||
|
||||
void GenericChatForm::disableSearchText()
|
||||
{
|
||||
if (searchPoint != QPoint(1, -1)) {
|
||||
QVector<ChatLine::Ptr> lines = chatWidget->getLines();
|
||||
int numLines = lines.size();
|
||||
int index = numLines - searchPoint.x();
|
||||
if (index >= 0 && numLines > index) {
|
||||
ChatLine::Ptr l = lines[index];
|
||||
if (l->getColumnCount() >= 2) {
|
||||
ChatLineContent* content = l->getContent(1);
|
||||
Text* text = static_cast<Text*>(content);
|
||||
text->deselectText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GenericChatForm::searchInText(const QString& phrase, bool searchUp)
|
||||
{
|
||||
bool isSearch = false;
|
||||
|
||||
if (phrase.isEmpty()) {
|
||||
disableSearchText();
|
||||
}
|
||||
|
||||
QVector<ChatLine::Ptr> lines = chatWidget->getLines();
|
||||
|
||||
if (lines.isEmpty()) {
|
||||
return isSearch;
|
||||
}
|
||||
|
||||
int numLines = lines.size();
|
||||
int startLine = numLines - searchPoint.x();
|
||||
|
||||
if (startLine < 0 || startLine >= numLines) {
|
||||
return isSearch;
|
||||
}
|
||||
|
||||
for (int i = startLine; searchUp ? i >= 0 : i < numLines; searchUp ? --i : ++i) {
|
||||
ChatLine::Ptr l = lines[i];
|
||||
|
||||
if (l->getColumnCount() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ChatLineContent* content = l->getContent(1);
|
||||
Text* text = static_cast<Text*>(content);
|
||||
|
||||
if (searchUp && searchPoint.y() == 0) {
|
||||
text->deselectText();
|
||||
searchPoint.setY(-1);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
QString txt = content->getText();
|
||||
|
||||
if (!txt.contains(phrase, Qt::CaseInsensitive)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = indexForSearchInLine(txt, phrase, searchUp);
|
||||
if ((index == -1 && searchPoint.y() > -1)) {
|
||||
text->deselectText();
|
||||
searchPoint.setY(-1);
|
||||
} else {
|
||||
chatWidget->scrollToLine(l);
|
||||
text->deselectText();
|
||||
text->selectText(phrase, index);
|
||||
searchPoint = QPoint(numLines - i, index);
|
||||
isSearch = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isSearch;
|
||||
}
|
||||
|
||||
int GenericChatForm::indexForSearchInLine(const QString& txt, const QString& phrase, bool searchUp)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
if (searchUp) {
|
||||
int startIndex = -1;
|
||||
if (searchPoint.y() > -1) {
|
||||
startIndex = searchPoint.y() - 1;
|
||||
}
|
||||
|
||||
index = txt.lastIndexOf(phrase, startIndex, Qt::CaseInsensitive);
|
||||
} else {
|
||||
int startIndex = 0;
|
||||
if (searchPoint.y() > -1) {
|
||||
startIndex = searchPoint.y() + 1;
|
||||
}
|
||||
|
||||
index = txt.indexOf(phrase, startIndex, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void GenericChatForm::clearChatArea()
|
||||
{
|
||||
clearChatArea(true);
|
||||
|
@ -662,6 +787,43 @@ void GenericChatForm::copyLink()
|
|||
QApplication::clipboard()->setText(linkText);
|
||||
}
|
||||
|
||||
void GenericChatForm::searchFormShow()
|
||||
{
|
||||
if (searchForm->isHidden()) {
|
||||
searchForm->show();
|
||||
searchForm->setFocusEditor();
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatForm::onSearchTriggered()
|
||||
{
|
||||
if (searchForm->isHidden()) {
|
||||
searchForm->removeSearchPhrase();
|
||||
|
||||
disableSearchText();
|
||||
} else {
|
||||
searchPoint = QPoint(1, -1);
|
||||
searchAfterLoadHistory = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatForm::searchInBegin(const QString& phrase)
|
||||
{
|
||||
disableSearchText();
|
||||
|
||||
searchPoint = QPoint(1, -1);
|
||||
onSearchUp(phrase);
|
||||
}
|
||||
|
||||
void GenericChatForm::onContinueSearch()
|
||||
{
|
||||
QString phrase = searchForm->getSearchPhrase();
|
||||
if (!phrase.isEmpty() && searchAfterLoadHistory) {
|
||||
searchAfterLoadHistory = false;
|
||||
onSearchUp(phrase);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatForm::retranslateUi()
|
||||
{
|
||||
sendButton->setToolTip(tr("Send message"));
|
||||
|
@ -672,6 +834,7 @@ void GenericChatForm::retranslateUi()
|
|||
clearAction->setText(tr("Clear displayed messages"));
|
||||
quoteAction->setText(tr("Quote selected text"));
|
||||
copyLinkAction->setText(tr("Copy link address"));
|
||||
searchAction->setText(tr("Search in text"));
|
||||
}
|
||||
|
||||
void GenericChatForm::showNetcam()
|
||||
|
|
|
@ -40,6 +40,7 @@ class CroppingLabel;
|
|||
class FlyoutOverlayWidget;
|
||||
class GenericNetCamView;
|
||||
class MaskablePixmapWidget;
|
||||
class SearchForm;
|
||||
class Widget;
|
||||
|
||||
class QLabel;
|
||||
|
@ -102,6 +103,13 @@ protected slots:
|
|||
void onSplitterMoved(int pos, int index);
|
||||
void quoteSelectedText();
|
||||
void copyLink();
|
||||
void searchFormShow();
|
||||
void onSearchTriggered();
|
||||
|
||||
void searchInBegin(const QString& phrase);
|
||||
virtual void onSearchUp(const QString& phrase) = 0;
|
||||
virtual void onSearchDown(const QString& phrase) = 0;
|
||||
void onContinueSearch();
|
||||
|
||||
private:
|
||||
void retranslateUi();
|
||||
|
@ -123,6 +131,9 @@ protected:
|
|||
virtual bool event(QEvent*) final override;
|
||||
virtual void resizeEvent(QResizeEvent* event) final override;
|
||||
virtual bool eventFilter(QObject* object, QEvent* event) final override;
|
||||
void disableSearchText();
|
||||
bool searchInText(const QString& phrase, bool searchUp);
|
||||
int indexForSearchInLine(const QString& txt, const QString& phrase, bool searchUp);
|
||||
|
||||
protected:
|
||||
bool audioInputFlag;
|
||||
|
@ -133,6 +144,7 @@ protected:
|
|||
QAction* clearAction;
|
||||
QAction* quoteAction;
|
||||
QAction* copyLinkAction;
|
||||
QAction* searchAction;
|
||||
|
||||
ToxPk previousId;
|
||||
|
||||
|
@ -151,11 +163,15 @@ protected:
|
|||
|
||||
ChatFormHeader* headWidget;
|
||||
|
||||
SearchForm *searchForm;
|
||||
ChatLog* chatWidget;
|
||||
ChatTextEdit* msgEdit;
|
||||
FlyoutOverlayWidget* fileFlyout;
|
||||
GenericNetCamView* netcam;
|
||||
Widget* parent;
|
||||
|
||||
QPoint searchPoint;
|
||||
bool searchAfterLoadHistory;
|
||||
};
|
||||
|
||||
#endif // GENERICCHATFORM_H
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "tabcompleter.h"
|
||||
#include "src/core/core.h"
|
||||
#include "src/core/coreav.h"
|
||||
#include "src/chatlog/chatlog.h"
|
||||
#include "src/chatlog/content/text.h"
|
||||
#include "src/model/friend.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "src/model/group.h"
|
||||
|
@ -189,6 +191,16 @@ void GroupChatForm::onTitleChanged(uint32_t groupId, const QString& author, cons
|
|||
addSystemInfoMessage(message, ChatMessage::INFO, curTime);
|
||||
}
|
||||
|
||||
void GroupChatForm::onSearchUp(const QString& phrase)
|
||||
{
|
||||
searchInText(phrase, true);
|
||||
}
|
||||
|
||||
void GroupChatForm::onSearchDown(const QString& phrase)
|
||||
{
|
||||
searchInText(phrase, false);
|
||||
}
|
||||
|
||||
void GroupChatForm::onScreenshotClicked()
|
||||
{
|
||||
// Unsupported
|
||||
|
|
|
@ -49,6 +49,8 @@ private slots:
|
|||
void onCallClicked();
|
||||
void onUserListChanged();
|
||||
void onTitleChanged(uint32_t groupId, const QString& author, const QString& title);
|
||||
void onSearchUp(const QString& phrase) override;
|
||||
void onSearchDown(const QString& phrase) override;
|
||||
|
||||
protected:
|
||||
virtual GenericNetCamView* createNetcam() final override;
|
||||
|
|
128
src/widget/searchform.cpp
Normal file
128
src/widget/searchform.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
Copyright © 2015-2016 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre 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
|
||||
(at your option) any later version.
|
||||
|
||||
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "searchform.h"
|
||||
#include "src/widget/style.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QKeyEvent>
|
||||
|
||||
SearchForm::SearchForm(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout();
|
||||
searchLine = new LineEdit();
|
||||
|
||||
upButton = createButton("searchUpButton", "green");
|
||||
downButton = createButton("searchDownButton", "green");
|
||||
hideButton = createButton("searchHideButton", "red");
|
||||
|
||||
layout->setMargin(0);
|
||||
layout->addWidget(searchLine);
|
||||
layout->addWidget(upButton);
|
||||
layout->addWidget(downButton);
|
||||
layout->addWidget(hideButton);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
connect(searchLine, &LineEdit::textChanged, this, &SearchForm::changedSearchPhrase);
|
||||
connect(searchLine, &LineEdit::clickEnter, this, &SearchForm::clickedUp);
|
||||
connect(searchLine, &LineEdit::clickShiftEnter, this, &SearchForm::clickedDown);
|
||||
connect(searchLine, &LineEdit::clickEsc, this, &SearchForm::clickedHide);
|
||||
|
||||
connect(upButton, &QPushButton::clicked, this, &SearchForm::clickedUp);
|
||||
connect(downButton, &QPushButton::clicked, this, &SearchForm::clickedDown);
|
||||
connect(hideButton, &QPushButton::clicked, this, &SearchForm::clickedHide);
|
||||
}
|
||||
|
||||
void SearchForm::removeSearchPhrase()
|
||||
{
|
||||
searchLine->setText("");
|
||||
}
|
||||
|
||||
QString SearchForm::getSearchPhrase() const
|
||||
{
|
||||
return searchPhrase;
|
||||
}
|
||||
|
||||
void SearchForm::setFocusEditor()
|
||||
{
|
||||
searchLine->setFocus();
|
||||
}
|
||||
|
||||
void SearchForm::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
emit visibleChanged();
|
||||
}
|
||||
|
||||
QPushButton *SearchForm::createButton(const QString& name, const QString& state)
|
||||
{
|
||||
QPushButton* btn = new QPushButton();
|
||||
btn->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
btn->setObjectName(name);
|
||||
btn->setProperty("state", state);
|
||||
btn->setStyleSheet(Style::getStylesheet(QStringLiteral(":/ui/chatForm/buttons.css")));
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
void SearchForm::changedSearchPhrase(const QString& text)
|
||||
{
|
||||
searchPhrase = text;
|
||||
emit searchInBegin(searchPhrase);
|
||||
}
|
||||
|
||||
void SearchForm::clickedUp()
|
||||
{
|
||||
emit searchUp(searchPhrase);
|
||||
}
|
||||
|
||||
void SearchForm::clickedDown()
|
||||
{
|
||||
emit searchDown(searchPhrase);
|
||||
}
|
||||
|
||||
void SearchForm::clickedHide()
|
||||
{
|
||||
hide();
|
||||
emit visibleChanged();
|
||||
}
|
||||
|
||||
LineEdit::LineEdit(QWidget* parent) : QLineEdit(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void LineEdit::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
int key = event->key();
|
||||
|
||||
if ((key == Qt::Key_Enter || key == Qt::Key_Return)) {
|
||||
if ((event->modifiers() & Qt::ShiftModifier)) {
|
||||
emit clickShiftEnter();
|
||||
} else {
|
||||
emit clickEnter();
|
||||
}
|
||||
} else if (key == Qt::Key_Escape) {
|
||||
emit clickEsc();
|
||||
}
|
||||
|
||||
QLineEdit::keyPressEvent(event);
|
||||
}
|
||||
|
||||
|
81
src/widget/searchform.h
Normal file
81
src/widget/searchform.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
Copyright © 2015-2016 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre 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
|
||||
(at your option) any later version.
|
||||
|
||||
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SEARCHFORM_H
|
||||
#define SEARCHFORM_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLineEdit>
|
||||
|
||||
class QPushButton;
|
||||
class LineEdit;
|
||||
|
||||
class SearchForm final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SearchForm(QWidget* parent = nullptr);
|
||||
void removeSearchPhrase();
|
||||
QString getSearchPhrase() const;
|
||||
void setFocusEditor();
|
||||
|
||||
protected:
|
||||
virtual void showEvent(QShowEvent* event) final override;
|
||||
|
||||
private:
|
||||
// TODO: Merge with 'createButton' from chatformheader.cpp
|
||||
QPushButton* createButton(const QString& name, const QString& state);
|
||||
|
||||
QPushButton* upButton;
|
||||
QPushButton* downButton;
|
||||
QPushButton* hideButton;
|
||||
LineEdit* searchLine;
|
||||
|
||||
QString searchPhrase;
|
||||
|
||||
private slots:
|
||||
void changedSearchPhrase(const QString& text);
|
||||
void clickedUp();
|
||||
void clickedDown();
|
||||
void clickedHide();
|
||||
|
||||
signals:
|
||||
void searchInBegin(const QString& phrase);
|
||||
void searchUp(const QString& phrase);
|
||||
void searchDown(const QString& phrase);
|
||||
void visibleChanged();
|
||||
};
|
||||
|
||||
class LineEdit : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LineEdit(QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void keyPressEvent(QKeyEvent* event) final override;
|
||||
|
||||
signals:
|
||||
void clickEnter();
|
||||
void clickShiftEnter();
|
||||
void clickEsc();
|
||||
};
|
||||
|
||||
#endif // SEARCHFORM_H
|
|
@ -67,6 +67,33 @@ QAbstractButton#callButton
|
|||
height: 40px;
|
||||
}
|
||||
|
||||
/* SearchLine */
|
||||
|
||||
QAbstractButton#searchHideButton
|
||||
{
|
||||
background-image: url(":/ui/chatForm/searchHideButton.svg");
|
||||
border-radius: 5px;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
|
||||
QAbstractButton#searchUpButton
|
||||
{
|
||||
background-image: url(":/ui/chatForm/searchUpButton.svg");
|
||||
border-radius: 5px;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
QAbstractButton#searchDownButton
|
||||
{
|
||||
background-image: url(":/ui/chatForm/searchDownButton.svg");
|
||||
border-radius: 5px;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
/* Common */
|
||||
|
||||
QAbstractButton
|
||||
|
|
82
ui/chatForm/searchDownButton.svg
Normal file
82
ui/chatForm/searchDownButton.svg
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="7.0183306mm"
|
||||
height="4.1245208mm"
|
||||
viewBox="0 0 7.0183304 4.1245209"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||
sodipodi:docname="searchDownButton.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="185.76311"
|
||||
inkscape:cy="-116.1932"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="876"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(59.722852,-41.245778)">
|
||||
<g
|
||||
id="g11"
|
||||
style="fill:#ffffff"
|
||||
transform="matrix(0.03267961,0,0,0.03267961,-57.771132,39.897882)">
|
||||
<rect
|
||||
transform="rotate(-44.285246)"
|
||||
style="fill:#ffffff;stroke-width:0.36410955"
|
||||
y="123.15389"
|
||||
x="-82.806313"
|
||||
height="30"
|
||||
width="150"
|
||||
id="rect10" />
|
||||
<rect
|
||||
transform="matrix(-0.71587256,-0.69823097,-0.69823097,0.71587256,0,0)"
|
||||
style="fill:#ffffff;stroke-width:0.36410955"
|
||||
y="56.601276"
|
||||
x="-151.04047"
|
||||
height="30.000002"
|
||||
width="150"
|
||||
id="rect10-7" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
81
ui/chatForm/searchHideButton.svg
Normal file
81
ui/chatForm/searchHideButton.svg
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="5.5071211mm"
|
||||
height="5.5071211mm"
|
||||
viewBox="0 0 5.5071209 5.5071212"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||
sodipodi:docname="hideButton.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="186.40227"
|
||||
inkscape:cy="-38.76831"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="876"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(59.891963,-41.449704)">
|
||||
<g
|
||||
id="g30"
|
||||
transform="matrix(0.42362466,0.42362466,-0.42362466,0.42362466,-22.854056,42.277356)"
|
||||
style="fill:#ffffff">
|
||||
<rect
|
||||
y="41.988605"
|
||||
x="-43.942345"
|
||||
height="1.5"
|
||||
width="11.5"
|
||||
id="rect11"
|
||||
style="fill:#ffffff;stroke-width:0.26458332" />
|
||||
<rect
|
||||
transform="rotate(-90)"
|
||||
y="-38.942345"
|
||||
x="-48.488605"
|
||||
height="1.5"
|
||||
width="11.500001"
|
||||
id="rect11-3"
|
||||
style="fill:#ffffff;stroke-width:0.26458332" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
82
ui/chatForm/searchUpButton.svg
Normal file
82
ui/chatForm/searchUpButton.svg
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="7.0183306mm"
|
||||
height="4.1245208mm"
|
||||
viewBox="0 0 7.0183304 4.1245209"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||
sodipodi:docname="searchUpButton.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="185.76311"
|
||||
inkscape:cy="-116.1932"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="876"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(59.722852,-41.245778)">
|
||||
<g
|
||||
id="g11"
|
||||
style="fill:#ffffff"
|
||||
transform="matrix(0.03267961,0,0,-0.03267961,-57.771132,46.718195)">
|
||||
<rect
|
||||
transform="rotate(-44.285246)"
|
||||
style="fill:#ffffff;stroke-width:0.36410955"
|
||||
y="123.15389"
|
||||
x="-82.806313"
|
||||
height="30"
|
||||
width="150"
|
||||
id="rect10" />
|
||||
<rect
|
||||
transform="matrix(-0.71587256,-0.69823097,-0.69823097,0.71587256,0,0)"
|
||||
style="fill:#ffffff;stroke-width:0.36410955"
|
||||
y="56.601276"
|
||||
x="-151.04047"
|
||||
height="30.000002"
|
||||
width="150"
|
||||
id="rect10-7" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
Loading…
Reference in New Issue
Block a user