[patch] Selection stats in statusbar

Pavel Sanda sanda at lyx.org
Thu Aug 4 21:35:31 UTC 2022


On Sun, Jul 31, 2022 at 07:29:35AM -0400, Scott Kostyshak wrote:
> On Sun, Jul 31, 2022 at 11:34:25AM +0200, Pavel Sanda wrote:
> > On Sun, Jul 31, 2022 at 05:16:17AM -0400, Scott Kostyshak wrote:
> > > I'm on vacation and won't be able to test for a few weeks. Can you do
> > > the test I described before? Just select a big document, and then hold
> > > Shift + <UP> to decrease the selection towards the beginning of the
> > > document. I just counted using a wrist stopwatch and compared
> > > with/without the patch.
> > 
> > I did that already and haven't seen noticeable lag. But I might have too 
> > powerful machine, that's why I ask. Anyway, increasing delay would be
> > trivial change later.
> That's good enough for me. 0.5 seems fine to me and I imagine it could

Attached is a newer version. Actually something like 4th rewrite - I figured
that even the combination of mid-sized document and very fast keyboard repeat
rates has already visible interference from stats computation.

I ended with more conservative approach - stats updates have now 5s sampling
rate and I think that's totally fine for casual statusbar look how your 
document is doing.

The only situation when sampling rate get's back 0.5s is when small
piece (<5000 chars) of text is being selected -  that's when you need
instant visual feedback (and it's the initial motivation for this whole
patch).

I suggest we put this into master and let ppl test on their machines
if any noticeable changes are visible when editing documents. At worst
we can disable this feature by default.

Pavel
-------------- next part --------------
diff --git a/lib/ui/stdcontext.inc b/lib/ui/stdcontext.inc
index bd94e7c904..8c79c5f351 100644
--- a/lib/ui/stdcontext.inc
+++ b/lib/ui/stdcontext.inc
@@ -731,6 +731,8 @@ Menuset
 		Separator
 		Item "Show Zoom Level|Z" "ui-toggle zoomlevel"
 		Item "Show Zoom Slider|S" "ui-toggle zoomslider"
+		Separator
+		Item "Show Document Statistics|D" "ui-toggle statistics"
 	End
 
 End
diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp
index 992b47a441..ae8245d070 100644
--- a/src/LyXAction.cpp
+++ b/src/LyXAction.cpp
@@ -4097,6 +4097,7 @@ void LyXAction::init()
                frame      : Toggle visibility of the frames around editing window.\n
                zoomslider : Toggle visibility of the zoom slider in statusbar.\n
                zoomlevel  : Toggle visibility of the zoom level display in statusbar.\n
+               statistics : Toggle visibility of the document statistics count in statusbar.\n
                fullscreen : Toggle fullscreen mode. This also covers calling the
                             previous functions. However #LFUN_TOOLBAR_TOGGLE for the
                             custom tweaks of the toolbars should be used.
diff --git a/src/frontends/qt/GuiView.cpp b/src/frontends/qt/GuiView.cpp
index 8f6bf07115..fd738e100e 100644
--- a/src/frontends/qt/GuiView.cpp
+++ b/src/frontends/qt/GuiView.cpp
@@ -523,6 +523,7 @@ public:
 
 	///
 	QTimer statusbar_timer_;
+	QTimer statusbar_stats_timer_;
 	/// auto-saving of buffers
 	Timeout autosave_timeout_;
 
@@ -546,6 +547,18 @@ public:
 
 	/// flag against a race condition due to multiclicks, see bug #1119
 	bool in_show_;
+
+	// Timers for statistic updates in buffer
+	/// Current time left to the nearest info update
+	int time_to_update = 1000;
+	///Basic step for timer in ms. Basically reaction time for short selections
+	int const timer_rate = 500;
+	/// Real stats updates infrequently. First they take long time for big buffers, second
+	/// they are visible for fast-repeat keyboards even for mid documents.
+	int const default_stats_rate = 5000;
+	/// Detection of new selection, so we can react fast
+	bool already_in_selection_ = false;
+
 };
 
 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
@@ -553,8 +566,8 @@ QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
 
 GuiView::GuiView(int id)
 	: d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
-	  command_execute_(false), minibuffer_focus_(false), toolbarsMovable_(true),
-	  devel_mode_(false)
+	  command_execute_(false), minibuffer_focus_(false), stat_counts_enabled_(true),
+	  toolbarsMovable_(true), devel_mode_(false)
 {
 	connect(this, SIGNAL(bufferViewChanged()),
 	        this, SLOT(onBufferViewChanged()));
@@ -582,6 +595,9 @@ GuiView::GuiView(int id)
 	}
 	connect(&d.statusbar_timer_, SIGNAL(timeout()),
 		this, SLOT(clearMessage()));
+	connect(&d.statusbar_stats_timer_, SIGNAL(timeout()),
+		this, SLOT(showStats()));
+	d.statusbar_stats_timer_.start(d.timer_rate);
 
 	// We don't want to keep the window in memory if it is closed.
 	setAttribute(Qt::WA_DeleteOnClose, true);
@@ -627,6 +643,13 @@ GuiView::GuiView(int id)
 		busySVG, SLOT(hide()));
 	connect(busySVG, SIGNAL(pressed()), this, SLOT(checkCancelBackground()));
 
+	stat_counts_ = new QLabel(statusBar());
+	stat_counts_->setAlignment(Qt::AlignCenter);
+	stat_counts_->setFrameStyle(QFrame::StyledPanel);
+	stat_counts_->hide();
+	statusBar()->addPermanentWidget(stat_counts_);
+
+
 	QFontMetrics const fm(statusBar()->fontMetrics());
 
 	zoom_slider_ = new QSlider(Qt::Horizontal, statusBar());
@@ -952,6 +975,7 @@ void GuiView::saveLayout() const
 	settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
 	settings.setValue("zoom_value_visible", zoom_value_->isVisible());
 	settings.setValue("zoom_slider_visible", zoom_slider_->isVisible());
+	settings.setValue("document_stats_enabled", stat_counts_enabled_);
 }
 
 
@@ -1001,6 +1025,9 @@ bool GuiView::restoreLayout()
 	zoom_in_->setVisible(show_zoom_slider);
 	zoom_out_->setVisible(show_zoom_slider);
 
+	stat_counts_enabled_ = settings.value("document_stats_enabled", true).toBool();
+	stat_counts_->setVisible(stat_counts_enabled_);
+
 	if (guiApp->platformName() == "qt4x11" || guiApp->platformName() == "xcb") {
 		QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
 		QSize size = settings.value("size", QSize(690, 510)).toSize();
@@ -1271,6 +1298,7 @@ void GuiView::closeEvent(QCloseEvent * close_event)
 
 	// Make sure the timer time out will not trigger a statusbar update.
 	d.statusbar_timer_.stop();
+	d.statusbar_stats_timer_.stop();
 
 	// Saving fullscreen requires additional tweaks in the toolbar code.
 	// It wouldn't also work under linux natively.
@@ -1376,6 +1404,59 @@ void GuiView::clearMessage()
 	d.statusbar_timer_.stop();
 }
 
+void GuiView::showStats()
+{
+	if (!stat_counts_enabled_)
+		return;
+
+
+	d.time_to_update -= d.timer_rate;
+
+	BufferView * bv = currentBufferView();
+	Buffer * buf = bv ? &bv->buffer() : nullptr;
+	if (buf) {
+
+		Cursor const & cur = bv->cursor();
+
+		//we start new selection and need faster update
+		if (!d.already_in_selection_ && cur.selection())
+			d.time_to_update = 0;
+
+		if (d.time_to_update <= 0) {
+
+			DocIterator from, to;
+
+			if (cur.selection()) {
+				from = cur.selectionBegin();
+				to = cur.selectionEnd();
+				d.already_in_selection_ = true;
+			} else {
+				from = doc_iterator_begin(buf);
+				to = doc_iterator_end(buf);
+				d.already_in_selection_ = false;
+			}
+
+			buf->updateStatistics(from, to);
+
+			int const words = buf->wordCount();
+			int const chars = buf->charCount(false);
+			int const chars_blanks = buf->charCount(true);
+
+			QString stats = "w:" + QString::number(words) + " c:" +  QString::number(chars) +
+				 " cb:" + QString::number(chars_blanks);
+			stat_counts_->setText(stats);
+			stat_counts_->show();
+
+			d.time_to_update = d.default_stats_rate;
+			//fast updates for small selections
+			if (chars_blanks < 5000 && cur.selection())
+				d.time_to_update = d.timer_rate;
+		}
+
+	} else
+		stat_counts_->hide();
+}
+
 
 void GuiView::updateWindowTitle(GuiWorkArea * wa)
 {
@@ -2419,6 +2500,8 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 			flag.setOnOff(zoom_value_ ? zoom_value_->isVisible() : false);
 		} else if (cmd.argument() == "zoomslider") {
 			flag.setOnOff(zoom_slider_ ? zoom_slider_->isVisible() : false);
+		} else if (cmd.argument() == "statistics") {
+			flag.setOnOff(stat_counts_enabled_);
 		} else
 			flag.setOnOff(isFullScreen());
 		break;
@@ -4904,6 +4987,9 @@ bool GuiView::lfunUiToggle(string const & ui_component)
 		zoom_slider_->setVisible(!zoom_slider_->isVisible());
 		zoom_in_->setVisible(zoom_slider_->isVisible());
 		zoom_out_->setVisible(zoom_slider_->isVisible());
+	} else if (ui_component == "statistics") {
+		stat_counts_enabled_ = !stat_counts_enabled_;
+		stat_counts_->setVisible(stat_counts_enabled_);
 	} else if (ui_component == "frame") {
 		int const l = contentsMargins().left();
 
diff --git a/src/frontends/qt/GuiView.h b/src/frontends/qt/GuiView.h
index 241701cb8f..2f7c34e422 100644
--- a/src/frontends/qt/GuiView.h
+++ b/src/frontends/qt/GuiView.h
@@ -235,6 +235,8 @@ public Q_SLOTS:
 	/// idle timeout.
 	/// clear any temporary message and replace with current status.
 	void clearMessage();
+	/// show documents stats in toolbar and trigger new iteration
+	void showStats();
 	///
 	void updateWindowTitle(GuiWorkArea * wa);
 	///
@@ -510,6 +512,10 @@ private:
 	QLabel * read_only_;
 	/// Statusbar widget that shows version control status
 	QLabel * version_control_;
+	/// Statusbar widget that document count statistics
+	QLabel * stat_counts_;
+	/// Stats info feature can be disabled by context menu
+	bool stat_counts_enabled_;
 	/// Statusbar widget that shows zoom value
 	QLabel * zoom_value_;
 	/// The zoom slider widget


More information about the lyx-devel mailing list