Skip to content

Commit 70f9c22

Browse files
committedFeb 4, 2018
[options search] API refactoring
* avoid using lambdas and use dedicate class and subclasses for each widget type (lable, groupbox, tree, etc) * this makes the code much more clearer * allow creating custom highlight widgets with API
1 parent 213d4b7 commit 70f9c22

11 files changed

+849
-347
lines changed
 

‎python/gui/gui_auto.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@
160160
%Include qgsnewgeopackagelayerdialog.sip
161161
%Include qgsopacitywidget.sip
162162
%Include qgsoptionsdialogbase.sip
163+
%Include qgsoptionsdialoghighlightwidget.sip
164+
%Include qgsoptionsdialoghighlightwidgetsimpl.sip
163165
%Include qgsoptionswidgetfactory.sip
164166
%Include qgsorderbydialog.sip
165167
%Include qgsowssourceselect.sip

‎python/gui/qgsoptionsdialogbase.sip.in

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,57 +13,6 @@
1313

1414

1515

16-
class QgsSearchHighlightOptionWidget : QObject
17-
{
18-
%Docstring
19-
Container for a widget to be used to search text in the option dialog
20-
If the widget type is handled, it is valid.
21-
It can perform a text search in the widget and highlight it in case of success.
22-
This uses stylesheets.
23-
24-
.. versionadded:: 3.0
25-
%End
26-
27-
%TypeHeaderCode
28-
#include "qgsoptionsdialogbase.h"
29-
%End
30-
public:
31-
32-
explicit QgsSearchHighlightOptionWidget( QWidget *widget = 0 );
33-
%Docstring
34-
Constructor
35-
36-
:param widget: the widget used to search text into
37-
%End
38-
39-
bool isValid();
40-
%Docstring
41-
Returns if it valid: if the widget type is handled and if the widget is not still available
42-
%End
43-
44-
bool searchHighlight( const QString &searchText );
45-
%Docstring
46-
search for a text pattern and highlight the widget if the text is found
47-
48-
:return: true if the text pattern is found
49-
%End
50-
51-
void reset();
52-
%Docstring
53-
reset the style to the original state
54-
%End
55-
56-
QWidget *widget();
57-
%Docstring
58-
return the widget
59-
%End
60-
61-
virtual bool eventFilter( QObject *obj, QEvent *event );
62-
63-
64-
};
65-
66-
6716

6817
class QgsOptionsDialogBase : QDialog
6918
{
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgsoptionsdialoghighlightwidget.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsOptionsDialogHighlightWidget : QObject
13+
{
14+
%Docstring
15+
Container for a widget to be used to search text in the option dialog
16+
If the widget type is handled, it is valid.
17+
It can perform a text search in the widget and highlight it in case of success.
18+
This uses stylesheets.
19+
20+
.. versionadded:: 3.0
21+
%End
22+
23+
%TypeHeaderCode
24+
#include "qgsoptionsdialoghighlightwidget.h"
25+
%End
26+
public:
27+
static QgsOptionsDialogHighlightWidget *createWidget( QWidget *widget ) /Factory/;
28+
29+
bool isValid();
30+
%Docstring
31+
Returns if it valid: if the widget type is handled and if the widget is not still available
32+
%End
33+
34+
bool searchHighlight( const QString &text );
35+
%Docstring
36+
search for a text pattern and highlight the widget if the text is found
37+
38+
:return: true if the text pattern is found
39+
%End
40+
41+
QWidget *widget();
42+
%Docstring
43+
Return the widget
44+
%End
45+
46+
47+
virtual bool eventFilter( QObject *obj, QEvent *event );
48+
49+
50+
51+
protected:
52+
53+
virtual bool searchText( const QString &text ) = 0;
54+
%Docstring
55+
Search for the ``text`` in the widget and return true if it was found
56+
%End
57+
58+
virtual bool highlightText( const QString &text ) = 0;
59+
%Docstring
60+
Highlight the ``text`` in the widget.
61+
62+
:return: true if the text could be highlighted.
63+
%End
64+
65+
virtual void reset() = 0;
66+
%Docstring
67+
reset the style of the widgets to its original state
68+
%End
69+
70+
explicit QgsOptionsDialogHighlightWidget( QWidget *widget = 0 );
71+
%Docstring
72+
Constructor
73+
74+
:param widget: the widget used to search text into
75+
%End
76+
77+
78+
};
79+
80+
/************************************************************************
81+
* This file has been generated automatically from *
82+
* *
83+
* src/gui/qgsoptionsdialoghighlightwidget.h *
84+
* *
85+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
86+
************************************************************************/
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgsoptionsdialoghighlightwidgetsimpl.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
14+
15+
class QgsOptionsDialogHighlightLabel : QgsOptionsDialogHighlightWidget
16+
{
17+
%Docstring
18+
A highlight widget for labels.
19+
This is used to search and highlight text in QgsOptionsDialogBase implementations.
20+
%End
21+
22+
%TypeHeaderCode
23+
#include "qgsoptionsdialoghighlightwidgetsimpl.h"
24+
%End
25+
public:
26+
QgsOptionsDialogHighlightLabel( QLabel *label );
27+
%Docstring
28+
constructs a highlight widget for a label
29+
%End
30+
protected:
31+
virtual bool searchText( const QString &text );
32+
virtual bool highlightText( const QString &text );
33+
virtual void reset();
34+
};
35+
36+
class QgsOptionsDialogHighlightCheckBox : QgsOptionsDialogHighlightWidget
37+
{
38+
%Docstring
39+
A highlight widget for checkboxes.
40+
This is used to search and highlight text in QgsOptionsDialogBase implementations.
41+
%End
42+
43+
%TypeHeaderCode
44+
#include "qgsoptionsdialoghighlightwidgetsimpl.h"
45+
%End
46+
public:
47+
QgsOptionsDialogHighlightCheckBox( QCheckBox *checkBox );
48+
%Docstring
49+
constructs a highlight widget for a checkbox
50+
%End
51+
protected:
52+
virtual bool searchText( const QString &text );
53+
virtual bool highlightText( const QString &text );
54+
virtual void reset();
55+
};
56+
57+
class QgsOptionsDialogHighlightButton : QgsOptionsDialogHighlightWidget
58+
{
59+
%Docstring
60+
A highlight widget for buttons.
61+
This is used to search and highlight text in QgsOptionsDialogBase implementations.
62+
%End
63+
64+
%TypeHeaderCode
65+
#include "qgsoptionsdialoghighlightwidgetsimpl.h"
66+
%End
67+
public:
68+
QgsOptionsDialogHighlightButton( QAbstractButton *button );
69+
%Docstring
70+
constructs a highlight widget for a button.
71+
%End
72+
protected:
73+
virtual bool searchText( const QString &text );
74+
virtual bool highlightText( const QString &text );
75+
virtual void reset();
76+
};
77+
78+
class QgsOptionsDialogHighlightGroupBox : QgsOptionsDialogHighlightWidget
79+
{
80+
%Docstring
81+
A highlight widget for group boxes.
82+
This is used to search and highlight text in QgsOptionsDialogBase implementations.
83+
%End
84+
85+
%TypeHeaderCode
86+
#include "qgsoptionsdialoghighlightwidgetsimpl.h"
87+
%End
88+
public:
89+
QgsOptionsDialogHighlightGroupBox( QGroupBox *groupBox );
90+
%Docstring
91+
constructs a highlight widget for a group box.
92+
%End
93+
protected:
94+
virtual bool searchText( const QString &text );
95+
virtual bool highlightText( const QString &text );
96+
virtual void reset();
97+
};
98+
99+
class QgsOptionsDialogHighlightTree : QgsOptionsDialogHighlightWidget
100+
{
101+
%Docstring
102+
A highlight widget for trees.
103+
This is used to search and highlight text in QgsOptionsDialogBase implementations.
104+
Highlighting is only available for tree widgets only while searching can be performed
105+
in any tree view or inherited class.
106+
%End
107+
108+
%TypeHeaderCode
109+
#include "qgsoptionsdialoghighlightwidgetsimpl.h"
110+
%End
111+
public:
112+
QgsOptionsDialogHighlightTree( QTreeView *treeView );
113+
%Docstring
114+
constructs a highlight widget for a tree view or widget.
115+
%End
116+
protected:
117+
virtual bool searchText( const QString &text );
118+
virtual bool highlightText( const QString &text );
119+
virtual void reset();
120+
};
121+
/************************************************************************
122+
* This file has been generated automatically from *
123+
* *
124+
* src/gui/qgsoptionsdialoghighlightwidgetsimpl.h *
125+
* *
126+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
127+
************************************************************************/

‎src/gui/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ SET(QGIS_GUI_SRCS
314314
qgsnewgeopackagelayerdialog.cpp
315315
qgsopacitywidget.cpp
316316
qgsoptionsdialogbase.cpp
317+
qgsoptionsdialoghighlightwidget.cpp
318+
qgsoptionsdialoghighlightwidgetsimpl.cpp
317319
qgsorderbydialog.cpp
318320
qgsowssourceselect.cpp
319321
qgspanelwidget.cpp
@@ -479,6 +481,8 @@ SET(QGIS_GUI_MOC_HDRS
479481
qgsnewgeopackagelayerdialog.h
480482
qgsopacitywidget.h
481483
qgsoptionsdialogbase.h
484+
qgsoptionsdialoghighlightwidget.h
485+
qgsoptionsdialoghighlightwidgetsimpl.h
482486
qgsoptionswidgetfactory.h
483487
qgsorderbydialog.h
484488
qgsowssourceselect.h

‎src/gui/qgsoptionsdialogbase.cpp

Lines changed: 7 additions & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,8 @@
1616

1717
#include "qgsoptionsdialogbase.h"
1818

19-
#include <QCheckBox>
2019
#include <QDialog>
2120
#include <QDialogButtonBox>
22-
#include <QEvent>
23-
#include <QGroupBox>
24-
#include <QLabel>
2521
#include <QLayout>
2622
#include <QListWidget>
2723
#include <QListWidgetItem>
@@ -31,13 +27,12 @@
3127
#include <QSplitter>
3228
#include <QStackedWidget>
3329
#include <QTimer>
34-
#include <QTreeView>
35-
#include <QTreeWidget>
36-
#include <QAbstractItemModel>
3730

3831
#include "qgsfilterlineedit.h"
3932
#include "qgsmessagebaritem.h"
4033
#include "qgslogger.h"
34+
#include "qgsoptionsdialoghighlightwidget.h"
35+
#include "qgsoptionswidgetfactory.h"
4136

4237
QgsOptionsDialogBase::QgsOptionsDialogBase( const QString &settingsKey, QWidget *parent, Qt::WindowFlags fl, QgsSettings *settings )
4338
: QDialog( parent, fl )
@@ -227,15 +222,10 @@ void QgsOptionsDialogBase::searchText( const QString &text )
227222
mOptListWidget->setRowHidden( r, !text.isEmpty() );
228223
}
229224

230-
for ( const QPair< QgsSearchHighlightOptionWidget *, int > &rsw : qgis::as_const( mRegisteredSearchWidgets ) )
225+
for ( const QPair< QgsOptionsDialogHighlightWidget *, int > &rsw : qgis::as_const( mRegisteredSearchWidgets ) )
231226
{
232-
rsw.first->reset();
233-
if ( !text.isEmpty() && rsw.first->searchHighlight( text ) )
227+
if ( rsw.first->searchHighlight( text ) )
234228
{
235-
QgsDebugMsgLevel( QString( "Found %1 in %2 (tab: %3)" )
236-
.arg( text )
237-
.arg( rsw.first->isValid() ? rsw.first->widget()->objectName() : "no widget" )
238-
.arg( mOptListWidget->item( rsw.second )->text() ), 4 );
239229
mOptListWidget->setRowHidden( rsw.second, false );
240230
}
241231
}
@@ -266,8 +256,8 @@ void QgsOptionsDialogBase::registerTextSearchWidgets()
266256
{
267257
Q_FOREACH ( QWidget *w, mOptStackedWidget->widget( i )->findChildren<QWidget *>() )
268258
{
269-
QgsSearchHighlightOptionWidget *shw = new QgsSearchHighlightOptionWidget( w );
270-
if ( shw->isValid() )
259+
QgsOptionsDialogHighlightWidget *shw = QgsOptionsDialogHighlightWidget::createWidget( w );
260+
if ( shw && shw->isValid() )
271261
{
272262
QgsDebugMsgLevel( QString( "Registering: %1" ).arg( w->objectName() ), 4 );
273263
mRegisteredSearchWidgets.append( qMakePair( shw, i ) );
@@ -371,7 +361,7 @@ void QgsOptionsDialogBase::optionsStackedWidget_WidgetRemoved( int index )
371361
// will need to take item first, if widgets are set for item in future
372362
delete mOptListWidget->item( index );
373363

374-
QList<QPair< QgsSearchHighlightOptionWidget *, int > >::iterator it = mRegisteredSearchWidgets.begin();
364+
QList<QPair< QgsOptionsDialogHighlightWidget *, int > >::iterator it = mRegisteredSearchWidgets.begin();
375365
while ( it != mRegisteredSearchWidgets.end() )
376366
{
377367
if ( ( *it ).second == index )
@@ -391,218 +381,3 @@ void QgsOptionsDialogBase::warnAboutMissingObjects()
391381
QMessageBox::Ok );
392382
}
393383

394-
395-
QgsSearchHighlightOptionWidget::QgsSearchHighlightOptionWidget( QWidget *widget )
396-
: QObject( widget )
397-
, mWidget( widget )
398-
{
399-
QWidget *parent = widget;
400-
while ( ( parent = parent->parentWidget() ) )
401-
{
402-
// do not register message bar content, items disappear and causes QGIS to crash
403-
if ( qobject_cast< QgsMessageBarItem * >( parent ) )
404-
{
405-
mValid = false;
406-
return;
407-
}
408-
}
409-
410-
QString styleSheet;
411-
if ( qobject_cast<QLabel *>( widget ) )
412-
{
413-
styleSheet = QStringLiteral( "QLabel { background-color: yellow; color: blue;}" );
414-
mTextFound = [ = ]( const QString & searchText ) {return qobject_cast<QLabel *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
415-
}
416-
else if ( qobject_cast<QCheckBox *>( widget ) )
417-
{
418-
styleSheet = QStringLiteral( "QCheckBox { background-color: yellow; color: blue;}" );
419-
mTextFound = [ = ]( const QString & searchText ) {return qobject_cast<QCheckBox *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
420-
}
421-
else if ( qobject_cast<QAbstractButton *>( widget ) )
422-
{
423-
styleSheet = QStringLiteral( "QAbstractButton { background-color: yellow; color: blue;}" );
424-
mTextFound = [ = ]( const QString & searchText ) {return qobject_cast<QAbstractButton *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
425-
}
426-
else if ( qobject_cast<QGroupBox *>( widget ) )
427-
{
428-
styleSheet = QStringLiteral( "QGroupBox::title { background-color: yellow; color: blue;}" );
429-
mTextFound = [ = ]( const QString & searchText ) {return qobject_cast<QGroupBox *>( mWidget )->title().contains( searchText, Qt::CaseInsensitive );};
430-
}
431-
if ( !styleSheet.isEmpty() )
432-
{
433-
styleSheet.prepend( "/*!search!*/" ).append( "/*!search!*/" );
434-
435-
mHighlight = [ = ]( const QString & searchText )
436-
{
437-
Q_UNUSED( searchText );
438-
mWidget->setStyleSheet( mWidget->styleSheet() + styleSheet );
439-
};
440-
441-
mReset = [ = ]()
442-
{
443-
if ( mWidget )
444-
{
445-
QString ss = mWidget->styleSheet();
446-
ss.remove( styleSheet );
447-
mWidget->setStyleSheet( ss );
448-
}
449-
};
450-
}
451-
else if ( qobject_cast<QTreeView *>( widget ) )
452-
{
453-
mTextFound = [ = ]( const QString & searchText )
454-
{
455-
QTreeView *treeView = qobject_cast<QTreeView *>( mWidget );
456-
if ( !treeView )
457-
return false;
458-
QModelIndexList hits = treeView->model()->match( treeView->model()->index( 0, 0 ), Qt::DisplayRole, searchText, 1, Qt::MatchContains | Qt::MatchRecursive );
459-
return !hits.isEmpty();
460-
};
461-
462-
if ( qobject_cast<QTreeWidget *>( widget ) )
463-
{
464-
mHighlight = [ = ]( const QString & searchText )
465-
{
466-
QTreeWidget *treeWidget = qobject_cast<QTreeWidget *>( widget );
467-
if ( treeWidget )
468-
{
469-
QList<QTreeWidgetItem *> items = treeWidget->findItems( searchText, Qt::MatchContains | Qt::MatchRecursive, 0 );
470-
mChangedStyle = items.count() ? true : false;
471-
mTreeInitialStyle.clear();
472-
mTreeInitialExpand.clear();
473-
for ( QTreeWidgetItem *item : items )
474-
{
475-
mTreeInitialStyle.insert( item, qMakePair( item->background( 0 ), item->foreground( 0 ) ) );
476-
item->setBackground( 0, QBrush( QColor( Qt::yellow ) ) );
477-
item->setForeground( 0, QBrush( QColor( Qt::blue ) ) );
478-
479-
QTreeWidgetItem *parent = item;
480-
while ( ( parent = parent->parent() ) )
481-
{
482-
if ( mTreeInitialExpand.contains( parent ) )
483-
break;
484-
mTreeInitialExpand.insert( parent, parent->isExpanded() );
485-
parent->setExpanded( true );
486-
}
487-
}
488-
}
489-
};
490-
491-
mReset = [ = ]()
492-
{
493-
for ( QTreeWidgetItem *item : mTreeInitialExpand.keys() )
494-
{
495-
if ( item )
496-
{
497-
item->setExpanded( mTreeInitialExpand.value( item ) );
498-
}
499-
}
500-
for ( QTreeWidgetItem *item : mTreeInitialStyle.keys() )
501-
{
502-
if ( item )
503-
{
504-
item->setBackground( 0, mTreeInitialStyle.value( item ).first );
505-
item->setForeground( 0, mTreeInitialStyle.value( item ).second );
506-
}
507-
}
508-
mTreeInitialStyle.clear();
509-
mTreeInitialExpand.clear();
510-
};
511-
}
512-
}
513-
else
514-
{
515-
mValid = false;
516-
}
517-
518-
if ( mValid )
519-
{
520-
connect( mWidget, &QWidget::destroyed, this, &QgsSearchHighlightOptionWidget::widgetDestroyed );
521-
}
522-
}
523-
524-
bool QgsSearchHighlightOptionWidget::searchHighlight( const QString &searchText )
525-
{
526-
mSearchText = searchText;
527-
bool found = false;
528-
if ( !mWidget )
529-
return found;
530-
531-
if ( !searchText.isEmpty() )
532-
{
533-
found = mTextFound( searchText );
534-
}
535-
536-
if ( found )
537-
{
538-
if ( mChangedStyle )
539-
{
540-
mReset();
541-
mChangedStyle = false;
542-
}
543-
if ( !mWidget->isVisible() )
544-
{
545-
// show the widget to get initial stylesheet in case it's modified
546-
QgsDebugMsg( QString( "installing event filter on: %1 (%2)" )
547-
.arg( mWidget->objectName() )
548-
.arg( qobject_cast<QLabel *>( mWidget ) ? qobject_cast<QLabel *>( mWidget )->text() : QString() ) );
549-
mWidget->installEventFilter( this );
550-
mInstalledFilter = true;
551-
}
552-
else
553-
{
554-
mHighlight( searchText );
555-
mChangedStyle = true;
556-
}
557-
}
558-
559-
return found;
560-
}
561-
562-
bool QgsSearchHighlightOptionWidget::eventFilter( QObject *obj, QEvent *event )
563-
{
564-
if ( mInstalledFilter && event->type() == QEvent::Show && obj == mWidget )
565-
{
566-
mWidget->removeEventFilter( this );
567-
mInstalledFilter = false;
568-
// instead of catching the event and calling show again
569-
// it might be better to use a timer to change the style
570-
// after the widget is shown
571-
#if 1
572-
mWidget->show();
573-
mHighlight( mSearchText );
574-
mChangedStyle = true;
575-
return true;
576-
#else
577-
QTimer::singleShot( 500, this, [ = ]
578-
{
579-
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
580-
mChangedStyle = true;
581-
} );
582-
#endif
583-
}
584-
return QObject::eventFilter( obj, event );
585-
}
586-
587-
void QgsSearchHighlightOptionWidget::reset()
588-
{
589-
if ( mWidget && mValid )
590-
{
591-
if ( mChangedStyle )
592-
{
593-
mReset();
594-
mChangedStyle = false;
595-
}
596-
if ( mInstalledFilter )
597-
{
598-
mWidget->removeEventFilter( this );
599-
mInstalledFilter = false;
600-
}
601-
}
602-
}
603-
604-
void QgsSearchHighlightOptionWidget::widgetDestroyed()
605-
{
606-
mWidget = nullptr;
607-
mValid = false;
608-
}

‎src/gui/qgsoptionsdialogbase.h

Lines changed: 2 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
#include <QDialog>
2727
#include <QPointer>
2828
#include <QStyledItemDelegate>
29-
#include <QMap>
30-
3129

3230
class QDialogButtonBox;
3331
class QListWidget;
@@ -37,69 +35,9 @@ class QPainter;
3735
class QStackedWidget;
3836
class QStyleOptionViewItem;
3937
class QSplitter;
40-
class QTreeWidgetItem;
4138

4239
class QgsFilterLineEdit;
43-
44-
/**
45-
* \ingroup gui
46-
* \class QgsSearchHighlightOptionWidget
47-
* Container for a widget to be used to search text in the option dialog
48-
* If the widget type is handled, it is valid.
49-
* It can perform a text search in the widget and highlight it in case of success.
50-
* This uses stylesheets.
51-
* \since QGIS 3.0
52-
*/
53-
class GUI_EXPORT QgsSearchHighlightOptionWidget : public QObject
54-
{
55-
Q_OBJECT
56-
public:
57-
58-
/**
59-
* Constructor
60-
* \param widget the widget used to search text into
61-
*/
62-
explicit QgsSearchHighlightOptionWidget( QWidget *widget = nullptr );
63-
64-
/**
65-
* Returns if it valid: if the widget type is handled and if the widget is not still available
66-
*/
67-
bool isValid() { return mWidget && mValid; }
68-
69-
/**
70-
* search for a text pattern and highlight the widget if the text is found
71-
* \returns true if the text pattern is found
72-
*/
73-
bool searchHighlight( const QString &searchText );
74-
75-
/**
76-
* reset the style to the original state
77-
*/
78-
void reset();
79-
80-
/**
81-
* return the widget
82-
*/
83-
QWidget *widget() { return mWidget; }
84-
85-
bool eventFilter( QObject *obj, QEvent *event ) override;
86-
87-
private slots:
88-
void widgetDestroyed();
89-
90-
private:
91-
QPointer< QWidget > mWidget;
92-
QString mSearchText = QString();
93-
// a map to save the tree state (backouground, font, expanded) before highlighting items
94-
QMap<QTreeWidgetItem *, QPair<QBrush, QBrush>> mTreeInitialStyle = QMap<QTreeWidgetItem *, QPair<QBrush, QBrush>>();
95-
QMap<QTreeWidgetItem *, bool> mTreeInitialExpand = QMap<QTreeWidgetItem *, bool>();
96-
bool mValid = true;
97-
bool mChangedStyle = false;
98-
std::function < bool( const QString & )> mTextFound = []( const QString &searchText ) {Q_UNUSED( searchText ); return false;};
99-
std::function < void( const QString & )> mHighlight = []( const QString &searchText ) {Q_UNUSED( searchText );};
100-
std::function < void()> mReset = []() {};
101-
bool mInstalledFilter = false;
102-
};
40+
class QgsOptionsDialogHighlightWidget;
10341

10442

10543
/**
@@ -190,7 +128,7 @@ class GUI_EXPORT QgsOptionsDialogBase : public QDialog
190128
*/
191129
void registerTextSearchWidgets();
192130

193-
QList< QPair< QgsSearchHighlightOptionWidget *, int > > mRegisteredSearchWidgets;
131+
QList< QPair< QgsOptionsDialogHighlightWidget *, int > > mRegisteredSearchWidgets;
194132

195133
QString mOptsKey;
196134
bool mInit;
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/***************************************************************************
2+
qgsoptionsdialoghighlightwidget.cpp
3+
-------------------------------
4+
Date : February 2018
5+
Copyright : (C) 2018 Denis Rouzaud
6+
Email : denis.rouzaud@gmail.com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include <QCheckBox>
17+
#include <QEvent>
18+
#include <QGroupBox>
19+
#include <QLabel>
20+
#include <QLayout>
21+
#include <QTimer>
22+
#include <QTreeView>
23+
#include <QTreeWidget>
24+
25+
#include "qgsoptionsdialoghighlightwidget.h"
26+
#include "qgsmessagebaritem.h"
27+
28+
#include "qgsoptionsdialoghighlightwidgetsimpl.h"
29+
30+
31+
32+
33+
QgsOptionsDialogHighlightWidget::QgsOptionsDialogHighlightWidget( QWidget *widget )
34+
: QObject( widget )
35+
, mWidget( widget )
36+
{}
37+
38+
QgsOptionsDialogHighlightWidget *QgsOptionsDialogHighlightWidget::createWidget( QWidget *widget )
39+
{
40+
QWidget *parent = widget;
41+
while ( ( parent = parent->parentWidget() ) )
42+
{
43+
// do not register message bar content, items disappear and causes QGIS to crash
44+
if ( qobject_cast< QgsMessageBarItem * >( parent ) )
45+
{
46+
// return invalid widget
47+
return nullptr;
48+
}
49+
}
50+
51+
if ( qobject_cast<QLabel *>( widget ) )
52+
{
53+
return new QgsOptionsDialogHighlightLabel( qobject_cast<QLabel *>( widget ) );
54+
}
55+
else if ( qobject_cast<QCheckBox *>( widget ) )
56+
{
57+
return new QgsOptionsDialogHighlightCheckBox( qobject_cast<QCheckBox *>( widget ) );
58+
}
59+
else if ( qobject_cast<QAbstractButton *>( widget ) )
60+
{
61+
return new QgsOptionsDialogHighlightButton( qobject_cast<QAbstractButton *>( widget ) );
62+
}
63+
else if ( qobject_cast<QGroupBox *>( widget ) )
64+
{
65+
return new QgsOptionsDialogHighlightGroupBox( qobject_cast<QGroupBox *>( widget ) );
66+
}
67+
else if ( qobject_cast<QTreeView *>( widget ) )
68+
{
69+
return new QgsOptionsDialogHighlightTree( qobject_cast<QTreeView *>( widget ) );
70+
}
71+
else
72+
{
73+
// return invalid widget
74+
return nullptr;
75+
}
76+
}
77+
78+
bool QgsOptionsDialogHighlightWidget::searchHighlight( const QString &text )
79+
{
80+
mSearchText = text;
81+
bool found = false;
82+
83+
if ( !mWidget )
84+
return found;
85+
86+
if ( mChangedStyle )
87+
{
88+
reset();
89+
mChangedStyle = false;
90+
}
91+
92+
if ( mInstalledFilter )
93+
{
94+
mWidget->removeEventFilter( this );
95+
mInstalledFilter = false;
96+
}
97+
98+
if ( !text.isEmpty() )
99+
{
100+
found = searchText( text );
101+
}
102+
103+
if ( found )
104+
{
105+
106+
if ( !mWidget->isVisible() )
107+
{
108+
mWidget->installEventFilter( this );
109+
mInstalledFilter = true;
110+
}
111+
else
112+
{
113+
mChangedStyle = highlightText( text );
114+
}
115+
}
116+
117+
return found;
118+
}
119+
120+
bool QgsOptionsDialogHighlightWidget::eventFilter( QObject *obj, QEvent *event )
121+
{
122+
if ( mInstalledFilter && event->type() == QEvent::Show && obj == mWidget )
123+
{
124+
mWidget->removeEventFilter( this );
125+
mInstalledFilter = false;
126+
// instead of catching the event and calling show again
127+
// it might be better to use a timer to change the style
128+
// after the widget is shown
129+
#if 1
130+
mWidget->show();
131+
mChangedStyle = highlightText( mSearchText );
132+
return true;
133+
#else
134+
QTimer::singleShot( 500, this, [ = ]
135+
{
136+
mChangedStyle = highlightText( mSearchText );
137+
} );
138+
#endif
139+
}
140+
return QObject::eventFilter( obj, event );
141+
}
142+
143+
144+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/***************************************************************************
2+
qgsoptionsdialoghighlightwidget.h
3+
-------------------------------
4+
Date : February 2018
5+
Copyright : (C) 2018 Denis Rouzaud
6+
Email : denis.rouzaud@gmail.com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSOPTIONSDIALOGHIGHLIGHTWIDGET_H
17+
#define QGSOPTIONSDIALOGHIGHLIGHTWIDGET_H
18+
19+
#include <QObject>
20+
#include <QPointer>
21+
#include <QWidget>
22+
23+
#include "qgis_gui.h"
24+
#include "qgis_sip.h"
25+
26+
/**
27+
* \ingroup gui
28+
* \class QgsSearchHighlightOptionWidget
29+
* Container for a widget to be used to search text in the option dialog
30+
* If the widget type is handled, it is valid.
31+
* It can perform a text search in the widget and highlight it in case of success.
32+
* This uses stylesheets.
33+
* \since QGIS 3.0
34+
*/
35+
class GUI_EXPORT QgsOptionsDialogHighlightWidget : public QObject
36+
{
37+
38+
Q_OBJECT
39+
public:
40+
static QgsOptionsDialogHighlightWidget *createWidget( QWidget *widget ) SIP_FACTORY;
41+
42+
/**
43+
* Returns if it valid: if the widget type is handled and if the widget is not still available
44+
*/
45+
bool isValid() { return !mWidget.isNull(); }
46+
47+
/**
48+
* search for a text pattern and highlight the widget if the text is found
49+
* \returns true if the text pattern is found
50+
*/
51+
bool searchHighlight( const QString &text );
52+
53+
/**
54+
* Return the widget
55+
*/
56+
QWidget *widget() {return mWidget;}
57+
58+
59+
bool eventFilter( QObject *obj, QEvent *event ) override;
60+
61+
62+
protected:
63+
64+
/**
65+
* Search for the \a text in the widget and return true if it was found
66+
*/
67+
virtual bool searchText( const QString &text ) = 0;
68+
69+
/**
70+
* Highlight the \a text in the widget.
71+
* \return true if the text could be highlighted.
72+
*/
73+
virtual bool highlightText( const QString &text ) = 0;
74+
75+
/**
76+
* reset the style of the widgets to its original state
77+
*/
78+
virtual void reset() = 0;
79+
80+
/**
81+
* Constructor
82+
* \param widget the widget used to search text into
83+
*/
84+
explicit QgsOptionsDialogHighlightWidget( QWidget *widget = nullptr );
85+
86+
//! Pointer to the widget
87+
QPointer< QWidget > mWidget;
88+
89+
private:
90+
QString mSearchText = QString();
91+
bool mChangedStyle = false;
92+
bool mInstalledFilter = false;
93+
};
94+
95+
#endif // QGSOPTIONSDIALOGHIGHLIGHTWIDGET_H
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/***************************************************************************
2+
qgsoptionsdialoghighlightwidgetsimpl.cpp
3+
-------------------------------
4+
Date : February 2018
5+
Copyright : (C) 2018 Denis Rouzaud
6+
Email : denis.rouzaud@gmail.com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include <QCheckBox>
17+
#include <QDialog>
18+
#include <QDialogButtonBox>
19+
#include <QEvent>
20+
#include <QGroupBox>
21+
#include <QLabel>
22+
#include <QTreeView>
23+
#include <QTreeWidget>
24+
#include <QAbstractItemModel>
25+
26+
#include "qgsoptionsdialoghighlightwidget.h"
27+
#include "qgsmessagebaritem.h"
28+
29+
#include "qgsoptionsdialoghighlightwidgetsimpl.h"
30+
31+
32+
33+
// ****************
34+
// QLabel
35+
QgsOptionsDialogHighlightLabel::QgsOptionsDialogHighlightLabel( QLabel *label )
36+
: QgsOptionsDialogHighlightWidget( label )
37+
, mLabel( label )
38+
{}
39+
40+
bool QgsOptionsDialogHighlightLabel::searchText( const QString &text )
41+
{
42+
if ( !mLabel )
43+
return false;
44+
45+
return mLabel->text().contains( text, Qt::CaseInsensitive );
46+
}
47+
48+
bool QgsOptionsDialogHighlightLabel::highlightText( const QString &text )
49+
{
50+
if ( !mWidget )
51+
return false;
52+
Q_UNUSED( text );
53+
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );

Comment on line R53

nyalldawson commented on Feb 4, 2018

@nyalldawson
Collaborator

@3nids idea: what about searching tooltip text too?

3nids replied on Feb 5, 2018

@3nids
MemberAuthor

@nyalldawson not sure, the tool tip wouldn't be displayed... No strong opinion here. I would follow!

Code has comments. Press enter to view.
54+
return true;
55+
}
56+
57+
void QgsOptionsDialogHighlightLabel::reset()
58+
{
59+
if ( !mWidget )
60+
return;
61+
QString ss = mWidget->styleSheet();
62+
ss.remove( mStyleSheet );
63+
mWidget->setStyleSheet( ss );
64+
}
65+
66+
// ****************
67+
// QCheckBox
68+
QgsOptionsDialogHighlightCheckBox::QgsOptionsDialogHighlightCheckBox( QCheckBox *checkBox )
69+
: QgsOptionsDialogHighlightWidget( checkBox )
70+
, mCheckBox( checkBox )
71+
{
72+
}
73+
74+
bool QgsOptionsDialogHighlightCheckBox::searchText( const QString &text )
75+
{
76+
if ( !mCheckBox )
77+
return false;
78+
79+
return mCheckBox->text().contains( text, Qt::CaseInsensitive );
80+
81+
}
82+
83+
bool QgsOptionsDialogHighlightCheckBox::highlightText( const QString &text )
84+
{
85+
if ( !mWidget )
86+
return false;
87+
Q_UNUSED( text );
88+
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
89+
return true;
90+
}
91+
92+
void QgsOptionsDialogHighlightCheckBox::reset()
93+
{
94+
if ( !mWidget )
95+
return;
96+
QString ss = mWidget->styleSheet();
97+
ss.remove( mStyleSheet );
98+
mWidget->setStyleSheet( ss );
99+
}
100+
101+
// ****************
102+
// QAbstractButton
103+
QgsOptionsDialogHighlightButton::QgsOptionsDialogHighlightButton( QAbstractButton *button )
104+
: QgsOptionsDialogHighlightWidget( button )
105+
, mButton( button )
106+
{
107+
}
108+
109+
bool QgsOptionsDialogHighlightButton::searchText( const QString &text )
110+
{
111+
if ( !mButton )
112+
return false;
113+
114+
return mButton->text().contains( text, Qt::CaseInsensitive );
115+
116+
}
117+
118+
bool QgsOptionsDialogHighlightButton::highlightText( const QString &text )
119+
{
120+
if ( !mWidget )
121+
return false;
122+
Q_UNUSED( text );
123+
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
124+
return true;
125+
}
126+
127+
void QgsOptionsDialogHighlightButton::reset()
128+
{
129+
if ( !mWidget )
130+
return;
131+
QString ss = mWidget->styleSheet();
132+
ss.remove( mStyleSheet );
133+
mWidget->setStyleSheet( ss );
134+
}
135+
136+
// ****************
137+
// QGroupBox
138+
QgsOptionsDialogHighlightGroupBox::QgsOptionsDialogHighlightGroupBox( QGroupBox *groupBox )
139+
: QgsOptionsDialogHighlightWidget( groupBox )
140+
, mGroupBox( groupBox )
141+
{
142+
}
143+
144+
bool QgsOptionsDialogHighlightGroupBox::searchText( const QString &text )
145+
{
146+
if ( !mGroupBox )
147+
return false;
148+
149+
return mGroupBox->title().contains( text, Qt::CaseInsensitive );
150+
}
151+
152+
bool QgsOptionsDialogHighlightGroupBox::highlightText( const QString &text )
153+
{
154+
Q_UNUSED( text );
155+
if ( !mWidget )
156+
return false;
157+
158+
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
159+
return true;
160+
}
161+
162+
void QgsOptionsDialogHighlightGroupBox::reset()
163+
{
164+
if ( !mWidget )
165+
return;
166+
QString ss = mWidget->styleSheet();
167+
ss.remove( mStyleSheet );
168+
mWidget->setStyleSheet( ss );
169+
}
170+
171+
// ****************
172+
// QTreeView
173+
QgsOptionsDialogHighlightTree::QgsOptionsDialogHighlightTree( QTreeView *treeView )
174+
: QgsOptionsDialogHighlightWidget( treeView )
175+
, mTreeView( treeView )
176+
{
177+
}
178+
179+
bool QgsOptionsDialogHighlightTree::searchText( const QString &text )
180+
{
181+
if ( !mTreeView )
182+
return false;
183+
QModelIndexList hits = mTreeView->model()->match( mTreeView->model()->index( 0, 0 ), Qt::DisplayRole, text, 1, Qt::MatchContains | Qt::MatchRecursive );
184+
return !hits.isEmpty();
185+
}
186+
187+
bool QgsOptionsDialogHighlightTree::highlightText( const QString &text )
188+
{
189+
bool success = false;
190+
QTreeWidget *treeWidget = qobject_cast<QTreeWidget *>( mTreeView );
191+
if ( treeWidget )
192+
{
193+
QList<QTreeWidgetItem *> items = treeWidget->findItems( text, Qt::MatchContains | Qt::MatchRecursive, 0 );
194+
success = items.count() ? true : false;
195+
mTreeInitialStyle.clear();
196+
mTreeInitialExpand.clear();
197+
for ( QTreeWidgetItem *item : items )
198+
{
199+
mTreeInitialStyle.insert( item, qMakePair( item->background( 0 ), item->foreground( 0 ) ) );
200+
item->setBackground( 0, QBrush( QColor( Qt::yellow ) ) );
201+
item->setForeground( 0, QBrush( QColor( Qt::blue ) ) );
202+
203+
QTreeWidgetItem *parent = item;
204+
while ( ( parent = parent->parent() ) )
205+
{
206+
if ( mTreeInitialExpand.contains( parent ) )
207+
break;
208+
mTreeInitialExpand.insert( parent, parent->isExpanded() );
209+
parent->setExpanded( true );
210+
}
211+
}
212+
}
213+
214+
return success;
215+
}
216+
217+
void QgsOptionsDialogHighlightTree::reset()
218+
{
219+
if ( !mTreeView )
220+
return;
221+
222+
QTreeWidget *treeWidget = qobject_cast<QTreeWidget *>( mTreeView );
223+
if ( treeWidget )
224+
{
225+
for ( QTreeWidgetItem *item : mTreeInitialExpand.keys() )
226+
{
227+
if ( item )
228+
{
229+
item->setExpanded( mTreeInitialExpand.value( item ) );
230+
}
231+
}
232+
for ( QTreeWidgetItem *item : mTreeInitialStyle.keys() )
233+
{
234+
if ( item )
235+
{
236+
item->setBackground( 0, mTreeInitialStyle.value( item ).first );
237+
item->setForeground( 0, mTreeInitialStyle.value( item ).second );
238+
}
239+
}
240+
mTreeInitialStyle.clear();
241+
mTreeInitialExpand.clear();
242+
}
243+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/***************************************************************************
2+
qgsoptionsdialoghighlightwidgetsimpl.h
3+
-------------------------------
4+
Date : February 2018
5+
Copyright : (C) 2018 Denis Rouzaud
6+
Email : denis.rouzaud@gmail.com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSOPTIONSDIALOGHIGHLIGHTWIDGETSIMPL_H
17+
#define QGSOPTIONSDIALOGHIGHLIGHTWIDGETSIMPL_H
18+
19+
#include <QObject>
20+
#include <QMap>
21+
#include <QBrush>
22+
23+
24+
#include "qgis_gui.h"
25+
#include "qgis_sip.h"
26+
#include "qgsoptionsdialoghighlightwidget.h"
27+
28+
class QLabel;
29+
class QCheckBox;
30+
class QAbstractButton;
31+
class QGroupBox;
32+
class QTreeView;
33+
class QTreeWidgetItem;
34+
35+
36+
/**
37+
* \ingroup gui
38+
* \class QgsOptionsDialogHighlightLabel
39+
* A highlight widget for labels.
40+
* This is used to search and highlight text in QgsOptionsDialogBase implementations.
41+
*/
42+
class GUI_EXPORT QgsOptionsDialogHighlightLabel : public QgsOptionsDialogHighlightWidget
43+
{
44+
Q_OBJECT
45+
public:
46+
//! constructs a highlight widget for a label
47+
QgsOptionsDialogHighlightLabel( QLabel *label );
48+
protected:
49+
virtual bool searchText( const QString &text ) override;
50+
virtual bool highlightText( const QString &text ) override;
51+
virtual void reset() override;
52+
QPointer<QLabel> mLabel;
53+
QString mStyleSheet = QStringLiteral( /*!search!*/"QLabel { background-color: yellow; color: blue;}/*!search!*/" );
54+
};
55+
56+
/**
57+
* \ingroup gui
58+
* \class QgsOptionsDialogHighlightCheckBox
59+
* A highlight widget for checkboxes.
60+
* This is used to search and highlight text in QgsOptionsDialogBase implementations.
61+
*/
62+
class GUI_EXPORT QgsOptionsDialogHighlightCheckBox : public QgsOptionsDialogHighlightWidget
63+
{
64+
Q_OBJECT
65+
public:
66+
//! constructs a highlight widget for a checkbox
67+
QgsOptionsDialogHighlightCheckBox( QCheckBox *checkBox );
68+
protected:
69+
virtual bool searchText( const QString &text ) override;
70+
virtual bool highlightText( const QString &text ) override;
71+
virtual void reset() override;
72+
QPointer<QCheckBox> mCheckBox;
73+
QString mStyleSheet = QStringLiteral( "/*!search!*/QCheckBox { background-color: yellow; color: blue;}/*!search!*/" );
74+
};
75+
76+
/**
77+
* \ingroup gui
78+
* \class QgsOptionsDialogHighlightButton
79+
* A highlight widget for buttons.
80+
* This is used to search and highlight text in QgsOptionsDialogBase implementations.
81+
*/
82+
class GUI_EXPORT QgsOptionsDialogHighlightButton : public QgsOptionsDialogHighlightWidget
83+
{
84+
Q_OBJECT
85+
public:
86+
//! constructs a highlight widget for a button.
87+
QgsOptionsDialogHighlightButton( QAbstractButton *button );
88+
protected:
89+
virtual bool searchText( const QString &text ) override;
90+
virtual bool highlightText( const QString &text ) override;
91+
virtual void reset() override;
92+
QPointer<QAbstractButton> mButton;
93+
QString mStyleSheet = QStringLiteral( "/*!search!*/QAbstractButton { background-color: yellow; color: blue;}/*!search!*/" );
94+
};
95+
96+
/**
97+
* \ingroup gui
98+
* \class QgsOptionsDialogHighlightGroupBox
99+
* A highlight widget for group boxes.
100+
* This is used to search and highlight text in QgsOptionsDialogBase implementations.
101+
*/
102+
class GUI_EXPORT QgsOptionsDialogHighlightGroupBox : public QgsOptionsDialogHighlightWidget
103+
{
104+
Q_OBJECT
105+
public:
106+
//! constructs a highlight widget for a group box.
107+
QgsOptionsDialogHighlightGroupBox( QGroupBox *groupBox );
108+
protected:
109+
virtual bool searchText( const QString &text ) override;
110+
virtual bool highlightText( const QString &text ) override;
111+
virtual void reset() override;
112+
QPointer<QGroupBox> mGroupBox;
113+
QString mStyleSheet = QStringLiteral( "/*!search!*/QGroupBox::title { background-color: yellow; color: blue;}/*!search!*/" );
114+
};
115+
116+
/**
117+
* \ingroup gui
118+
* \class QgsOptionsDialogHighlightTree
119+
* A highlight widget for trees.
120+
* This is used to search and highlight text in QgsOptionsDialogBase implementations.
121+
* Highlighting is only available for tree widgets only while searching can be performed
122+
* in any tree view or inherited class.
123+
*/
124+
class GUI_EXPORT QgsOptionsDialogHighlightTree : public QgsOptionsDialogHighlightWidget
125+
{
126+
Q_OBJECT
127+
public:
128+
//! constructs a highlight widget for a tree view or widget.
129+
QgsOptionsDialogHighlightTree( QTreeView *treeView );
130+
protected:
131+
virtual bool searchText( const QString &text ) override;
132+
virtual bool highlightText( const QString &text ) override;
133+
virtual void reset() override;
134+
QPointer<QTreeView> mTreeView;
135+
// a map to save the tree state (backouground, font, expanded) before highlighting items
136+
QMap<QTreeWidgetItem *, QPair<QBrush, QBrush>> mTreeInitialStyle = QMap<QTreeWidgetItem *, QPair<QBrush, QBrush>>();
137+
QMap<QTreeWidgetItem *, bool> mTreeInitialExpand = QMap<QTreeWidgetItem *, bool>();
138+
};
139+
#endif // QGSOPTIONSDIALOGHIGHLIGHTWIDGETSIMPL_H

0 commit comments

Comments
 (0)
Please sign in to comment.