Quick search/filter for dictionary names in settings (Dictionaries and Groups tabs).

This commit is contained in:
Tvangeste 2013-01-04 14:24:08 +01:00
parent ebf28c6343
commit c8c6a515d6
11 changed files with 297 additions and 76 deletions

View file

@ -10,9 +10,10 @@ ExtLineEdit::ExtLineEdit(QWidget *parent) :
{ {
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
iconButtons[i] = new IconButton(parent); iconButtons[i] = new IconButton(this);
iconButtons[i]->installEventFilter(this); iconButtons[i]->installEventFilter(this);
iconButtons[i]->hide(); iconButtons[i]->hide();
iconButtons[i]->setAutoHide(false);
iconEnabled[i] = false; iconEnabled[i] = false;
} }
@ -21,6 +22,8 @@ ExtLineEdit::ExtLineEdit(QWidget *parent) :
connect(iconButtons[Left], SIGNAL(clicked()), this, SLOT(iconClicked())); connect(iconButtons[Left], SIGNAL(clicked()), this, SLOT(iconClicked()));
connect(iconButtons[Right], SIGNAL(clicked()), this, SLOT(iconClicked())); connect(iconButtons[Right], SIGNAL(clicked()), this, SLOT(iconClicked()));
connect(this, SIGNAL( textChanged( QString ) ), this, SLOT( updateButtons( QString ) ) );
} }
ExtLineEdit::~ExtLineEdit() ExtLineEdit::~ExtLineEdit()
@ -39,6 +42,33 @@ bool ExtLineEdit::isButtonVisible(Side side) const
return iconEnabled[side]; return iconEnabled[side];
} }
void ExtLineEdit::setButtonAutoHide(Side side, bool autohide)
{
iconButtons[side]->setAutoHide(autohide);
if (autohide)
{
iconButtons[side]->setOpacity( text().isEmpty() ? 0.0 : 1.0 );
}
else
{
iconButtons[side]->setOpacity( 1.0 );
}
}
void ExtLineEdit::updateButtons(QString text)
{
if ( oldText.isEmpty() || text.isEmpty() ) {
for (int i = 0; i < 2; ++i) {
if ( iconButtons[i]->isAutoHide() )
{
iconButtons[i]->animate( !text.isEmpty() );
}
}
oldText = text;
}
}
void ExtLineEdit::iconClicked() void ExtLineEdit::iconClicked()
{ {
IconButton * button = qobject_cast<IconButton *>( sender() ); IconButton * button = qobject_cast<IconButton *>( sender() );
@ -65,8 +95,8 @@ void ExtLineEdit::updateMargins()
int leftMargin = iconButtons[realLeft]->pixmap().width() + 8; int leftMargin = iconButtons[realLeft]->pixmap().width() + 8;
int rightMargin = iconButtons[realRight]->pixmap().width() + 8; int rightMargin = iconButtons[realRight]->pixmap().width() + 8;
QMargins margins((iconEnabled[realLeft] ? leftMargin : 0), 0, QMargins margins((iconEnabled[realLeft] ? leftMargin : 0), 1,
(iconEnabled[realRight] ? rightMargin : 0), 0); (iconEnabled[realRight] ? rightMargin : 0), 1);
setTextMargins(margins); setTextMargins(margins);
} }
@ -128,11 +158,29 @@ IconButton::IconButton(QWidget *parent)
void IconButton::paintEvent(QPaintEvent *) void IconButton::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
QIcon::Mode state = QIcon::Disabled;
if (isEnabled())
state = isDown() ? QIcon::Selected : QIcon::Normal;
QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height()); QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
pixmapRect.moveCenter(rect().center()); pixmapRect.moveCenter(rect().center());
if (m_autohide)
{
painter.setOpacity(m_opacity);
}
painter.drawPixmap(pixmapRect, m_pixmap); painter.drawPixmap(pixmapRect, m_pixmap);
} }
void IconButton::animate(bool visible)
{
QPropertyAnimation *animation = new QPropertyAnimation(this, "opacity");
animation->setDuration(250);
if (visible)
{
animation->setEndValue(1.0);
}
else
{
animation->setEndValue(0.0);
}
animation->start(QAbstractAnimation::DeleteWhenStopped);
}

View file

@ -6,20 +6,32 @@
#include <QLineEdit> #include <QLineEdit>
#include <QAbstractButton> #include <QAbstractButton>
#include <QPropertyAnimation>
class IconButton: public QAbstractButton class IconButton: public QAbstractButton
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
Q_PROPERTY(float opacity READ opacity WRITE setOpacity)
public: public:
explicit IconButton(QWidget * parent = 0); explicit IconButton(QWidget * parent = 0);
void paintEvent(QPaintEvent * event); void paintEvent(QPaintEvent * event);
void animate(bool visible);
void setPixmap(const QPixmap & pixmap) { m_pixmap = pixmap; update(); } void setPixmap(const QPixmap & pixmap) { m_pixmap = pixmap; update(); }
QPixmap pixmap() const { return m_pixmap; } QPixmap pixmap() const { return m_pixmap; }
void setAutoHide(bool autohide) { m_autohide = autohide; update(); }
bool isAutoHide() const { return m_autohide; }
float opacity() { return m_opacity; }
void setOpacity(float opacity) { m_opacity = opacity; update(); }
private: private:
QPixmap m_pixmap; QPixmap m_pixmap;
bool m_autohide;
float m_opacity;
}; };
class ExtLineEdit : public QLineEdit class ExtLineEdit : public QLineEdit
@ -42,12 +54,15 @@ public:
void setButtonToolTip(Side side, const QString &); void setButtonToolTip(Side side, const QString &);
void setButtonFocusPolicy(Side side, Qt::FocusPolicy policy); void setButtonFocusPolicy(Side side, Qt::FocusPolicy policy);
void setButtonAutoHide(Side side, bool autohide);
signals: signals:
void leftButtonClicked(); void leftButtonClicked();
void rightButtonClicked(); void rightButtonClicked();
private slots: private slots:
void iconClicked(); void iconClicked();
void updateButtons(QString text);
protected: protected:
virtual void resizeEvent( QResizeEvent * e ); virtual void resizeEvent( QResizeEvent * e );
@ -58,6 +73,7 @@ private:
QPixmap pixmaps[2]; QPixmap pixmaps[2];
IconButton * iconButtons[2]; IconButton * iconButtons[2];
bool iconEnabled[2]; bool iconEnabled[2];
QString oldText;
}; };
#endif // EXTLINEEDIT_H #endif // EXTLINEEDIT_H

View file

@ -23,6 +23,9 @@ Groups::Groups( QWidget * parent,
ui.dictionaries->populate( Instances::Group( order, dicts ).dictionaries, ui.dictionaries->populate( Instances::Group( order, dicts ).dictionaries,
dicts ); dicts );
ui.searchLine->applyTo( ui.dictionaries );
addAction( ui.searchLine->getFocusAction() );
// Populate groups' widget // Populate groups' widget
ui.groups->populate( groups, dicts, ui.dictionaries->getCurrentDictionaries() ); ui.groups->populate( groups, dicts, ui.dictionaries->getCurrentDictionaries() );
@ -201,3 +204,4 @@ void Groups::showDictInfo( QPoint const & pos )
} }
} }

View file

@ -23,6 +23,9 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QuickFilterLine" name="searchLine"/>
</item>
<item> <item>
<widget class="DictListWidget" name="dictionaries"/> <widget class="DictListWidget" name="dictionaries"/>
</item> </item>
@ -206,6 +209,11 @@
<extends>QListWidget</extends> <extends>QListWidget</extends>
<header>groups_widgets.hh</header> <header>groups_widgets.hh</header>
</customwidget> </customwidget>
<customwidget>
<class>QuickFilterLine</class>
<extends>QLineEdit</extends>
<header>groups_widgets.hh</header>
</customwidget>
<customwidget> <customwidget>
<class>DictGroupsWidget</class> <class>DictGroupsWidget</class>
<extends>QTabWidget</extends> <extends>QTabWidget</extends>
@ -223,6 +231,7 @@
<tabstop>renameGroup</tabstop> <tabstop>renameGroup</tabstop>
<tabstop>removeGroup</tabstop> <tabstop>removeGroup</tabstop>
<tabstop>removeAllGroups</tabstop> <tabstop>removeAllGroups</tabstop>
<tabstop>searchLine</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections/> <connections/>

View file

@ -370,7 +370,19 @@ void DictListModel::addSelectedUniqueFromModel( QItemSelectionModel * source )
if ( !rows.count() ) if ( !rows.count() )
return; return;
const DictListModel * baseModel = dynamic_cast< const DictListModel * > ( source->model() ); const QSortFilterProxyModel * proxyModel = dynamic_cast< const QSortFilterProxyModel * > ( source->model() );
const DictListModel * baseModel;
if ( proxyModel )
{
baseModel = dynamic_cast< const DictListModel * > ( proxyModel->sourceModel() );
}
else
{
baseModel = dynamic_cast< const DictListModel * > ( source->model() );
}
if ( !baseModel ) if ( !baseModel )
return; return;
@ -381,7 +393,8 @@ void DictListModel::addSelectedUniqueFromModel( QItemSelectionModel * source )
for ( int i = 0; i < rows.count(); i++ ) for ( int i = 0; i < rows.count(); i++ )
{ {
std::string id = baseModel->dictionaries.at( rows.at( i ).row() )->getId(); QModelIndex idx = proxyModel ? proxyModel->mapToSource(rows.at( i )) : rows.at( i );
std::string id = baseModel->dictionaries.at( idx.row() )->getId();
if ( !dicts.contains( id ) ) if ( !dicts.contains( id ) )
list.append( id ); list.append( id );
@ -996,3 +1009,66 @@ void DictGroupsWidget::tabDataChanged()
+ QString::number( getCurrentModel()->getCurrentDictionaries().size() ); + QString::number( getCurrentModel()->getCurrentDictionaries().size() );
setTabToolTip( currentIndex(), toolTipStr ); setTabToolTip( currentIndex(), toolTipStr );
} }
QuickFilterLine::QuickFilterLine( QWidget * parent ): ExtLineEdit( parent ), m_focusAction(this)
{
m_proxyModel.setFilterCaseSensitivity( Qt::CaseInsensitive );
#if QT_VERSION >= 0x040700
setPlaceholderText( tr( "Dictionary search/filter (Ctrl+F)" ) );
#endif
m_focusAction.setShortcut( QKeySequence( "Ctrl+F" ) );
connect( &m_focusAction, SIGNAL( triggered() ),
this, SLOT( focusFilterLine() ) );
QPixmap image(":/icons/system-search.png");
setButtonPixmap(ExtLineEdit::Left, image.scaled(18, 18, Qt::KeepAspectRatio, Qt::SmoothTransformation));
setButtonToolTip(ExtLineEdit::Left, tr("Quick Search"));
setButtonVisible(ExtLineEdit::Left, true);
QPixmap right(":/icons/clear.png");
setButtonPixmap(ExtLineEdit::Right, right);
setButtonToolTip(ExtLineEdit::Right, tr("Clear Search"));
setButtonVisible(ExtLineEdit::Right, true);
setButtonAutoHide(ExtLineEdit::Right, true);
connect( this, SIGNAL( rightButtonClicked() ), this, SLOT( clear() ) );
setFocusPolicy(Qt::StrongFocus);
connect (this, SIGNAL( textChanged( QString const & ) ),
this, SLOT( filterChangedInternal() ) );
}
QuickFilterLine::~QuickFilterLine()
{
}
void QuickFilterLine::applyTo( QAbstractItemView * source )
{
m_proxyModel.setSourceModel( source->model() );
source->setModel( &m_proxyModel );
}
QModelIndex QuickFilterLine::mapToSource( QModelIndex const & idx )
{
return m_proxyModel.mapToSource( idx );
}
void QuickFilterLine::filterChangedInternal()
{
// emit signal in async manner, to avoid UI slowdown
QTimer::singleShot( 1, this, SLOT( emitFilterChanged() ) );
}
void QuickFilterLine::emitFilterChanged()
{
m_proxyModel.setFilterFixedString(text());
emit filterChanged( text() );
}
void QuickFilterLine::focusFilterLine()
{
setFocus();
selectAll();
}

View file

@ -6,10 +6,14 @@
// Various custom widgets used in the Groups dialog // Various custom widgets used in the Groups dialog
#include <vector>
#include <QListWidget> #include <QListWidget>
#include <QSortFilterProxyModel>
#include "config.hh" #include "config.hh"
#include "dictionary.hh" #include "dictionary.hh"
#include <vector> #include "extlineedit.hh"
/// A model to be projected into the view, according to Qt's MVC model /// A model to be projected into the view, according to Qt's MVC model
class DictListModel: public QAbstractListModel class DictListModel: public QAbstractListModel
@ -191,5 +195,35 @@ signals:
void showDictionaryInfo( QString const & id ); void showDictionaryInfo( QString const & id );
}; };
#endif class QuickFilterLine: public ExtLineEdit
{
Q_OBJECT
public:
QuickFilterLine( QWidget * parent );
~QuickFilterLine();
/// Sets the source view to filter
void applyTo( QAbstractItemView * source );
QAction * getFocusAction() { return & m_focusAction; }
QModelIndex mapToSource( QModelIndex const & idx );
private:
QSortFilterProxyModel m_proxyModel;
QAction m_focusAction;
private slots:
void filterChangedInternal();
void emitFilterChanged();
void focusFilterLine();
signals:
void filterChanged(QString const & filter);
};
#endif

BIN
icons/clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

View file

@ -104,11 +104,16 @@ OrderAndProps::OrderAndProps( QWidget * parent,
ui.dictionaryOrder->populate( order.dictionaries, allDictionaries ); ui.dictionaryOrder->populate( order.dictionaries, allDictionaries );
ui.inactiveDictionaries->populate( inactive.dictionaries, allDictionaries ); ui.inactiveDictionaries->populate( inactive.dictionaries, allDictionaries );
ui.searchLine->applyTo( ui.dictionaryOrder );
addAction( ui.searchLine->getFocusAction() );
disableDictionaryDescription(); disableDictionaryDescription();
ui.dictionaryOrder->setContextMenuPolicy( Qt::CustomContextMenu ); ui.dictionaryOrder->setContextMenuPolicy( Qt::CustomContextMenu );
connect( ui.dictionaryOrder, SIGNAL( customContextMenuRequested( QPoint ) ), connect( ui.dictionaryOrder, SIGNAL( customContextMenuRequested( QPoint ) ),
this, SLOT( contextMenuRequested( QPoint ) ) ); this, SLOT( contextMenuRequested( QPoint ) ) );
connect (ui.searchLine, SIGNAL( filterChanged( QString const & ) ),
this, SLOT( filterChanged( QString const &) ) );
} }
Config::Group OrderAndProps::getCurrentDictionaryOrder() const Config::Group OrderAndProps::getCurrentDictionaryOrder() const
@ -129,9 +134,16 @@ Config::Group OrderAndProps::getCurrentInactiveDictionaries() const
return g.makeConfigGroup(); return g.makeConfigGroup();
} }
void OrderAndProps::filterChanged( QString const & filterText)
{
// when the filter is active, disable the possibility
// to drop dictionaries to this filtered list
ui.dictionaryOrder->setAcceptDrops(filterText.isEmpty());
}
void OrderAndProps::on_dictionaryOrder_clicked( QModelIndex const & idx ) void OrderAndProps::on_dictionaryOrder_clicked( QModelIndex const & idx )
{ {
describeDictionary( ui.dictionaryOrder, idx ); describeDictionary( ui.dictionaryOrder, ui.searchLine->mapToSource( idx ) );
} }
void OrderAndProps::on_inactiveDictionaries_clicked( QModelIndex const & idx ) void OrderAndProps::on_inactiveDictionaries_clicked( QModelIndex const & idx )

View file

@ -6,6 +6,7 @@
#include "ui_orderandprops.h" #include "ui_orderandprops.h"
#include "groups_widgets.hh" #include "groups_widgets.hh"
#include <QSortFilterProxyModel>
class OrderAndProps: public QWidget class OrderAndProps: public QWidget
{ {
@ -25,6 +26,7 @@ private slots:
void on_dictionaryOrder_clicked( QModelIndex const & ); void on_dictionaryOrder_clicked( QModelIndex const & );
void on_inactiveDictionaries_clicked( QModelIndex const & ); void on_inactiveDictionaries_clicked( QModelIndex const & );
void contextMenuRequested( const QPoint & pos ); void contextMenuRequested( const QPoint & pos );
void filterChanged( QString const & filterText );
private: private:

View file

@ -19,68 +19,8 @@
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,10,0,0,2"> <layout class="QGridLayout" name="gridLayout_2" rowstretch="0,10,10,0,0,2">
<item row="0" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Dictionary order:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="DictListWidget" name="dictionaryOrder"/>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="moveActiveUp">
<property name="text">
<string>...</string>
</property>
<property name="arrowType">
<enum>Qt::UpArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="moveActiveDown">
<property name="text">
<string>...</string>
</property>
<property name="arrowType">
<enum>Qt::DownArrow</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
@ -130,15 +70,78 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="3" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Dictionary order:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="DictListWidget" name="dictionaryOrder"/>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="moveActiveUp">
<property name="text">
<string>...</string>
</property>
<property name="arrowType">
<enum>Qt::UpArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="moveActiveDown">
<property name="text">
<string>...</string>
</property>
<property name="arrowType">
<enum>Qt::DownArrow</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="DictListWidget" name="inactiveDictionaries"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Inactive (disabled) dictionaries:</string> <string>Inactive (disabled) dictionaries:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="1" column="0">
<widget class="DictListWidget" name="inactiveDictionaries"/> <widget class="QuickFilterLine" name="searchLine"/>
</item> </item>
</layout> </layout>
</item> </item>
@ -417,7 +420,23 @@
<extends>QListWidget</extends> <extends>QListWidget</extends>
<header>groups_widgets.hh</header> <header>groups_widgets.hh</header>
</customwidget> </customwidget>
<customwidget>
<class>QuickFilterLine</class>
<extends>QLineEdit</extends>
<header>groups_widgets.hh</header>
</customwidget>
</customwidgets> </customwidgets>
<tabstops>
<tabstop>dictionaryOrder</tabstop>
<tabstop>moveActiveUp</tabstop>
<tabstop>moveActiveDown</tabstop>
<tabstop>moveToActive</tabstop>
<tabstop>moveToInactive</tabstop>
<tabstop>inactiveDictionaries</tabstop>
<tabstop>dictionaryDescription</tabstop>
<tabstop>dictionaryFileList</tabstop>
<tabstop>searchLine</tabstop>
</tabstops>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View file

@ -56,5 +56,6 @@
<file>icons/1downarrow.png</file> <file>icons/1downarrow.png</file>
<file>icons/system-search.png</file> <file>icons/system-search.png</file>
<file>icons/menu_button.png</file> <file>icons/menu_button.png</file>
<file>icons/clear.png</file>
</qresource> </qresource>
</RCC> </RCC>