mirror of
https://invent.kde.org/network/falkon.git
synced 2024-12-20 18:56:34 +01:00
Completely rewritten whole completion for address bar.
- fixed occasional flickering when typing in address bar - support for opening completions with pressing Down key - support for selecting completions with Tab key closes #105
This commit is contained in:
parent
98d51b0661
commit
64bdafde87
@ -50,7 +50,6 @@ SOURCES += \
|
|||||||
history/historymodel.cpp \
|
history/historymodel.cpp \
|
||||||
history/historymanager.cpp \
|
history/historymanager.cpp \
|
||||||
navigation/websearchbar.cpp \
|
navigation/websearchbar.cpp \
|
||||||
navigation/locationcompleter.cpp \
|
|
||||||
navigation/locationbar.cpp \
|
navigation/locationbar.cpp \
|
||||||
network/networkmanagerproxy.cpp \
|
network/networkmanagerproxy.cpp \
|
||||||
network/networkmanager.cpp \
|
network/networkmanager.cpp \
|
||||||
@ -170,7 +169,10 @@ SOURCES += \
|
|||||||
tools/plaineditwithlines.cpp \
|
tools/plaineditwithlines.cpp \
|
||||||
webview/websettings.cpp \
|
webview/websettings.cpp \
|
||||||
tools/focusselectlineedit.cpp \
|
tools/focusselectlineedit.cpp \
|
||||||
navigation/locationcompleterdelegate.cpp
|
navigation/completer/locationcompleterdelegate.cpp \
|
||||||
|
navigation/completer/locationcompleter.cpp \
|
||||||
|
navigation/completer/locationcompletermodel.cpp \
|
||||||
|
navigation/completer/locationcompleterview.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
webview/tabpreview.h \
|
webview/tabpreview.h \
|
||||||
@ -189,7 +191,6 @@ HEADERS += \
|
|||||||
history/historymodel.h \
|
history/historymodel.h \
|
||||||
history/historymanager.h \
|
history/historymanager.h \
|
||||||
navigation/websearchbar.h \
|
navigation/websearchbar.h \
|
||||||
navigation/locationcompleter.h \
|
|
||||||
navigation/locationbar.h \
|
navigation/locationbar.h \
|
||||||
network/networkmanagerproxy.h \
|
network/networkmanagerproxy.h \
|
||||||
network/networkmanager.h \
|
network/networkmanager.h \
|
||||||
@ -313,7 +314,10 @@ HEADERS += \
|
|||||||
sidebar/sidebarinterface.h \
|
sidebar/sidebarinterface.h \
|
||||||
webview/websettings.h \
|
webview/websettings.h \
|
||||||
tools/focusselectlineedit.h \
|
tools/focusselectlineedit.h \
|
||||||
navigation/locationcompleterdelegate.h
|
navigation/completer/locationcompleterdelegate.h \
|
||||||
|
navigation/completer/locationcompleter.h \
|
||||||
|
navigation/completer/locationcompletermodel.h \
|
||||||
|
navigation/completer/locationcompleterview.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
preferences/autofillmanager.ui \
|
preferences/autofillmanager.ui \
|
||||||
|
120
src/lib/navigation/completer/locationcompleter.cpp
Normal file
120
src/lib/navigation/completer/locationcompleter.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* QupZilla - WebKit based browser
|
||||||
|
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.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
|
||||||
|
* (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. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* ============================================================ */
|
||||||
|
#include "locationcompleter.h"
|
||||||
|
#include "locationcompletermodel.h"
|
||||||
|
#include "locationcompleterview.h"
|
||||||
|
#include "locationcompleterdelegate.h"
|
||||||
|
#include "locationbar.h"
|
||||||
|
|
||||||
|
LocationCompleterView* LocationCompleter::s_view = 0;
|
||||||
|
LocationCompleterModel* LocationCompleter::s_model = 0;
|
||||||
|
|
||||||
|
LocationCompleter::LocationCompleter(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_locationBar(0)
|
||||||
|
{
|
||||||
|
if (!s_view) {
|
||||||
|
s_model = new LocationCompleterModel;
|
||||||
|
s_view = new LocationCompleterView;
|
||||||
|
|
||||||
|
s_view->setModel(s_model);
|
||||||
|
s_view->setItemDelegate(new LocationCompleterDelegate(s_view));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::setLocationBar(LocationBar* locationBar)
|
||||||
|
{
|
||||||
|
m_locationBar = locationBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::closePopup()
|
||||||
|
{
|
||||||
|
s_view->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::complete(const QString &string)
|
||||||
|
{
|
||||||
|
s_model->refreshCompletions(string);
|
||||||
|
|
||||||
|
showPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::showMostVisited()
|
||||||
|
{
|
||||||
|
s_model->refreshCompletions(QString());
|
||||||
|
|
||||||
|
showPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::currentChanged(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
QString completion = index.data().toString();
|
||||||
|
if (completion.isEmpty()) {
|
||||||
|
completion = m_originalText;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit showCompletion(completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::popupClosed()
|
||||||
|
{
|
||||||
|
disconnect(s_view->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(currentChanged(QModelIndex)));
|
||||||
|
disconnect(s_view, SIGNAL(clicked(QModelIndex)), this, SIGNAL(completionActivated()));
|
||||||
|
disconnect(s_view, SIGNAL(closed()), this, SLOT(popupClosed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::showPopup()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_locationBar);
|
||||||
|
|
||||||
|
if (s_model->rowCount() == 0) {
|
||||||
|
s_view->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_view->isVisible()) {
|
||||||
|
adjustPopupSize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect popupRect(m_locationBar->mapToGlobal(m_locationBar->pos()), m_locationBar->size());
|
||||||
|
popupRect.setY(popupRect.bottom());
|
||||||
|
|
||||||
|
s_view->setFocusProxy(m_locationBar);
|
||||||
|
s_view->setGeometry(popupRect);
|
||||||
|
|
||||||
|
connect(s_view->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(currentChanged(QModelIndex)));
|
||||||
|
connect(s_view, SIGNAL(clicked(QModelIndex)), this, SIGNAL(completionActivated()));
|
||||||
|
connect(s_view, SIGNAL(closed()), this, SLOT(popupClosed()));
|
||||||
|
|
||||||
|
adjustPopupSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleter::adjustPopupSize()
|
||||||
|
{
|
||||||
|
const int maxItemsCount = 6;
|
||||||
|
|
||||||
|
int popupHeight = s_view->sizeHintForRow(0) * qMin(maxItemsCount, s_model->rowCount());
|
||||||
|
popupHeight += 2 * s_view->frameWidth();
|
||||||
|
|
||||||
|
s_view->resize(s_view->width(), popupHeight);
|
||||||
|
s_view->setCurrentIndex(QModelIndex());
|
||||||
|
s_view->show();
|
||||||
|
|
||||||
|
m_originalText = m_locationBar->text();
|
||||||
|
}
|
63
src/lib/navigation/completer/locationcompleter.h
Normal file
63
src/lib/navigation/completer/locationcompleter.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* QupZilla - WebKit based browser
|
||||||
|
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.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
|
||||||
|
* (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. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* ============================================================ */
|
||||||
|
#ifndef LOCATIONCOMPLETER_H
|
||||||
|
#define LOCATIONCOMPLETER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "qz_namespace.h"
|
||||||
|
|
||||||
|
class QModelIndex;
|
||||||
|
|
||||||
|
class LocationCompleterModel;
|
||||||
|
class LocationCompleterView;
|
||||||
|
class LocationBar;
|
||||||
|
|
||||||
|
class QT_QUPZILLA_EXPORT LocationCompleter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit LocationCompleter(QObject* parent = 0);
|
||||||
|
|
||||||
|
void setLocationBar(LocationBar* locationBar);
|
||||||
|
void closePopup();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void showCompletion(const QString &);
|
||||||
|
void completionActivated();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void complete(const QString &string);
|
||||||
|
void showMostVisited();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void currentChanged(const QModelIndex &index);
|
||||||
|
void popupClosed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void showPopup();
|
||||||
|
void adjustPopupSize();
|
||||||
|
|
||||||
|
LocationBar* m_locationBar;
|
||||||
|
QString m_originalText;
|
||||||
|
|
||||||
|
static LocationCompleterView* s_view;
|
||||||
|
static LocationCompleterModel* s_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOCATIONCOMPLETER_H
|
@ -16,78 +16,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
#include "locationcompleterdelegate.h"
|
#include "locationcompleterdelegate.h"
|
||||||
|
#include "locationcompleterview.h"
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
||||||
#include <QDebug>
|
LocationCompleterDelegate::LocationCompleterDelegate(LocationCompleterView* parent)
|
||||||
|
|
||||||
CompleterListView::CompleterListView(QWidget* parent)
|
|
||||||
: QListView(parent)
|
|
||||||
, m_selectedItemByMousePosition(false)
|
|
||||||
, m_rowHeight(0)
|
|
||||||
{
|
|
||||||
setMouseTracking(true);
|
|
||||||
setUniformItemSizes(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CompleterListView::ignoreSelectedFlag() const
|
|
||||||
{
|
|
||||||
return m_selectedItemByMousePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CompleterListView::rowHeight() const
|
|
||||||
{
|
|
||||||
return m_rowHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompleterListView::setRowHeight(int height)
|
|
||||||
{
|
|
||||||
m_rowHeight = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompleterListView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
|
||||||
{
|
|
||||||
m_selectedItemByMousePosition = false;
|
|
||||||
m_lastMouseIndex = current;
|
|
||||||
|
|
||||||
QListView::currentChanged(current, previous);
|
|
||||||
|
|
||||||
viewport()->repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompleterListView::mouseMoveEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
QModelIndex last = m_lastMouseIndex;
|
|
||||||
QModelIndex atCursor = indexAt(mapFromGlobal(QCursor::pos()));
|
|
||||||
|
|
||||||
if (atCursor.isValid()) {
|
|
||||||
m_lastMouseIndex = atCursor;
|
|
||||||
m_selectedItemByMousePosition = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last != atCursor) {
|
|
||||||
viewport()->repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::mouseMoveEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompleterListView::keyPressEvent(QKeyEvent* event)
|
|
||||||
{
|
|
||||||
if (currentIndex() != m_lastMouseIndex) {
|
|
||||||
setCurrentIndex(m_lastMouseIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::keyPressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationCompleterDelegate::LocationCompleterDelegate(CompleterListView* parent)
|
|
||||||
: QStyledItemDelegate(parent)
|
: QStyledItemDelegate(parent)
|
||||||
, m_rowHeight(0)
|
, m_rowHeight(0)
|
||||||
, m_padding(0)
|
, m_padding(0)
|
||||||
, m_listView(parent)
|
, m_view(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,14 +49,15 @@ void LocationCompleterDelegate::paint(QPainter* painter, const QStyleOptionViewI
|
|||||||
int leftPosition = m_padding * 2;
|
int leftPosition = m_padding * 2;
|
||||||
int rightPosition = opt.rect.right() - m_padding;
|
int rightPosition = opt.rect.right() - m_padding;
|
||||||
|
|
||||||
if (m_listView->ignoreSelectedFlag()) {
|
// if (m_view->ignoreSelectedFlag()) {
|
||||||
if (opt.state.testFlag(QStyle::State_MouseOver)) {
|
// if (opt.state.testFlag(QStyle::State_MouseOver)) {
|
||||||
|
if (m_view->hoveredIndex() == index) {
|
||||||
opt.state |= QStyle::State_Selected;
|
opt.state |= QStyle::State_Selected;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
opt.state &= ~QStyle::State_Selected;
|
opt.state &= ~QStyle::State_Selected;
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
|
|
||||||
const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text;
|
const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text;
|
||||||
const QPalette::ColorRole colorLinkRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Link;
|
const QPalette::ColorRole colorLinkRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Link;
|
||||||
@ -169,11 +109,7 @@ QSize LocationCompleterDelegate::sizeHint(const QStyleOptionViewItem &option, co
|
|||||||
const QFontMetrics titleMetrics(titleFont);
|
const QFontMetrics titleMetrics(titleFont);
|
||||||
|
|
||||||
m_rowHeight = 2 * m_padding + opt.fontMetrics.leading() + opt.fontMetrics.height() + titleMetrics.height();
|
m_rowHeight = 2 * m_padding + opt.fontMetrics.leading() + opt.fontMetrics.height() + titleMetrics.height();
|
||||||
|
|
||||||
m_listView->setRowHeight(m_rowHeight);
|
|
||||||
m_listView->setMaximumHeight(6 * m_rowHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return QSize(200, m_rowHeight);
|
return QSize(200, m_rowHeight);
|
||||||
}
|
}
|
||||||
|
|
@ -19,38 +19,15 @@
|
|||||||
#define LOCATIONCOMPLETERDELEGATE_H
|
#define LOCATIONCOMPLETERDELEGATE_H
|
||||||
|
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <QListView>
|
|
||||||
|
|
||||||
#include "qz_namespace.h"
|
#include "qz_namespace.h"
|
||||||
|
|
||||||
class QT_QUPZILLA_EXPORT CompleterListView : public QListView
|
class LocationCompleterView;
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit CompleterListView(QWidget* parent = 0);
|
|
||||||
|
|
||||||
bool ignoreSelectedFlag() const;
|
|
||||||
|
|
||||||
int rowHeight() const;
|
|
||||||
void setRowHeight(int height);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
|
||||||
protected:
|
|
||||||
void mouseMoveEvent(QMouseEvent* event);
|
|
||||||
void keyPressEvent(QKeyEvent* event);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_selectedItemByMousePosition;
|
|
||||||
int m_rowHeight;
|
|
||||||
|
|
||||||
QModelIndex m_lastMouseIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
class QT_QUPZILLA_EXPORT LocationCompleterDelegate : public QStyledItemDelegate
|
class QT_QUPZILLA_EXPORT LocationCompleterDelegate : public QStyledItemDelegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit LocationCompleterDelegate(CompleterListView* parent = 0);
|
explicit LocationCompleterDelegate(LocationCompleterView* parent = 0);
|
||||||
|
|
||||||
void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
@ -59,7 +36,7 @@ private:
|
|||||||
mutable int m_rowHeight;
|
mutable int m_rowHeight;
|
||||||
mutable int m_padding;
|
mutable int m_padding;
|
||||||
|
|
||||||
CompleterListView* m_listView;
|
LocationCompleterView* m_view;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOCATIONCOMPLETERDELEGATE_H
|
#endif // LOCATIONCOMPLETERDELEGATE_H
|
@ -15,65 +15,37 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
#include "locationcompleter.h"
|
#include "locationcompletermodel.h"
|
||||||
#include "locationcompleterdelegate.h"
|
|
||||||
#include "locationbar.h"
|
|
||||||
#include "iconprovider.h"
|
#include "iconprovider.h"
|
||||||
#include "mainapplication.h"
|
#include "mainapplication.h"
|
||||||
|
|
||||||
#include <QStandardItemModel>
|
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
|
|
||||||
LocationCompleter::LocationCompleter(QObject* parent)
|
LocationCompleterModel::LocationCompleterModel(QObject* parent)
|
||||||
: QCompleter(parent)
|
: QStandardItemModel(parent)
|
||||||
|
, m_lastCompletion(QChar(QChar::Nbsp))
|
||||||
{
|
{
|
||||||
m_model = new QStandardItemModel();
|
|
||||||
|
|
||||||
m_listView = new CompleterListView();
|
|
||||||
m_listView->setItemDelegateForColumn(0, new LocationCompleterDelegate(m_listView));
|
|
||||||
|
|
||||||
setModel(m_model);
|
|
||||||
setPopup(m_listView);
|
|
||||||
|
|
||||||
setCompletionMode(QCompleter::PopupCompletion);
|
|
||||||
setMaxVisibleItems(6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList LocationCompleter::splitPath(const QString &path) const
|
void LocationCompleterModel::refreshCompletions(const QString &string)
|
||||||
{
|
{
|
||||||
Q_UNUSED(path);
|
if (m_lastCompletion == string) {
|
||||||
return QStringList();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocationCompleter::showMostVisited()
|
m_lastCompletion = string;
|
||||||
{
|
|
||||||
m_model->clear();
|
|
||||||
|
|
||||||
QSqlQuery query;
|
if (string.isEmpty()) {
|
||||||
query.exec("SELECT url, title FROM history ORDER BY count DESC LIMIT 15");
|
showMostVisited();
|
||||||
|
return;
|
||||||
while (query.next()) {
|
|
||||||
QStandardItem* item = new QStandardItem();
|
|
||||||
const QUrl &url = query.value(0).toUrl();
|
|
||||||
|
|
||||||
item->setIcon(_iconForUrl(url));
|
|
||||||
item->setText(url.toEncoded());
|
|
||||||
item->setData(query.value(1), Qt::UserRole);
|
|
||||||
|
|
||||||
m_model->appendRow(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QCompleter::complete();
|
clear();
|
||||||
}
|
|
||||||
|
|
||||||
void LocationCompleter::refreshCompleter(const QString &string)
|
|
||||||
{
|
|
||||||
int limit = string.size() < 3 ? 25 : 15;
|
int limit = string.size() < 3 ? 25 : 15;
|
||||||
QString searchString = QString("%%1%").arg(string);
|
QString searchString = QString("%%1%").arg(string);
|
||||||
QList<QUrl> urlList;
|
QList<QUrl> urlList;
|
||||||
|
|
||||||
m_model->clear();
|
|
||||||
|
|
||||||
QSqlQuery query;
|
QSqlQuery query;
|
||||||
query.prepare("SELECT url, title, icon FROM bookmarks WHERE title LIKE ? OR url LIKE ? LIMIT ?");
|
query.prepare("SELECT url, title, icon FROM bookmarks WHERE title LIKE ? OR url LIKE ? LIMIT ?");
|
||||||
query.addBindValue(searchString);
|
query.addBindValue(searchString);
|
||||||
@ -89,7 +61,7 @@ void LocationCompleter::refreshCompleter(const QString &string)
|
|||||||
item->setData(query.value(1), Qt::UserRole);
|
item->setData(query.value(1), Qt::UserRole);
|
||||||
item->setIcon(IconProvider::iconFromImage(QImage::fromData(query.value(2).toByteArray())));
|
item->setIcon(IconProvider::iconFromImage(QImage::fromData(query.value(2).toByteArray())));
|
||||||
|
|
||||||
m_model->appendRow(item);
|
appendRow(item);
|
||||||
urlList.append(url);
|
urlList.append(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +85,25 @@ void LocationCompleter::refreshCompleter(const QString &string)
|
|||||||
item->setText(url.toEncoded());
|
item->setText(url.toEncoded());
|
||||||
item->setData(query.value(1), Qt::UserRole);
|
item->setData(query.value(1), Qt::UserRole);
|
||||||
|
|
||||||
m_model->appendRow(item);
|
appendRow(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleterModel::showMostVisited()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
QSqlQuery query;
|
||||||
|
query.exec("SELECT url, title FROM history ORDER BY count DESC LIMIT 15");
|
||||||
|
|
||||||
|
while (query.next()) {
|
||||||
|
QStandardItem* item = new QStandardItem();
|
||||||
|
const QUrl &url = query.value(0).toUrl();
|
||||||
|
|
||||||
|
item->setIcon(_iconForUrl(url));
|
||||||
|
item->setText(url.toEncoded());
|
||||||
|
item->setData(query.value(1), Qt::UserRole);
|
||||||
|
|
||||||
|
appendRow(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,34 +15,26 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
#ifndef LOCATIONCOMPLETER_H
|
#ifndef LOCATIONCOMPLETERMODEL_H
|
||||||
#define LOCATIONCOMPLETER_H
|
#define LOCATIONCOMPLETERMODEL_H
|
||||||
|
|
||||||
#include <QCompleter>
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
#include "qz_namespace.h"
|
class LocationCompleterModel : public QStandardItemModel
|
||||||
|
|
||||||
class QStandardItemModel;
|
|
||||||
|
|
||||||
class CompleterListView;
|
|
||||||
|
|
||||||
class QT_QUPZILLA_EXPORT LocationCompleter : public QCompleter
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit LocationCompleter(QObject* parent = 0);
|
explicit LocationCompleterModel(QObject* parent = 0);
|
||||||
|
|
||||||
virtual QStringList splitPath(const QString &path) const;
|
void refreshCompletions(const QString &string);
|
||||||
|
void showMostVisited();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void refreshCompleter(const QString &string);
|
|
||||||
void showMostVisited();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CompleterListView* m_listView;
|
QString m_lastCompletion;
|
||||||
QStandardItemModel* m_model;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOCATIONCOMPLETER_H
|
#endif // LOCATIONCOMPLETERMODEL_H
|
188
src/lib/navigation/completer/locationcompleterview.cpp
Normal file
188
src/lib/navigation/completer/locationcompleterview.cpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* QupZilla - WebKit based browser
|
||||||
|
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.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
|
||||||
|
* (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. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* ============================================================ */
|
||||||
|
#include "locationcompleterview.h"
|
||||||
|
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
LocationCompleterView::LocationCompleterView()
|
||||||
|
: QListView(0)
|
||||||
|
, m_ignoreNextMouseMove(false)
|
||||||
|
{
|
||||||
|
setWindowFlags(Qt::Popup);
|
||||||
|
|
||||||
|
setUniformItemSizes(true);
|
||||||
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
|
||||||
|
setMouseTracking(true);
|
||||||
|
installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPersistentModelIndex LocationCompleterView::hoveredIndex() const
|
||||||
|
{
|
||||||
|
return m_hoveredIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LocationCompleterView::eventFilter(QObject* object, QEvent* event)
|
||||||
|
{
|
||||||
|
// Event filter based on QCompleter::eventFilter from qcompleter.cpp
|
||||||
|
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::KeyPress: {
|
||||||
|
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
|
QModelIndex curIndex = m_hoveredIndex;
|
||||||
|
|
||||||
|
if ((keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)
|
||||||
|
&& currentIndex() != curIndex) {
|
||||||
|
setCurrentIndex(curIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (keyEvent->key()) {
|
||||||
|
case Qt::Key_End:
|
||||||
|
case Qt::Key_Home:
|
||||||
|
if (keyEvent->modifiers() & Qt::ControlModifier) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qt::Key_Left:
|
||||||
|
case Qt::Key_Right:
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case Qt::Key_F4:
|
||||||
|
if (keyEvent->modifiers() == Qt::AltModifier) {
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
case Qt::Key_Backtab: {
|
||||||
|
Qt::Key k = keyEvent->key() == Qt::Key_Tab ? Qt::Key_Down : Qt::Key_Up;
|
||||||
|
QKeyEvent ev(QKeyEvent::KeyPress, k, Qt::NoModifier);
|
||||||
|
QApplication::sendEvent(this, &ev);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_Up:
|
||||||
|
if (!curIndex.isValid()) {
|
||||||
|
int rowCount = model()->rowCount();
|
||||||
|
QModelIndex lastIndex = model()->index(rowCount - 1, 0);
|
||||||
|
setCurrentIndex(lastIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (curIndex.row() == 0) {
|
||||||
|
setCurrentIndex(QModelIndex());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case Qt::Key_Down:
|
||||||
|
if (!curIndex.isValid()) {
|
||||||
|
QModelIndex firstIndex = model()->index(0, 0);
|
||||||
|
setCurrentIndex(firstIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (curIndex.row() == model()->rowCount() - 1) {
|
||||||
|
setCurrentIndex(QModelIndex());
|
||||||
|
scrollToTop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case Qt::Key_PageUp:
|
||||||
|
case Qt::Key_PageDown:
|
||||||
|
return false;
|
||||||
|
} // switch (keyEvent->key())
|
||||||
|
|
||||||
|
(static_cast<QObject*>(focusProxy()))->event(keyEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QEvent::Show:
|
||||||
|
m_ignoreNextMouseMove = true;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case QEvent::MouseButtonPress:
|
||||||
|
if (!underMouse()) {
|
||||||
|
close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::InputMethod:
|
||||||
|
case QEvent::ShortcutOverride:
|
||||||
|
QApplication::sendEvent(focusProxy(), event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return QListView::eventFilter(object, event);
|
||||||
|
} // switch (event->type())
|
||||||
|
|
||||||
|
return QListView::eventFilter(object, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleterView::close()
|
||||||
|
{
|
||||||
|
emit closed();
|
||||||
|
m_hoveredIndex = QPersistentModelIndex();
|
||||||
|
|
||||||
|
QListView::hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleterView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||||
|
{
|
||||||
|
m_hoveredIndex = current;
|
||||||
|
|
||||||
|
QListView::currentChanged(current, previous);
|
||||||
|
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationCompleterView::mouseMoveEvent(QMouseEvent* event)
|
||||||
|
{
|
||||||
|
if (m_ignoreNextMouseMove || !isVisible()) {
|
||||||
|
m_ignoreNextMouseMove = false;
|
||||||
|
|
||||||
|
QListView::mouseMoveEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex last = m_hoveredIndex;
|
||||||
|
QModelIndex atCursor = indexAt(mapFromGlobal(QCursor::pos()));
|
||||||
|
|
||||||
|
if (atCursor.isValid()) {
|
||||||
|
m_hoveredIndex = atCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != atCursor) {
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QListView::mouseMoveEvent(event);
|
||||||
|
}
|
53
src/lib/navigation/completer/locationcompleterview.h
Normal file
53
src/lib/navigation/completer/locationcompleterview.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* QupZilla - WebKit based browser
|
||||||
|
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.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
|
||||||
|
* (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. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* ============================================================ */
|
||||||
|
#ifndef LOCATIONCOMPLETERVIEW_H
|
||||||
|
#define LOCATIONCOMPLETERVIEW_H
|
||||||
|
|
||||||
|
#include <QListView>
|
||||||
|
|
||||||
|
#include "qz_namespace.h"
|
||||||
|
|
||||||
|
class QT_QUPZILLA_EXPORT LocationCompleterView : public QListView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit LocationCompleterView();
|
||||||
|
|
||||||
|
QPersistentModelIndex hoveredIndex() const;
|
||||||
|
|
||||||
|
bool eventFilter(QObject* object, QEvent* event);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void closed();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void close();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_ignoreNextMouseMove;
|
||||||
|
|
||||||
|
QPersistentModelIndex m_hoveredIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOCATIONCOMPLETERVIEW_H
|
@ -20,7 +20,6 @@
|
|||||||
#include "tabbedwebview.h"
|
#include "tabbedwebview.h"
|
||||||
#include "rssmanager.h"
|
#include "rssmanager.h"
|
||||||
#include "mainapplication.h"
|
#include "mainapplication.h"
|
||||||
#include "locationcompleter.h"
|
|
||||||
#include "clickablelabel.h"
|
#include "clickablelabel.h"
|
||||||
#include "siteinfowidget.h"
|
#include "siteinfowidget.h"
|
||||||
#include "rsswidget.h"
|
#include "rsswidget.h"
|
||||||
@ -66,12 +65,11 @@ LocationBar::LocationBar(QupZilla* mainClass)
|
|||||||
|
|
||||||
setWidgetSpacing(0);
|
setWidgetSpacing(0);
|
||||||
|
|
||||||
m_locationCompleter = new LocationCompleter();
|
m_completer.setLocationBar(this);
|
||||||
setCompleter(m_locationCompleter);
|
connect(&m_completer, SIGNAL(showCompletion(QString)), this, SLOT(showCompletion(QString)));
|
||||||
|
connect(&m_completer, SIGNAL(completionActivated()), this, SLOT(urlEnter()));
|
||||||
|
|
||||||
connect(this, SIGNAL(textEdited(QString)), this, SLOT(textEdit()));
|
connect(this, SIGNAL(textEdited(QString)), this, SLOT(textEdit()));
|
||||||
connect(this, SIGNAL(textEdited(QString)), m_locationCompleter, SLOT(refreshCompleter(QString)));
|
|
||||||
connect(m_locationCompleter->popup(), SIGNAL(clicked(QModelIndex)), this, SLOT(urlEnter()));
|
|
||||||
connect(m_siteIcon, SIGNAL(clicked()), this, SLOT(showSiteInfo()));
|
connect(m_siteIcon, SIGNAL(clicked()), this, SLOT(showSiteInfo()));
|
||||||
connect(m_goIcon, SIGNAL(clicked(QPoint)), this, SLOT(urlEnter()));
|
connect(m_goIcon, SIGNAL(clicked(QPoint)), this, SLOT(urlEnter()));
|
||||||
connect(m_rssIcon, SIGNAL(clicked(QPoint)), this, SLOT(rssIconClicked()));
|
connect(m_rssIcon, SIGNAL(clicked(QPoint)), this, SLOT(rssIconClicked()));
|
||||||
@ -93,6 +91,12 @@ void LocationBar::updatePlaceHolderText()
|
|||||||
setPlaceholderText(tr("Enter URL address or search on %1").arg(mApp->searchEnginesManager()->activeEngine().name));
|
setPlaceholderText(tr("Enter URL address or search on %1").arg(mApp->searchEnginesManager()->activeEngine().name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocationBar::showCompletion(const QString &newText)
|
||||||
|
{
|
||||||
|
LineEdit::setText(newText);
|
||||||
|
end(false);
|
||||||
|
}
|
||||||
|
|
||||||
QUrl LocationBar::createUrl()
|
QUrl LocationBar::createUrl()
|
||||||
{
|
{
|
||||||
QUrl urlToLoad;
|
QUrl urlToLoad;
|
||||||
@ -124,7 +128,7 @@ QUrl LocationBar::createUrl()
|
|||||||
|
|
||||||
void LocationBar::urlEnter()
|
void LocationBar::urlEnter()
|
||||||
{
|
{
|
||||||
m_locationCompleter->popup()->hide();
|
m_completer.closePopup();
|
||||||
m_webView->setFocus();
|
m_webView->setFocus();
|
||||||
|
|
||||||
emit loadUrl(createUrl());
|
emit loadUrl(createUrl());
|
||||||
@ -132,6 +136,13 @@ void LocationBar::urlEnter()
|
|||||||
|
|
||||||
void LocationBar::textEdit()
|
void LocationBar::textEdit()
|
||||||
{
|
{
|
||||||
|
if (!text().isEmpty()) {
|
||||||
|
m_completer.complete(text());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_completer.closePopup();
|
||||||
|
}
|
||||||
|
|
||||||
showGoButton();
|
showGoButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,15 +176,7 @@ void LocationBar::hideGoButton()
|
|||||||
|
|
||||||
void LocationBar::showMostVisited()
|
void LocationBar::showMostVisited()
|
||||||
{
|
{
|
||||||
if (text().isEmpty()) {
|
m_completer.complete(QString());
|
||||||
// Workaround: If we show popup when text in locationbar is empty and then
|
|
||||||
// move up and down in completer and then we leave completer -> completer will
|
|
||||||
// set text in locationbar back to last "real" completion
|
|
||||||
QKeyEvent event(QEvent::KeyPress, Qt::Key_unknown, Qt::NoModifier, QString(" "));
|
|
||||||
keyPressEvent(&event);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_locationCompleter->showMostVisited();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocationBar::showSiteInfo()
|
void LocationBar::showSiteInfo()
|
||||||
@ -400,6 +403,10 @@ void LocationBar::keyPressEvent(QKeyEvent* event)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Qt::Key_Down:
|
||||||
|
m_completer.complete(text());
|
||||||
|
break;
|
||||||
|
|
||||||
case Qt::Key_Escape:
|
case Qt::Key_Escape:
|
||||||
m_webView->setFocus();
|
m_webView->setFocus();
|
||||||
showUrl(m_webView->url());
|
showUrl(m_webView->url());
|
||||||
@ -470,5 +477,4 @@ void LocationBar::keyReleaseEvent(QKeyEvent* event)
|
|||||||
LocationBar::~LocationBar()
|
LocationBar::~LocationBar()
|
||||||
{
|
{
|
||||||
delete m_bookmarkIcon;
|
delete m_bookmarkIcon;
|
||||||
delete m_locationCompleter;
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "qz_namespace.h"
|
#include "qz_namespace.h"
|
||||||
#include "lineedit.h"
|
#include "lineedit.h"
|
||||||
|
#include "completer/locationcompleter.h"
|
||||||
|
|
||||||
class QupZilla;
|
class QupZilla;
|
||||||
class LineEdit;
|
class LineEdit;
|
||||||
@ -52,7 +53,7 @@ signals:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void showUrl(const QUrl &url);
|
void showUrl(const QUrl &url);
|
||||||
virtual void setText(const QString &text);
|
void setText(const QString &text);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void siteIconChanged();
|
void siteIconChanged();
|
||||||
@ -67,6 +68,7 @@ private slots:
|
|||||||
void pasteAndGo();
|
void pasteAndGo();
|
||||||
|
|
||||||
void updatePlaceHolderText();
|
void updatePlaceHolderText();
|
||||||
|
void showCompletion(const QString &newText);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void contextMenuEvent(QContextMenuEvent* event);
|
void contextMenuEvent(QContextMenuEvent* event);
|
||||||
@ -82,6 +84,8 @@ private:
|
|||||||
void showGoButton();
|
void showGoButton();
|
||||||
void hideGoButton();
|
void hideGoButton();
|
||||||
|
|
||||||
|
LocationCompleter m_completer;
|
||||||
|
|
||||||
BookmarkIcon* m_bookmarkIcon;
|
BookmarkIcon* m_bookmarkIcon;
|
||||||
GoIcon* m_goIcon;
|
GoIcon* m_goIcon;
|
||||||
RssIcon* m_rssIcon;
|
RssIcon* m_rssIcon;
|
||||||
@ -89,7 +93,6 @@ private:
|
|||||||
|
|
||||||
QupZilla* p_QupZilla;
|
QupZilla* p_QupZilla;
|
||||||
TabbedWebView* m_webView;
|
TabbedWebView* m_webView;
|
||||||
LocationCompleter* m_locationCompleter;
|
|
||||||
|
|
||||||
QMenu* m_menu;
|
QMenu* m_menu;
|
||||||
QAction* m_pasteAndGoAction;
|
QAction* m_pasteAndGoAction;
|
||||||
|
Loading…
Reference in New Issue
Block a user