From c69426d7f57e72e3bc9640ea5e86f721c04fa0e1 Mon Sep 17 00:00:00 2001 From: Guillaume Munch Date: Sun, 8 May 2016 23:56:55 +0100 Subject: [PATCH] TocWidget: fix an erroneous collapse and optimise updates based on profiling 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 | 1 - src/frontends/qt4/TocWidget.cpp | 85 +++++++++++++++------------------ src/frontends/qt4/TocWidget.h | 18 +++---- 3 files changed, 44 insertions(+), 60 deletions(-) diff --git a/src/frontends/qt4/GuiToc.cpp b/src/frontends/qt4/GuiToc.cpp index b757c90458..91a5d7eb78 100644 --- a/src/frontends/qt4/GuiToc.cpp +++ b/src/frontends/qt4/GuiToc.cpp @@ -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(); diff --git a/src/frontends/qt4/TocWidget.cpp b/src/frontends/qt4/TocWidget.cpp index 4fa6aef7a3..ed188b8fa6 100644 --- a/src/frontends/qt4/TocWidget.cpp +++ b/src/frontends/qt4/TocWidget.cpp @@ -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_) { + tocTV->setEnabled(true); + tocTV->setUpdatesEnabled(true); + 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::finishUpdateView() +{ + // 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(); - tocTV->setEnabled(true); - tocTV->setUpdatesEnabled(true); - if (focus_) - tocTV->setFocus(); -} - - -void TocWidget::checkModelChanged() -{ - if (!gui_view_.documentBufferView() || - gui_view_.tocModels().model(current_type_) != tocTV->model()) - realUpdateView(); } @@ -534,6 +524,7 @@ void TocWidget::init(QString const & str) typeCO->blockSignals(true); typeCO->setCurrentIndex(new_index); typeCO->blockSignals(false); + updateViewNow(); } } // namespace frontend diff --git a/src/frontends/qt4/TocWidget.h b/src/frontends/qt4/TocWidget.h index ec1d5ad264..3f67606302 100644 --- a/src/frontends/qt4/TocWidget.h +++ b/src/frontends/qt4/TocWidget.h @@ -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