Search feature for layer list

To enable the search feature just type into the layer list.

This change set also contains some more fixes:
 * Tab/Backtab now is functional also before the
   Display menu has been used the first time
 * Arrow keys work from the cell list again
This commit is contained in:
Matthias Koefferlein
2017-03-02 23:45:37 +01:00
parent 08076de815
commit 5c414ef0be
14 changed files with 486 additions and 208 deletions
-15
View File
@@ -756,21 +756,6 @@ CellTreeModel::search_children (const tl::GlobPattern &pattern, CellTreeItem *it
}
}
void
CellTreeModel::search_children (const char *name, CellTreeItem *item)
{
int children = item->children ();
for (int i = 0; i < children; ++i) {
CellTreeItem *c = item->child (i);
if (c) {
if (c->name_equals (name)) {
m_selected_indexes.push_back (model_index (c));
}
search_children (name, c);
}
}
}
QModelIndex
CellTreeModel::locate (const char *name, bool glob_pattern, bool case_sensitive, bool top_only)
{
-1
View File
@@ -238,7 +238,6 @@ private:
void build_top_level ();
void clear_top_level ();
void search_children (const tl::GlobPattern &pattern, CellTreeItem *item);
void search_children (const char *name, CellTreeItem *item);
};
/**
+16 -8
View File
@@ -69,13 +69,9 @@ public:
// --------------------------------------------------------------------
// HCPCellTreeWidget implementation
HCPCellTreeWidget::HCPCellTreeWidget (QWidget *parent, const char *name)
: QTreeView (parent)
HCPCellTreeWidget::HCPCellTreeWidget (QWidget *parent, const char *name, QWidget *key_event_receiver)
: QTreeView (parent), mp_key_event_receiver (key_event_receiver)
{
// Don't request focus: this leaves focus on the canvas and the arrow keys functional there
// @@@ setFocusPolicy (Qt::NoFocus); -> solve differently!!!
setFocusPolicy (Qt::ClickFocus);
// Allow dragging from here to
setDragDropMode (QAbstractItemView::DragOnly);
@@ -97,12 +93,24 @@ HCPCellTreeWidget::event (QEvent *event)
return QTreeView::event (event);
}
bool
HCPCellTreeWidget::focusNextPrevChild (bool /*next*/)
{
return false;
}
void
HCPCellTreeWidget::keyPressEvent (QKeyEvent *event)
{
QString t = event->text ();
if (!t.isEmpty ()) {
if (!t.isEmpty () && t[0].isPrint ()) {
emit search_triggered (t);
} else if (mp_key_event_receiver) {
// send other key events to the alternative receiver - this way we can make the
// view receive arrow keys for panning.
QCoreApplication::sendEvent (mp_key_event_receiver, event);
} else {
return QTreeView::keyPressEvent (event);
}
}
@@ -885,7 +893,7 @@ HierarchyControlPanel::do_update_content (int cv_index)
header->setVisible (split_mode);
cl_ly->addWidget (header);
HCPCellTreeWidget *cell_list = new HCPCellTreeWidget (cl_frame, "tree");
HCPCellTreeWidget *cell_list = new HCPCellTreeWidget (cl_frame, "tree", mp_view->view_object_widget ());
cl_ly->addWidget (cell_list);
cell_list->setStyle (mp_tree_style.get ());
cell_list->setModel (new CellTreeModel (cell_list, mp_view, cv_index, m_flat ? CellTreeModel::Flat : 0, 0, m_sorting));
+12 -1
View File
@@ -65,7 +65,7 @@ class HCPCellTreeWidget
Q_OBJECT
public:
HCPCellTreeWidget (QWidget *parent, const char *name);
HCPCellTreeWidget (QWidget *parent, const char *name, QWidget *key_event_receiver);
signals:
void cell_clicked (const QModelIndex &);
@@ -78,8 +78,11 @@ protected:
virtual void mousePressEvent (QMouseEvent *event);
virtual void mouseReleaseEvent (QMouseEvent *event);
virtual void startDrag (Qt::DropActions supportedActions);
virtual bool focusNextPrevChild (bool next);
virtual void keyPressEvent (QKeyEvent *event);
virtual bool event (QEvent *event);
QWidget *mp_key_event_receiver;
};
/**
@@ -255,6 +258,14 @@ public:
return m_split_mode;
}
/**
* @brief Gets the layout view this panel is attached to
*/
lay::LayoutView *view ()
{
return mp_view;
}
signals:
void cell_selected (cell_path_type path, int cellview_index);
void active_cellview_changed (int cellview_index);
+172 -4
View File
@@ -97,9 +97,6 @@ private:
LCPTreeWidget::LCPTreeWidget (QWidget *parent, lay::LayerTreeModel *model, const char *name)
: QTreeView (parent), mp_model (model)
{
// Don't request focus: this leaves focus on the canvas and the arrow keys functional there
setFocusPolicy (Qt::NoFocus);
setObjectName (QString::fromUtf8 (name));
setModel (model);
setItemDelegate (new LCPItemDelegate (this));
@@ -149,7 +146,38 @@ LCPTreeWidget::mouseDoubleClickEvent (QMouseEvent *event)
}
}
void
bool
LCPTreeWidget::focusNextPrevChild (bool /*next*/)
{
return false;
}
bool
LCPTreeWidget::event (QEvent *event)
{
// Handling this event makes the widget receive all keystrokes
if (event->type () == QEvent::ShortcutOverride) {
QKeyEvent *ke = static_cast<QKeyEvent *> (event);
QString t = ke->text ();
if (!t.isEmpty () && t[0].isPrint ()) {
ke->accept ();
}
}
return QTreeView::event (event);
}
void
LCPTreeWidget::keyPressEvent (QKeyEvent *event)
{
QString t = event->text ();
if (!t.isEmpty () && t[0].isPrint ()) {
emit search_triggered (t);
} else {
QTreeView::keyPressEvent (event);
}
}
void
LCPTreeWidget::collapse_all ()
{
#if QT_VERSION >= 0x040200
@@ -299,6 +327,70 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage
l->setMargin (0);
l->setSpacing (0);
mp_search_frame = new QFrame (this);
l->addWidget (mp_search_frame);
mp_search_frame->hide ();
mp_search_frame->setAutoFillBackground (true);
mp_search_frame->setObjectName (QString::fromUtf8 ("panel"));
mp_search_frame->setFrameStyle (QFrame::Panel | QFrame::Raised);
mp_search_frame->setLineWidth (1);
mp_search_frame->setBackgroundRole (QPalette::Highlight);
QHBoxLayout *sf_ly = new QHBoxLayout (mp_search_frame);
sf_ly->setMargin (0);
sf_ly->setContentsMargins (0, 0, 0, 0);
sf_ly->setSpacing (0);
mp_search_close_cb = new QCheckBox (mp_search_frame);
sf_ly->addWidget (mp_search_close_cb);
mp_search_close_cb->setFocusPolicy (Qt::NoFocus);
mp_search_close_cb->setBackgroundRole (QPalette::Highlight);
mp_search_close_cb->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred));
QPalette pl (mp_search_close_cb->palette ());
pl.setColor (QPalette::Foreground, pl.color (QPalette::Active, QPalette::HighlightedText));
mp_search_close_cb->setPalette (pl);
mp_search_close_cb->setMaximumSize (QSize (mp_search_close_cb->maximumSize ().width (), mp_search_close_cb->sizeHint ().height () - 4));
connect (mp_search_close_cb, SIGNAL (clicked ()), this, SLOT (search_editing_finished ()));
mp_search_edit_box = new lay::DecoratedLineEdit (mp_search_frame);
mp_search_edit_box->setObjectName (QString::fromUtf8 ("cellview_search_edit_box"));
mp_search_edit_box->set_escape_signal_enabled (true);
mp_search_edit_box->set_tab_signal_enabled (true);
connect (mp_search_edit_box, SIGNAL (returnPressed ()), this, SLOT (search_editing_finished ()));
connect (mp_search_edit_box, SIGNAL (textEdited (const QString &)), this, SLOT (search_edited ()));
connect (mp_search_edit_box, SIGNAL (esc_pressed ()), this, SLOT (search_editing_finished ()));
connect (mp_search_edit_box, SIGNAL (tab_pressed ()), this, SLOT (search_next ()));
connect (mp_search_edit_box, SIGNAL (backtab_pressed ()), this, SLOT (search_prev ()));
sf_ly->addWidget (mp_search_edit_box);
mp_use_regular_expressions = new QAction (this);
mp_use_regular_expressions->setCheckable (true);
mp_use_regular_expressions->setChecked (true);
mp_use_regular_expressions->setText (tr ("Use expressions (use * and ? for any character)"));
mp_case_sensitive = new QAction (this);
mp_case_sensitive->setCheckable (true);
mp_case_sensitive->setChecked (true);
mp_case_sensitive->setText (tr ("Case sensitive search"));
QMenu *m = new QMenu (mp_search_edit_box);
m->addAction (mp_use_regular_expressions);
m->addAction (mp_case_sensitive);
connect (mp_use_regular_expressions, SIGNAL (triggered ()), this, SLOT (search_edited ()));
connect (mp_case_sensitive, SIGNAL (triggered ()), this, SLOT (search_edited ()));
mp_search_edit_box->set_clear_button_enabled (true);
mp_search_edit_box->set_options_button_enabled (true);
mp_search_edit_box->set_options_menu (m);
QToolButton *sf_next = new QToolButton (mp_search_frame);
sf_next->setAutoRaise (true);
sf_next->setToolTip (tr ("Find next"));
sf_next->setIcon (QIcon (QString::fromUtf8 (":/find.png")));
connect (sf_next, SIGNAL (clicked ()), this, SLOT (search_next ()));
sf_ly->addWidget (sf_next);
mp_tab_bar = new QTabBar (this);
mp_tab_bar->setObjectName (QString::fromUtf8 ("lcp_tabs"));
connect (mp_tab_bar, SIGNAL (currentChanged (int)), this, SLOT (tab_selected (int)));
@@ -321,6 +413,7 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage
connect (mp_layer_list, SIGNAL (double_clicked (const QModelIndex &, Qt::KeyboardModifiers)), this, SLOT (double_clicked (const QModelIndex &, Qt::KeyboardModifiers)));
connect (mp_layer_list, SIGNAL (collapsed (const QModelIndex &)), this, SLOT (group_collapsed (const QModelIndex &)));
connect (mp_layer_list, SIGNAL (expanded (const QModelIndex &)), this, SLOT (group_expanded (const QModelIndex &)));
connect (mp_layer_list, SIGNAL (search_triggered (const QString &)), this, SLOT (search_triggered (const QString &)));
mp_layer_list->setContextMenuPolicy (Qt::CustomContextMenu);
connect (mp_layer_list, SIGNAL(customContextMenuRequested (const QPoint &)), this, SLOT (context_menu (const QPoint &)));
mp_layer_list->header ()->hide ();
@@ -1130,6 +1223,77 @@ LayerControlPanel::clear_selection ()
set_selection (empty_sel);
}
void
LayerControlPanel::search_triggered (const QString &t)
{
if (mp_model) {
mp_search_close_cb->setChecked (true);
mp_search_frame->show ();
mp_search_edit_box->setText (t);
mp_search_edit_box->setFocus ();
search_edited ();
}
}
void
LayerControlPanel::search_edited ()
{
if (! mp_model) {
return;
}
QString t = mp_search_edit_box->text ();
if (t.isEmpty ()) {
mp_model->clear_locate ();
mp_layer_list->setCurrentIndex (QModelIndex ());
} else {
QModelIndex found = mp_model->locate (t.toUtf8 ().constData (), mp_use_regular_expressions->isChecked (), mp_case_sensitive->isChecked (), false);
mp_layer_list->setCurrentIndex (found);
if (found.isValid ()) {
mp_layer_list->scrollTo (found);
}
}
}
void
LayerControlPanel::search_next ()
{
if (! mp_model) {
return;
}
QModelIndex found = mp_model->locate_next ();
if (found.isValid ()) {
mp_layer_list->setCurrentIndex (found);
mp_layer_list->scrollTo (found);
}
}
void
LayerControlPanel::search_prev ()
{
if (! mp_model) {
return;
}
QModelIndex found = mp_model->locate_prev ();
if (found.isValid ()) {
mp_layer_list->setCurrentIndex (found);
mp_layer_list->scrollTo (found);
}
}
void
LayerControlPanel::search_editing_finished ()
{
if (! mp_model) {
return;
}
mp_model->clear_locate ();
mp_search_frame->hide ();
}
void
LayerControlPanel::cm_regroup_flatten ()
{
@@ -1727,6 +1891,10 @@ set_hidden_flags_rec (LayerTreeModel *model, QTreeView *tree_view, const QModelI
void
LayerControlPanel::do_update_content ()
{
// clear search. TODO: update search instead of clearing
mp_search_edit_box->clear ();
mp_model->clear_locate();
mp_model->set_phase (m_phase);
if (m_tabs_need_update) {
+18
View File
@@ -44,6 +44,7 @@
#include "layDitherPattern.h"
#include "layLayerProperties.h"
#include "layLayerTreeModel.h"
#include "layWidgets.h"
#include "dbObject.h"
#include "tlDeferredExecution.h"
@@ -52,6 +53,7 @@ class QModelIndex;
class QMenu;
class QLabel;
class QTabBar;
class QCheckBox;
namespace lay
{
@@ -85,6 +87,12 @@ public:
signals:
void double_clicked (const QModelIndex &, Qt::KeyboardModifiers);
void search_triggered (const QString &t);
protected:
virtual void keyPressEvent (QKeyEvent *event);
virtual bool event (QEvent *event);
virtual bool focusNextPrevChild (bool next);
private:
lay::LayerTreeModel *mp_model;
@@ -322,6 +330,11 @@ public slots:
void upup_clicked ();
void down_clicked ();
void downdown_clicked ();
void search_triggered (const QString &t);
void search_edited ();
void search_editing_finished ();
void search_next ();
void search_prev ();
private:
QTabBar *mp_tab_bar;
@@ -341,6 +354,11 @@ private:
std::set<unsigned int> m_expanded;
bool m_no_stipples;
QLabel *m_no_stipples_label;
lay::DecoratedLineEdit *mp_search_edit_box;
QAction *mp_case_sensitive;
QAction *mp_use_regular_expressions;
QFrame *mp_search_frame;
QCheckBox *mp_search_close_cb;
void clear_selection ();
+99
View File
@@ -27,6 +27,7 @@
#include "dbLayoutUtils.h"
#include "tlLog.h"
#include "tlTimer.h"
#include "tlGlobPattern.h"
#include <QTreeView>
#include <QModelIndex>
@@ -278,6 +279,92 @@ LayerTreeModel::index (int row, int column, const QModelIndex &parent) const
}
}
void
LayerTreeModel::clear_locate ()
{
m_selected_indexes.clear ();
m_current_index = m_selected_indexes.begin ();
m_selected_ids.clear ();
signal_data_changed ();
}
QModelIndex
LayerTreeModel::locate_next ()
{
if (m_current_index == m_selected_indexes.end ()) {
return QModelIndex ();
} else {
++m_current_index;
if (m_current_index == m_selected_indexes.end ()) {
m_current_index = m_selected_indexes.begin ();
}
return *m_current_index;
}
}
QModelIndex
LayerTreeModel::locate_prev ()
{
if (m_current_index == m_selected_indexes.end ()) {
return QModelIndex ();
} else {
if (m_current_index == m_selected_indexes.begin ()) {
m_current_index = m_selected_indexes.end ();
}
--m_current_index;
return *m_current_index;
}
}
void
LayerTreeModel::search_children (const tl::GlobPattern &pattern, const QModelIndex &parent, bool recurse)
{
int children = rowCount (parent);
for (int i = 0; i < children; ++i) {
QModelIndex child = index (i, 0, parent);
lay::LayerPropertiesConstIterator iter (iterator (child));
if (!iter.is_null () && !iter.at_end () &&
pattern.match (iter->display_string (mp_view, true /*real*/))) {
m_selected_indexes.push_back (child);
}
if (recurse && iter->has_children ()) {
search_children (pattern, child, recurse);
}
}
}
QModelIndex
LayerTreeModel::locate (const char *name, bool glob_pattern, bool case_sensitive, bool top_only)
{
m_selected_indexes.clear ();
tl::GlobPattern p = tl::GlobPattern (std::string (name));
p.set_case_sensitive (case_sensitive);
p.set_exact (!glob_pattern);
p.set_header_match (true);
search_children (p, QModelIndex (), !top_only);
m_selected_ids.clear ();
for (std::vector<QModelIndex>::const_iterator i = m_selected_indexes.begin (); i != m_selected_indexes.end (); ++i) {
m_selected_ids.insert (size_t (i->internalPointer ()));
}
signal_data_changed ();
m_current_index = m_selected_indexes.begin ();
if (m_current_index == m_selected_indexes.end ()) {
return QModelIndex ();
} else {
return *m_current_index;
}
}
void
LayerTreeModel::signal_data_changed ()
{
@@ -648,6 +735,18 @@ LayerTreeModel::data (const QModelIndex &index, int role) const
}
} else if (role == Qt::BackgroundRole) {
if (m_selected_ids.find (size_t (index.internalPointer ())) != m_selected_ids.end ()) {
// for selected items pick a color between Highlight and Base
QPalette pl (mp_view->palette ());
QColor c1 = pl.color (QPalette::Highlight);
QColor cb = pl.color (QPalette::Base);
return QVariant (QColor ((c1.red () + cb.red ()) / 2, (c1.green () + cb.green ()) / 2, (c1.blue () + cb.blue ()) / 2));
} else {
return QVariant ();
}
} else if (role == Qt::TextColorRole || role == Qt::FontRole) {
if (index.column () == 1) {
+33
View File
@@ -38,6 +38,11 @@ namespace db
class Layout;
}
namespace tl
{
class GlobPattern;
}
namespace lay
{
@@ -161,6 +166,29 @@ public:
*/
void signal_data_changed ();
/**
* @brief Locate an index by name (at least closest)
*
* If top_only is set, only top-level items are searched. An invalid model index is returned if
* no corresponding item could be found.
*/
QModelIndex locate (const char *name, bool glob_pattern = false, bool case_sensitive = true, bool top_only = true);
/**
* @brief Locate the next index (after the first locate)
*/
QModelIndex locate_next ();
/**
* @brief Locate the previous index (after the first locate)
*/
QModelIndex locate_prev ();
/**
* @brief Clears the locate flags
*/
void clear_locate ();
/**
* @brief Set the test_shapes_in_view flag
*
@@ -194,6 +222,11 @@ private:
QFont m_font;
QColor m_text_color, m_background_color;
mutable EmptyWithinViewCache m_test_shapes_cache;
std::set <size_t> m_selected_ids;
std::vector <QModelIndex> m_selected_indexes;
std::vector <QModelIndex>::const_iterator m_current_index;
void search_children (const tl::GlobPattern &pattern, const QModelIndex &parent, bool recurse);
};
}
+1
View File
@@ -42,6 +42,7 @@
#include "tlString.h"
#include "tlLog.h"
#include "tlAssert.h"
#include "layLayoutView.h"
#include "layViewOp.h"
#include "layViewObject.h"
#include "layLayoutViewConfigPages.h"
+7
View File
@@ -25,6 +25,7 @@
#include <QWheelEvent>
#include <QKeyEvent>
#include <QMimeData>
#include <QCoreApplication>
#include "layViewObject.h"
#include "layCanvasPlane.h"
@@ -367,6 +368,12 @@ ViewObjectWidget::end_mouse_event ()
}
}
bool
ViewObjectWidget::focusNextPrevChild (bool /*next*/)
{
return false;
}
void
ViewObjectWidget::keyPressEvent (QKeyEvent *e)
{
+65 -60
View File
@@ -603,66 +603,6 @@ public:
*/
~ViewObjectWidget ();
/**
* @brief Qt keyboard event handler
*/
void keyPressEvent (QKeyEvent *e);
/**
* @brief Qt mouse move event handler
*/
void mouseMoveEvent (QMouseEvent *e);
/**
* @brief Qt mouse leave event handler
*/
void leaveEvent (QEvent *e);
/**
* @brief Qt mouse enter event handler
*/
void enterEvent (QEvent *e);
/**
* @brief Qt mouse button press event handler
*/
void mousePressEvent (QMouseEvent *e);
/**
* @brief Qt mouse button double-click event handler
*/
void mouseDoubleClickEvent (QMouseEvent *e);
/**
* @brief Qt mouse button release event handler
*/
void mouseReleaseEvent (QMouseEvent *e);
/**
* @brief Qt drag enter event handler
*/
void dragEnterEvent (QDragEnterEvent *event);
/**
* @brief Qt drag leave event handler
*/
void dragLeaveEvent (QDragLeaveEvent *event);
/**
* @brief Qt drag enter event handler
*/
void dragMoveEvent (QDragMoveEvent *event);
/**
* @brief Qt drop event handler
*/
void dropEvent (QDropEvent *event);
/**
* @brief Qt mouse wheel event handler
*/
void wheelEvent (QWheelEvent *e);
/**
* @brief Cancel all drag operations
*/
@@ -966,6 +906,71 @@ public:
void set_default_cursor (lay::Cursor::cursor_shape cursor);
protected:
/**
* @brief Qt focus event handler
*/
bool focusNextPrevChild (bool next);
/**
* @brief Qt keyboard event handler
*/
void keyPressEvent (QKeyEvent *e);
/**
* @brief Qt mouse move event handler
*/
void mouseMoveEvent (QMouseEvent *e);
/**
* @brief Qt mouse leave event handler
*/
void leaveEvent (QEvent *e);
/**
* @brief Qt mouse enter event handler
*/
void enterEvent (QEvent *e);
/**
* @brief Qt mouse button press event handler
*/
void mousePressEvent (QMouseEvent *e);
/**
* @brief Qt mouse button double-click event handler
*/
void mouseDoubleClickEvent (QMouseEvent *e);
/**
* @brief Qt mouse button release event handler
*/
void mouseReleaseEvent (QMouseEvent *e);
/**
* @brief Qt drag enter event handler
*/
void dragEnterEvent (QDragEnterEvent *event);
/**
* @brief Qt drag leave event handler
*/
void dragLeaveEvent (QDragLeaveEvent *event);
/**
* @brief Qt drag enter event handler
*/
void dragMoveEvent (QDragMoveEvent *event);
/**
* @brief Qt drop event handler
*/
void dropEvent (QDropEvent *event);
/**
* @brief Qt mouse wheel event handler
*/
void wheelEvent (QWheelEvent *e);
/**
* @brief Set the transformation for mouse events
*/
+2
View File
@@ -976,6 +976,8 @@ bool DecoratedLineEdit::event (QEvent *event)
QKeyEvent *ke = static_cast<QKeyEvent *> (event);
if (ke->key () == Qt::Key_Escape && m_escape_signal_enabled) {
ke->accept ();
} else if ((ke->key () == Qt::Key_Tab || ke->key () == Qt::Key_Backtab) && m_tab_signal_enabled) {
ke->accept ();
}
}
return QLineEdit::event (event);