commit 2baa3a46a6ad3576fc94fa0b915adc30ba2bbdff Author: Juergen Spitzmueller <sp...@lyx.org> Date: Sun Feb 14 17:18:00 2021 +0100
Transform simple search dialog to dock widget (#2625) Also solves #8054 --- lib/bind/cua.bind | 4 +- lib/bind/mac.bind | 4 +- src/frontends/qt/GuiSearch.cpp | 181 ++++++++++++++++++--- src/frontends/qt/GuiSearch.h | 75 ++++++++-- src/frontends/qt/ui/SearchUi.ui | 342 +++++++++++++++++++++++---------------- 5 files changed, 428 insertions(+), 178 deletions(-) diff --git a/lib/bind/cua.bind b/lib/bind/cua.bind index fc776c2..61e941b 100644 --- a/lib/bind/cua.bind +++ b/lib/bind/cua.bind @@ -72,8 +72,8 @@ Format 5 \bind "C-S-M" "math-display" \bind "C-M-n" "command-sequence math-display; math-number-toggle;" -\bind "C-f" "dialog-show findreplace" -\bind "C-S-f" "dialog-show findreplaceadv" +\bind "C-f" "dialog-toggle findreplace" +\bind "C-S-f" "dialog-toggle findreplaceadv" \bind "C-i" "inset-toggle" # 'i' for Inset \bind "C-M-i" "inset-settings" diff --git a/lib/bind/mac.bind b/lib/bind/mac.bind index a1b3ed7..f65895f 100644 --- a/lib/bind/mac.bind +++ b/lib/bind/mac.bind @@ -142,8 +142,8 @@ Format 5 # -: "Command-E" # Use the selection for a find \bind "C-e" "font-emph" # +: "Command-F" # Open a Find window -\bind "C-f" "dialog-show findreplace" -\bind "C-S-f" "dialog-show findreplaceadv" +\bind "C-f" "dialog-toggle findreplace" +\bind "C-S-f" "dialog-toggle findreplaceadv" # -: "Option-Command-F" # Move to the search field control # +: "Command-G" # Find the next occurrence of the selection \bind "C-g" "word-find-forward" diff --git a/src/frontends/qt/GuiSearch.cpp b/src/frontends/qt/GuiSearch.cpp index e33e421..6722655 100644 --- a/src/frontends/qt/GuiSearch.cpp +++ b/src/frontends/qt/GuiSearch.cpp @@ -17,6 +17,7 @@ #include "lyxfind.h" #include "qt_helpers.h" #include "FuncRequest.h" +#include "LyX.h" #include "BufferView.h" #include "Buffer.h" #include "Cursor.h" @@ -25,11 +26,14 @@ #include "GuiKeySymbol.h" #include "GuiView.h" +#include "support/debug.h" #include "support/gettext.h" #include "frontends/alert.h" #include <QLineEdit> +#include <QSettings> #include <QShowEvent> +#include "QSizePolicy" using namespace std; @@ -48,8 +52,8 @@ static void uniqueInsert(QComboBox * box, QString const & text) } -GuiSearch::GuiSearch(GuiView & lv) - : GuiDialog(lv, "findreplace", qt_("Find and Replace")) +GuiSearchWidget::GuiSearchWidget(QWidget * parent) + : QWidget(parent) { setupUi(this); @@ -57,34 +61,39 @@ GuiSearch::GuiSearch(GuiView & lv) setFixedHeight(sizeHint().height()); // align items in grid on top - mainGridLayout->setAlignment(Qt::AlignTop); + gridLayout->setAlignment(Qt::AlignTop); - connect(buttonBox, SIGNAL(clicked(QAbstractButton *)), - this, SLOT(slotButtonBox(QAbstractButton *))); connect(findPB, SIGNAL(clicked()), this, SLOT(findClicked())); + connect(findPrevPB, SIGNAL(clicked()), this, SLOT(findPrevClicked())); + connect(minimizePB, SIGNAL(clicked()), this, SLOT(minimizeClicked())); connect(replacePB, SIGNAL(clicked()), this, SLOT(replaceClicked())); + connect(replacePrevPB, SIGNAL(clicked()), this, SLOT(replacePrevClicked())); connect(replaceallPB, SIGNAL(clicked()), this, SLOT(replaceallClicked())); connect(findCO, SIGNAL(editTextChanged(QString)), this, SLOT(findChanged())); setFocusProxy(findCO); - bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy); - bc().setCancel(buttonBox->button(QDialogButtonBox::Close)); - findCO->setCompleter(0); replaceCO->setCompleter(0); replacePB->setEnabled(false); + replacePrevPB->setEnabled(false); replaceallPB->setEnabled(false); } -void GuiSearch::keyPressEvent(QKeyEvent * ev) +void GuiSearchWidget::keyPressEvent(QKeyEvent * ev) { KeySymbol sym; setKeySymbol(&sym, ev); + // catch Return and Shift-Return + if (ev->key() == Qt::Key_Return) { + findClicked(ev->modifiers() == Qt::ShiftModifier); + return; + } + // we catch the key sequences for forward and backwards search if (sym.isOK()) { KeyModifier mod = lyx::q_key_state(ev->modifiers()); @@ -98,53 +107,96 @@ void GuiSearch::keyPressEvent(QKeyEvent * ev) findClicked(true); return; } + if (fr == FuncRequest(LFUN_DIALOG_TOGGLE, "findreplace")) { + dispatch(fr); + return; + } + } + QWidget::keyPressEvent(ev); +} + + +void GuiSearchWidget::minimizeClicked(bool const toggle) +{ + if (toggle) + minimized_ = !minimized_; + + replaceLA->setHidden(minimized_); + replaceCO->setHidden(minimized_); + replacePB->setHidden(minimized_); + replacePrevPB->setHidden(minimized_); + replaceallPB->setHidden(minimized_); + wordsCB->setHidden(minimized_); + caseCB->setHidden(minimized_); + if (minimized_) { + minimizePB->setText(qt_("Ex&pand")); + minimizePB->setToolTip("Show replace and option widgets"); + } else { + minimizePB->setText(qt_("&Minimize")); + minimizePB->setToolTip("Hide replace and option widgets"); } - QDialog::keyPressEvent(ev); + + Q_EMIT needSizeUpdate(); + Q_EMIT needTitleBarUpdate(); } -void GuiSearch::showEvent(QShowEvent * e) +void GuiSearchWidget::showEvent(QShowEvent * e) { findChanged(); findPB->setFocus(); findCO->lineEdit()->selectAll(); - GuiDialog::showEvent(e); + QWidget::showEvent(e); } -void GuiSearch::findChanged() +void GuiSearchWidget::findChanged() { findPB->setEnabled(!findCO->currentText().isEmpty()); - bool const replace = !findCO->currentText().isEmpty() && !isBufferReadonly(); + findPrevPB->setEnabled(!findCO->currentText().isEmpty()); + bool const replace = !findCO->currentText().isEmpty() + && bv_ && !bv_->buffer().isReadonly(); replacePB->setEnabled(replace); + replacePrevPB->setEnabled(replace); replaceallPB->setEnabled(replace); replaceLA->setEnabled(replace); replaceCO->setEnabled(replace); } -void GuiSearch::findClicked(bool const backwards) +void GuiSearchWidget::findClicked(bool const backwards) { docstring const needle = qstring_to_ucs4(findCO->currentText()); - find(needle, caseCB->isChecked(), wordsCB->isChecked(), - (!backwards && !backwardsCB->isChecked())); + find(needle, caseCB->isChecked(), wordsCB->isChecked(), !backwards); uniqueInsert(findCO, findCO->currentText()); findCO->lineEdit()->selectAll(); } -void GuiSearch::replaceClicked() +void GuiSearchWidget::findPrevClicked() +{ + findClicked(true); +} + + +void GuiSearchWidget::replaceClicked(bool const backwards) { docstring const needle = qstring_to_ucs4(findCO->currentText()); docstring const repl = qstring_to_ucs4(replaceCO->currentText()); replace(needle, repl, caseCB->isChecked(), wordsCB->isChecked(), - !backwardsCB->isChecked(), false); + !backwards, false); uniqueInsert(findCO, findCO->currentText()); uniqueInsert(replaceCO, replaceCO->currentText()); } -void GuiSearch::replaceallClicked() +void GuiSearchWidget::replacePrevClicked() +{ + replaceClicked(true); +} + + +void GuiSearchWidget::replaceallClicked() { replace(qstring_to_ucs4(findCO->currentText()), qstring_to_ucs4(replaceCO->currentText()), @@ -154,7 +206,7 @@ void GuiSearch::replaceallClicked() } -void GuiSearch::find(docstring const & search, bool casesensitive, +void GuiSearchWidget::find(docstring const & search, bool casesensitive, bool matchword, bool forward) { docstring const sdata = @@ -163,7 +215,7 @@ void GuiSearch::find(docstring const & search, bool casesensitive, } -void GuiSearch::replace(docstring const & search, docstring const & replace, +void GuiSearchWidget::replace(docstring const & search, docstring const & replace, bool casesensitive, bool matchword, bool forward, bool all) { @@ -173,6 +225,91 @@ void GuiSearch::replace(docstring const & search, docstring const & replace, dispatch(FuncRequest(LFUN_WORD_REPLACE, sdata)); } +void GuiSearchWidget::saveSession(QSettings & settings, QString const & session_key) const +{ + settings.setValue(session_key + "/casesensitive", caseCB->isChecked()); + settings.setValue(session_key + "/words", wordsCB->isChecked()); + settings.setValue(session_key + "/minimized", minimized_); +} + + +void GuiSearchWidget::restoreSession(QString const & session_key) +{ + QSettings settings; + caseCB->setChecked(settings.value(session_key + "/casesensitive", false).toBool()); + wordsCB->setChecked(settings.value(session_key + "/words", false).toBool()); + minimized_ = settings.value(session_key + "/minimized", false).toBool(); + // initialize hidings + minimizeClicked(false); +} + + +GuiSearch::GuiSearch(GuiView & parent, Qt::DockWidgetArea area, Qt::WindowFlags flags) + : DockView(parent, "findreplace", qt_("Search and Replace"), area, flags), + widget_(new GuiSearchWidget(this)) +{ + setWidget(widget_); + widget_->setBufferView(bufferview()); + setFocusProxy(widget_->findCO); + + connect(widget_, SIGNAL(needTitleBarUpdate()), this, SLOT(updateTitle())); + connect(widget_, SIGNAL(needSizeUpdate()), this, SLOT(updateSize())); +} + +void GuiSearch::onBufferViewChanged() +{ + widget_->setEnabled((bool)bufferview()); + widget_->setBufferView(bufferview()); +} + + +void GuiSearch::updateView() +{ + updateTitle(); + updateSize(); +} + + +void GuiSearch::saveSession(QSettings & settings) const +{ + Dialog::saveSession(settings); + widget_->saveSession(settings, sessionKey()); +} + + +void GuiSearch::restoreSession() +{ + DockView::restoreSession(); + widget_->restoreSession(sessionKey()); +} + + +void GuiSearch::updateTitle() +{ + if (widget_->isMinimized()) { + // remove title bar + setTitleBarWidget(new QWidget()); + titleBarWidget()->hide(); + } else + // restore title bar + setTitleBarWidget(nullptr); +} + + +void GuiSearch::updateSize() +{ + widget_->setFixedHeight(widget_->sizeHint().height()); + if (widget_->isMinimized()) + // FIXME still a bit too tall + setFixedHeight(widget_->sizeHint().height()); + else { + // undo setFixedHeight + setMaximumHeight(QWIDGETSIZE_MAX); + setMinimumHeight(0); + } + update(); +} + } // namespace frontend } // namespace lyx diff --git a/src/frontends/qt/GuiSearch.h b/src/frontends/qt/GuiSearch.h index e6c7c99..e0d5b12 100644 --- a/src/frontends/qt/GuiSearch.h +++ b/src/frontends/qt/GuiSearch.h @@ -14,43 +14,98 @@ #define GUISEARCH_H #include "GuiDialog.h" +#include "DockView.h" + +#include <QDockWidget> + #include "ui_SearchUi.h" namespace lyx { namespace frontend { -class GuiSearch : public GuiDialog, public Ui::SearchUi +class GuiSearch; + +class GuiSearchWidget : public QWidget, public Ui::SearchUi { Q_OBJECT public: - GuiSearch(GuiView & lv); + GuiSearchWidget(QWidget * parent); + /// + void saveSession(QSettings & settings, QString const & session_key) const; + /// + void restoreSession(QString const & session_key); + /// + void setBufferView(BufferView const * bv) { bv_ = bv; } + /// + bool isMinimized() { return minimized_; } private Q_SLOTS: void findChanged(); void findClicked(bool const backwards = false); - void replaceClicked(); + void findPrevClicked(); + void replaceClicked(bool const backwards = false); + void replacePrevClicked(); void replaceallClicked(); + void minimizeClicked(bool const toggle = true); +Q_SIGNALS: + void needTitleBarUpdate() const; + void needSizeUpdate() const; private: /// void keyPressEvent(QKeyEvent * e) override; /// void showEvent(QShowEvent * e) override; - /// - bool initialiseParams(std::string const &) override { return true; } - void clearParams() override {} - void dispatchParams() override {} - bool isBufferDependent() const override { return true; } - /// Searches occurrence of string void find(docstring const & search, bool casesensitive, bool matchword, bool forward); - /// Replaces occurrence of string void replace(docstring const & search, docstring const & replace, bool casesensitive, bool matchword, bool forward, bool all); + /// + BufferView const * bv_; + /// + bool minimized_ = false; +}; + + +class GuiSearch : public DockView +{ + Q_OBJECT + +public: + GuiSearch( + GuiView & parent, ///< the main window where to dock. + Qt::DockWidgetArea area = Qt::BottomDockWidgetArea, ///< Position of the dock (and also drawer) + Qt::WindowFlags flags = {}); + + /// Controller inherited method. + ///@{ + bool initialiseParams(std::string const &) override { return true; } + void clearParams() override {} + void dispatchParams() override {} + bool isBufferDependent() const override { return true; } + void updateView() override; + void saveSession(QSettings & settings) const override; + void restoreSession() override; + bool wantInitialFocus() const override { return true; } + ///@} + +public Q_SLOTS: + /// + void onBufferViewChanged() override; + +private Q_SLOTS: + /// update title display + void updateTitle(); + /// update dock size + void updateSize(); + +private: + /// The encapsulated widget. + GuiSearchWidget * widget_; }; } // namespace frontend diff --git a/src/frontends/qt/ui/SearchUi.ui b/src/frontends/qt/ui/SearchUi.ui index a42d67e..53661ba 100644 --- a/src/frontends/qt/ui/SearchUi.ui +++ b/src/frontends/qt/ui/SearchUi.ui @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>SearchUi</class> - <widget class="QDialog" name="SearchUi"> + <widget class="QWidget" name="SearchUi"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>416</width> - <height>240</height> + <width>678</width> + <height>135</height> </rect> </property> <property name="sizePolicy"> @@ -19,170 +19,228 @@ <property name="windowTitle"> <string/> </property> - <property name="sizeGripEnabled"> - <bool>true</bool> + <property name="sizeGripEnabled" stdset="0"> + <bool>false</bool> </property> - <layout class="QGridLayout" name="gridLayout_3"> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QCheckBox" name="caseCB"> + <property name="text"> + <string>&Case sensitive[[search]]</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="wordsCB"> + <property name="text"> + <string>Match &whole words only</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="2" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>89</width> + <height>0</height> + </size> + </property> + </spacer> + </item> <item row="0" column="0"> - <layout class="QGridLayout" name="mainGridLayout"> - <item row="0" column="0" rowspan="2"> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QLabel" name="findLA"> - <property name="text"> - <string>&Find:</string> - </property> - <property name="buddy"> - <cstring>findCO</cstring> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QComboBox" name="findCO"> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="focusPolicy"> - <enum>Qt::StrongFocus</enum> - </property> - <property name="editable"> - <bool>true</bool> - </property> - <property name="maxCount"> - <number>666</number> - </property> - <property name="insertPolicy"> - <enum>QComboBox::InsertAtTop</enum> - </property> - <property name="duplicatesEnabled"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="replaceLA"> - <property name="text"> - <string>Re&place with:</string> - </property> - <property name="buddy"> - <cstring>replaceCO</cstring> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QComboBox" name="replaceCO"> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="editable"> - <bool>true</bool> - </property> - <property name="maxCount"> - <number>666</number> - </property> - <property name="insertPolicy"> - <enum>QComboBox::InsertAtTop</enum> - </property> - <property name="duplicatesEnabled"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="2"> + <widget class="QPushButton" name="findPrevPB"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Find previous occurrence (Shift+Enter)</string> + </property> + <property name="text"> + <string>Find[[Previous]]</string> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> </item> - <item row="0" column="1"> - <widget class="QPushButton" name="findPB"> + <item row="1" column="4"> + <widget class="QPushButton" name="replaceallPB"> <property name="enabled"> <bool>false</bool> </property> + <property name="toolTip"> + <string>Replace all occurrences</string> + </property> <property name="text"> - <string>Find &Next</string> + <string>Replace &All</string> </property> - <property name="default"> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="findCO"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + <property name="editable"> <bool>true</bool> </property> + <property name="maxCount"> + <number>666</number> + </property> + <property name="insertPolicy"> + <enum>QComboBox::InsertAtTop</enum> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QPushButton" name="minimizePB"> + <property name="toolTip"> + <string>Hide replace and option widgets</string> + </property> + <property name="text"> + <string>&Minimize</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="replaceLA"> + <property name="text"> + <string>Rep&lace with:</string> + </property> + <property name="buddy"> + <cstring>replaceCO</cstring> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="findLA"> + <property name="text"> + <string>&Search:</string> + </property> + <property name="buddy"> + <cstring>findCO</cstring> + </property> </widget> </item> <item row="1" column="1"> + <widget class="QComboBox" name="replaceCO"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="maxCount"> + <number>666</number> + </property> + <property name="insertPolicy"> + <enum>QComboBox::InsertAtTop</enum> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="3"> <widget class="QPushButton" name="replacePB"> <property name="enabled"> <bool>false</bool> </property> + <property name="toolTip"> + <string>Replace and find next occurrence</string> + </property> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> + </property> <property name="text"> - <string>&Replace</string> + <string>&Replace[[Next]]</string> + </property> + <property name="icon"> + <iconset theme="go-next"> + <normaloff>.</normaloff>.</iconset> </property> </widget> </item> - <item row="2" column="0"> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <widget class="QCheckBox" name="caseCB"> - <property name="text"> - <string>Case &sensitive[[search]]</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="wordsCB"> - <property name="text"> - <string>Match &whole words only</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="backwardsCB"> - <property name="text"> - <string>Search &backwards</string> - </property> - </widget> - </item> - </layout> + <item row="1" column="2"> + <widget class="QPushButton" name="replacePrevPB"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Replace and find previous occurrence</string> + </property> + <property name="text"> + <string>Re&place[[Previous]]</string> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> </item> - <item row="2" column="1"> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QPushButton" name="replaceallPB"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Replace &All</string> - </property> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>89</width> - <height>13</height> - </size> - </property> - </spacer> - </item> - </layout> + <item row="0" column="3"> + <widget class="QPushButton" name="findPB"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Find next occurrence (Enter)</string> + </property> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> + </property> + <property name="text"> + <string>Find[[Next]]</string> + </property> + <property name="icon"> + <iconset theme="go-next"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> </item> </layout> </item> - <item row="1" column="0"> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Close</set> - </property> - </widget> - </item> </layout> </widget> <includes> -- lyx-cvs mailing list lyx-cvs@lists.lyx.org http://lists.lyx.org/mailman/listinfo/lyx-cvs