WIP: help browser: on-page search, search box completer

This commit is contained in:
Matthias Koefferlein 2020-09-03 23:38:55 +02:00
parent 97df81f996
commit 523f8e41e4
5 changed files with 359 additions and 175 deletions

View File

@ -554,6 +554,21 @@ HelpSource::get_outline (const std::string &u)
return ol;
}
void
HelpSource::search_completers (const std::string &string, std::list<std::string> &completers)
{
size_t n = 0;
const size_t max_completers = 100;
// first produce all hits with match
for (std::vector <IndexEntry>::const_iterator i = m_index.begin (); i < m_index.end () && n < max_completers; ++i) {
if (i->normalized_key.find (string) != std::string::npos) {
completers.push_back (i->key);
++n;
}
}
}
std::string
HelpSource::next_topic (const std::string &url)
{

View File

@ -82,8 +82,9 @@ public:
virtual QImage get_image (const std::string &url);
virtual std::string get_css (const std::string &url);
virtual std::string next_topic (const std::string &url);
virtual void search_completers(const std::string &search_string, std::list<std::string> &completers);
virtual std::string next_topic (const std::string &url);
virtual std::string prev_topic (const std::string &url);
QDomDocument get_dom (const std::string &u);

View File

@ -52,52 +52,6 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>6</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="6">
<widget class="QToolButton" name="prev_topic_pb">
<property name="toolTip">
<string>Previous Topic</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/prev_topic.png</normaloff>:/prev_topic.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="8">
<spacer>
<property name="orientation">
@ -114,111 +68,6 @@
</property>
</spacer>
</item>
<item row="0" column="4">
<widget class="QToolButton" name="home_pb">
<property name="toolTip">
<string>Home</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/home.png</normaloff>:/home.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="7">
<widget class="QToolButton" name="next_topic_pb">
<property name="toolTip">
<string>Next Topic</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/next_topic.png</normaloff>:/next_topic.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="9">
<widget class="QLineEdit" name="searchEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="back_pb">
<property name="toolTip">
<string>Back</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/back.png</normaloff>:/back.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>6</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="11">
<widget class="QLabel" name="label_2">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../lay/lay/layResources.qrc">:/find.png</pixmap>
</property>
</widget>
</item>
<item row="1" column="0" colspan="12">
<widget class="QFrame" name="frame">
<property name="minimumSize">
@ -360,6 +209,9 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
@ -372,25 +224,47 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../lay/lay/layResources.qrc">:/find.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Find on page</string>
<string> Find on page </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
<widget class="QToolButton" name="on_page_search_next">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/find.png</normaloff>:/find.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="on_page_search_edit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/clear_edit.png</normaloff>:/clear_edit.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
@ -402,8 +276,15 @@
</layout>
</widget>
</item>
<item row="0" column="10">
<spacer name="horizontalSpacer">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -412,13 +293,156 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<width>6</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="back_pb">
<property name="toolTip">
<string>Back</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/back.png</normaloff>:/back.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>6</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="7">
<widget class="QToolButton" name="next_topic_pb">
<property name="toolTip">
<string>Next Topic</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/next_topic.png</normaloff>:/next_topic.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QToolButton" name="prev_topic_pb">
<property name="toolTip">
<string>Previous Topic</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/prev_topic.png</normaloff>:/prev_topic.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QToolButton" name="home_pb">
<property name="toolTip">
<string>Home</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/home.png</normaloff>:/home.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="10">
<widget class="QLineEdit" name="search_edit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="9">
<widget class="QToolButton" name="search_button">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/find.png</normaloff>:/find.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<action name="action_find">
<property name="text">
<string>Find</string>
</property>
<property name="shortcut">
<string>Ctrl+F</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
@ -428,7 +452,7 @@
</customwidget>
</customwidgets>
<tabstops>
<tabstop>searchEdit</tabstop>
<tabstop>search_edit</tabstop>
<tabstop>back_pb</tabstop>
<tabstop>forward_pb</tabstop>
<tabstop>home_pb</tabstop>
@ -436,5 +460,22 @@
<resources>
<include location="../../lay/lay/layResources.qrc"/>
</resources>
<connections/>
<connections>
<connection>
<sender>toolButton</sender>
<signal>clicked()</signal>
<receiver>search_frame</receiver>
<slot>hide()</slot>
<hints>
<hint type="sourcelabel">
<x>816</x>
<y>571</y>
</hint>
<hint type="destinationlabel">
<x>371</x>
<y>577</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -34,6 +34,9 @@
#endif
#include <QTreeWidgetItem>
#include <QTextBlock>
#include <QCompleter>
#include <QStringListModel>
namespace lay
{
@ -73,18 +76,35 @@ BrowserPanel::init ()
mp_ui->browser->set_panel (this);
mp_ui->browser->setWordWrapMode (QTextOption::WordWrap);
mp_ui->browser->addAction (mp_ui->action_find);
connect (mp_ui->back_pb, SIGNAL (clicked ()), this, SLOT (back ()));
connect (mp_ui->forward_pb, SIGNAL (clicked ()), this, SLOT (forward ()));
connect (mp_ui->next_topic_pb, SIGNAL (clicked ()), this, SLOT (next ()));
connect (mp_ui->prev_topic_pb, SIGNAL (clicked ()), this, SLOT (prev ()));
connect (mp_ui->home_pb, SIGNAL (clicked ()), this, SLOT (home ()));
connect (mp_ui->searchEdit, SIGNAL (returnPressed ()), this, SLOT (search_edited ()));
connect (mp_ui->search_edit, SIGNAL (textEdited (const QString &)), this, SLOT (search_text_changed (const QString &)));
connect (mp_ui->search_edit, SIGNAL (returnPressed ()), this, SLOT (search_edited ()));
connect (mp_ui->search_button, SIGNAL (clicked ()), this, SLOT (search_edited ()));
connect (mp_ui->browser, SIGNAL (textChanged ()), this, SLOT (text_changed ()));
connect (mp_ui->browser, SIGNAL (backwardAvailable (bool)), mp_ui->back_pb, SLOT (setEnabled (bool)));
connect (mp_ui->browser, SIGNAL (forwardAvailable (bool)), mp_ui->forward_pb, SLOT (setEnabled (bool)));
connect (mp_ui->outline_tree, SIGNAL (itemActivated (QTreeWidgetItem *, int)), this, SLOT (outline_item_clicked (QTreeWidgetItem *)));
connect (mp_ui->action_find, SIGNAL (triggered ()), this, SLOT (find ()));
connect (mp_ui->on_page_search_edit, SIGNAL (textChanged (const QString &)), this, SLOT (page_search_edited ()));
connect (mp_ui->on_page_search_edit, SIGNAL (returnPressed ()), this, SLOT (page_search_next ()));
connect (mp_ui->on_page_search_next, SIGNAL (clicked ()), this, SLOT (page_search_next ()));
mp_ui->searchEdit->hide ();
mp_completer = new QCompleter (this);
mp_completer->setFilterMode (Qt::MatchStartsWith);
mp_completer->setCaseSensitivity (Qt::CaseInsensitive);
mp_completer->setCompletionMode (QCompleter::UnfilteredPopupCompletion);
mp_completer_model = new QStringListModel (mp_completer);
mp_completer->setModel (mp_completer_model);
mp_ui->search_edit->setCompleter (mp_completer);
mp_ui->search_frame->hide ();
mp_ui->search_edit->hide ();
set_label (std::string ());
}
@ -110,6 +130,87 @@ BrowserPanel::url () const
return m_cached_url;
}
void
BrowserPanel::find ()
{
mp_ui->search_frame->show ();
mp_ui->on_page_search_edit->setFocus();
}
void
BrowserPanel::page_search_edited ()
{
m_search_selection.clear ();
m_search_index = -1;
if (mp_ui->on_page_search_edit->text ().size () < 2) {
mp_ui->browser->setExtraSelections (m_search_selection);
return;
}
QString search_text = mp_ui->on_page_search_edit->text ();
QTextDocument *doc = mp_ui->browser->document ();
for (QTextBlock b = doc->firstBlock (); b.isValid (); b = b.next ()) {
int from = 0;
int index;
QString t = b.text ();
while ((index = t.indexOf (search_text, from, Qt::CaseInsensitive)) >= 0) {
QTextCursor highlight (b);
highlight.movePosition (QTextCursor::NextCharacter, QTextCursor::MoveAnchor, index);
highlight.movePosition (QTextCursor::NextCharacter, QTextCursor::KeepAnchor, search_text.size ());
QTextEdit::ExtraSelection extra_selection;
extra_selection.cursor = highlight;
extra_selection.format.setBackground (QColor (255, 255, 160));
m_search_selection.push_back (extra_selection);
from = index + search_text.size ();
}
}
if (! m_search_selection.empty ()) {
m_search_index = 0;
mp_ui->browser->setExtraSelections (m_search_selection);
mp_ui->browser->setTextCursor (m_search_selection [m_search_index].cursor);
}
}
void
BrowserPanel::page_search_next ()
{
if (m_search_index >= 0) {
++m_search_index;
if (m_search_index >= m_search_selection.size ()) {
m_search_index = 0;
}
mp_ui->browser->setTextCursor (m_search_selection [m_search_index].cursor);
}
}
void
BrowserPanel::search_text_changed (const QString &text)
{
QList<QString> strings;
if (! text.isEmpty () && mp_source.get ()) {
std::list<std::string> cl;
mp_source->search_completers (tl::to_string (text.toLower ()), cl);
for (std::list<std::string>::const_iterator i = cl.begin (); i != cl.end (); ++i) {
strings.push_back (tl::to_qstring (*i));
}
}
mp_completer_model->setStringList (strings);
}
void
BrowserPanel::text_changed ()
{
@ -259,15 +360,15 @@ BrowserPanel::search (const std::string &s)
void
BrowserPanel::search_edited ()
{
if (mp_ui->searchEdit->text ().size () > 0) {
if (mp_ui->search_edit->text ().size () > 0) {
QUrl url (tl::to_qstring (m_search_url));
#if QT_VERSION >= 0x050000
QUrlQuery qi;
qi.addQueryItem (tl::to_qstring (m_search_query_item), mp_ui->searchEdit->text ());
qi.addQueryItem (tl::to_qstring (m_search_query_item), mp_ui->search_edit->text ());
url.setQuery (qi);
#else
QList<QPair<QString, QString> > qi;
qi.push_back (QPair<QString, QString> (tl::to_qstring (m_search_query_item), mp_ui->searchEdit->text ()));
qi.push_back (QPair<QString, QString> (tl::to_qstring (m_search_query_item), mp_ui->search_edit->text ()));
url.setQueryItems (qi);
#endif
load (url.toEncoded ().constData ());
@ -279,7 +380,7 @@ BrowserPanel::set_search_url (const std::string &url, const std::string &query_i
{
m_search_url = url;
m_search_query_item = query_item;
mp_ui->searchEdit->setVisible (! url.empty ());
mp_ui->search_edit->setVisible (! url.empty ());
}
void
@ -474,6 +575,12 @@ BrowserSource::get_outline (const std::string & /*url*/)
return BrowserOutline ();
}
void
BrowserSource::search_completers (const std::string & /*search_string*/, std::list<std::string> & /*completers*/)
{
// .. nothing here ..
}
std::string
BrowserSource::get (const std::string & /*url*/)
{

View File

@ -30,12 +30,15 @@
#include "gsiObject.h"
#include <QTextBrowser>
#include <QCompleter>
#include <string>
#include <list>
#include <set>
class QTreeWidgetItem;
class QCompleter;
class QStringListModel;
namespace Ui
{
@ -204,6 +207,11 @@ public:
*/
virtual BrowserOutline get_outline (const std::string &url);
/**
* @brief Gets the search completer items for a given search string
*/
virtual void search_completers (const std::string &search_string, std::list<std::string> &completers);
/**
* @brief Get the image for a given "int" URL in an image
*/
@ -385,7 +393,15 @@ public slots:
*/
void home ();
/**
* @brief "find" activated
*/
void find ();
protected slots:
void page_search_edited ();
void page_search_next();
void search_text_changed(const QString &text);
void search_edited ();
void text_changed ();
void outline_item_clicked (QTreeWidgetItem *item);
@ -408,6 +424,10 @@ private:
tl::DeferredMethod<BrowserPanel> m_back_dm;
std::string m_search_url, m_search_query_item;
QString m_current_title;
QList<QTextEdit::ExtraSelection> m_search_selection;
int m_search_index;
QCompleter *mp_completer;
QStringListModel *mp_completer_model;
void init ();
};