]> git.lyx.org Git - lyx.git/commitdiff
TocWidget: fix an erroneous collapse and optimise updates based on profiling
authorGuillaume Munch <gm@lyx.org>
Sun, 8 May 2016 22:56:55 +0000 (23:56 +0100)
committerRichard Heck <rgheck@lyx.org>
Wed, 15 Jun 2016 21:32:53 +0000 (22:32 +0100)
TocModels::reset() in GuiView::structureChanged() collapses the TocWidget, and
therefore requires an update right after, which was missing.

In fact, profiling TocWidget::updateView() shows that delaying the update is
good only for fast keypresses (essentially movement). It costs 5% of a
char-forward operation in a document with approx. 100 table of contents
items. The update optimisation has been rewritten to take this data into
account.

This also fixes #9826: Outline disclosure of subsection content disappears one
second after doubleclicking content item.

src/frontends/qt4/GuiToc.cpp
src/frontends/qt4/TocWidget.cpp
src/frontends/qt4/TocWidget.h

index 48349fcfa52d6a4010661435d0e636c306830091..29ec85c27ff9db904df6e0a4104ae91c75702584 100644 (file)
@@ -67,7 +67,6 @@ void GuiToc::dispatchParams()
 
 void GuiToc::enableView(bool enable)
 {
-       widget_->checkModelChanged();
        if (!enable)
                // In the opposite case, updateView() will be called anyway.
                widget_->updateViewNow();
index 4fa6aef7a3a215743f3c41323b260ab5ad14e84f..ed188b8fa611ee874db551e0cc76fd85224dddc3 100644 (file)
@@ -45,9 +45,7 @@ namespace frontend {
 
 TocWidget::TocWidget(GuiView & gui_view, QWidget * parent)
        : QWidget(parent), depth_(0), persistent_(false), gui_view_(gui_view),
-         update_timer_short_(new QTimer(this)),
-         update_timer_long_(new QTimer(this))
-
+         timer_(new QTimer(this))
 {
        setupUi(this);
 
@@ -89,14 +87,8 @@ TocWidget::TocWidget(GuiView & gui_view, QWidget * parent)
                this, SLOT(filterContents()));
 
        // setting the update timer
-       update_timer_short_->setSingleShot(true);
-       update_timer_long_->setSingleShot(true);
-       update_timer_short_->setInterval(0);
-       update_timer_long_->setInterval(2000);
-       connect(update_timer_short_, SIGNAL(timeout()),
-               this, SLOT(realUpdateView()));
-       connect(update_timer_long_, SIGNAL(timeout()),
-               this, SLOT(realUpdateView()));
+       timer_->setSingleShot(true);
+       connect(timer_, SIGNAL(timeout()), this, SLOT(finishUpdateView()));
 
        init(QString());
 }
@@ -388,24 +380,6 @@ void TocWidget::enableControls(bool enable)
 
 
 void TocWidget::updateView()
-{
-       // Subtler optimization for having the delay more UI invisible.
-       // We trigger update immediately for sparse editation actions,
-       // i.e. there was no editation/cursor movement in last 2 sec.
-       // At worst there will be +1 redraw after 2s in a such "calm" mode.
-       if (!update_timer_long_->isActive())
-               update_timer_short_->start();
-       // resets the timer to trigger after 2s
-       update_timer_long_->start();
-}
-
-void TocWidget::updateViewNow()
-{
-       update_timer_long_->stop();
-       update_timer_short_->start();
-}
-
-void TocWidget::realUpdateView()
 {
        if (!gui_view_.documentBufferView()) {
                tocTV->setModel(0);
@@ -417,7 +391,7 @@ void TocWidget::realUpdateView()
        setEnabled(true);
        bool const is_sortable = isSortable();
        sortCB->setEnabled(is_sortable);
-       bool focus_ = tocTV->hasFocus();
+       bool focus = tocTV->hasFocus();
        tocTV->setEnabled(false);
        tocTV->setUpdatesEnabled(false);
 
@@ -435,8 +409,7 @@ void TocWidget::realUpdateView()
                && gui_view_.tocModels().isSorted(current_type_));
        sortCB->blockSignals(false);
 
-       bool const can_navigate_ = canNavigate();
-       persistentCB->setEnabled(can_navigate_);
+       persistentCB->setEnabled(canNavigate());
 
        bool controls_enabled = toc_model && toc_model->rowCount() > 0
                && !gui_view_.documentBufferView()->buffer().isReadonly();
@@ -444,25 +417,42 @@ void TocWidget::realUpdateView()
 
        depthSL->setMaximum(gui_view_.tocModels().depth(current_type_));
        depthSL->setValue(depth_);
-       if (!persistent_ && can_navigate_)
-               setTreeDepth(depth_);
-       if (can_navigate_) {
-               persistentCB->setChecked(persistent_);
-               select(gui_view_.tocModels().currentIndex(current_type_));
-       }
-       filterContents();
        tocTV->setEnabled(true);
        tocTV->setUpdatesEnabled(true);
-       if (focus_)
+       if (focus)
                tocTV->setFocus();
+
+       // Expensive operations are on a timer.  We finish the update immediately
+       // for sparse edition actions, i.e. there was no edition/cursor movement
+       // recently, then every 300ms.
+       if (!timer_->isActive()) {
+               finishUpdateView();
+               timer_->start(300);
+       }
+}
+
+
+void TocWidget::updateViewNow()
+{
+       timer_->stop();
+       updateView();
 }
 
 
-void TocWidget::checkModelChanged()
+void TocWidget::finishUpdateView()
 {
-       if (!gui_view_.documentBufferView() ||
-           gui_view_.tocModels().model(current_type_) != tocTV->model())
-               realUpdateView();
+       // Profiling shows that this is the expensive stuff in the context of typing
+       // text and moving with arrows (still five times less than updateToolbars in
+       // my tests with a medium-sized document, however this grows linearly in the
+       // size of the document). For bigger operations, this is negligible, and
+       // outweighted by TocModels::reset() anyway.
+       if (canNavigate()) {
+               if (!persistent_)
+                       setTreeDepth(depth_);
+               persistentCB->setChecked(persistent_);
+               select(gui_view_.tocModels().currentIndex(current_type_));
+       }
+       filterContents();
 }
 
 
@@ -534,6 +524,7 @@ void TocWidget::init(QString const & str)
        typeCO->blockSignals(true);
        typeCO->setCurrentIndex(new_index);
        typeCO->blockSignals(false);
+       updateViewNow();
 }
 
 } // namespace frontend
index ec1d5ad2643731c91f4e4e688990cb4e57b58521..3f67606302705f2cb049477e46a2c7c9a9745782 100644 (file)
@@ -43,13 +43,11 @@ public:
        ///
        bool getStatus(Cursor & cur, FuncRequest const & fr, FuncStatus & status)
                const;
-       // update the view when the model has changed
-       void checkModelChanged();
 
 public Q_SLOTS:
-       /// Schedule an update of the dialog after a delay
+       /// Schedule an update of the dialog, delaying expensive operations
        void updateView();
-       /// Schedule an update of the dialog immediately
+       /// Update completely without delay
        void updateViewNow();
 
 protected Q_SLOTS:
@@ -74,8 +72,8 @@ protected Q_SLOTS:
        void showContextMenu(const QPoint & pos);
 
 private Q_SLOTS:
-       /// Update the display of the dialog
-       void realUpdateView();
+       /// Perform the expensive update operations
+       void finishUpdateView();
 
 private:
        ///
@@ -108,12 +106,8 @@ private:
        bool persistent_;
        ///
        GuiView & gui_view_;
-       // Timers for scheduling updates: one immediately and one after a delay.
-       // This is according to the logic of the previous code: when at rest, the
-       // update is carried out immediately, and when an update was done recently,
-       // we schedule an update to occur 2s after resting.
-       QTimer * update_timer_short_;
-       QTimer * update_timer_long_;
+       // Timer for scheduling expensive update operations
+       QTimer * timer_;
 };
 
 } // namespace frontend