From 681b9b5ba3f43658e5664eed9f551b4115b474cc Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sat, 27 Jan 2018 20:51:16 +0300
Subject: [PATCH] Improve text selection in bubbles.

---
 .../history/view/history_view_message.cpp     | 40 ++++++++++++-------
 Telegram/SourceFiles/ui/text/text.cpp         | 33 ++++++++++-----
 2 files changed, 48 insertions(+), 25 deletions(-)

diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index 86d3f41dc..d842e5fc3 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -739,11 +739,19 @@ TextState Message::textState(
 			}
 		}
 
-		auto needDateCheck = mediaOnBottom
-			? !(entry
-				? entry->customInfoLayout()
-				: media->customInfoLayout())
-			: true;
+		auto checkForPointInTime = [&] {
+			if (mediaOnBottom && (entry || media->customInfoLayout())) {
+				return;
+			}
+			const auto inDate = pointInTime(
+				g.left() + g.width(),
+				g.top() + g.height(),
+				point,
+				InfoDisplayType::Default);
+			if (inDate) {
+				result.cursor = CursorState::Date;
+			}
+		};
 		if (mediaDisplayed) {
 			auto mediaHeight = media->height();
 			auto mediaLeft = trect.x() - st::msgPadding.left();
@@ -752,17 +760,19 @@ TextState Message::textState(
 			if (point.y() >= mediaTop && point.y() < mediaTop + mediaHeight) {
 				result = media->textState(point - QPoint(mediaLeft, mediaTop), request);
 				result.symbol += item->_text.length();
-			} else if (trect.contains(point)) {
-				getStateText(point, trect, &result, request);
-			}
-		} else if (trect.contains(point)) {
-			getStateText(point, trect, &result, request);
-		}
-		if (needDateCheck) {
-			if (pointInTime(g.left() + g.width(), g.top() + g.height(), point, InfoDisplayType::Default)) {
-				result.cursor = CursorState::Date;
+			} else if (getStateText(point, trect, &result, request)) {
+				checkForPointInTime();
+				return result;
+			} else if (point.y() >= trect.y() + trect.height()) {
+				result.symbol = item->_text.length();
 			}
+		} else if (getStateText(point, trect, &result, request)) {
+			checkForPointInTime();
+			return result;
+		} else if (point.y() >= trect.y() + trect.height()) {
+			result.symbol = item->_text.length();
 		}
+		checkForPointInTime();
 		if (displayRightAction()) {
 			const auto fastShareSkip = snap(
 				(g.height() - st::historyFastShareSize) / 2,
@@ -921,7 +931,7 @@ bool Message::getStateText(
 		return false;
 	}
 	const auto item = message();
-	if (trect.contains(point)) {
+	if (base::in_range(point.y(), trect.y(), trect.y() + trect.height())) {
 		*outResult = TextState(item, item->_text.getState(
 			point - trect.topLeft(),
 			trect.width(),
diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp
index 06d2e62e1..e47db15f2 100644
--- a/Telegram/SourceFiles/ui/text/text.cpp
+++ b/Telegram/SourceFiles/ui/text/text.cpp
@@ -1184,12 +1184,16 @@ private:
 		auto currentBlock = _t->_blocks[blockIndex].get();
 		auto nextBlock = (++blockIndex < _blocksSize) ? _t->_blocks[blockIndex].get() : nullptr;
 
-		int32 delta = (currentBlock->from() < _lineStart ? qMin(_lineStart - currentBlock->from(), 2) : 0);
-		_localFrom = _lineStart - delta;
-		int32 lineEnd = (_endBlock && _endBlock->from() < trimmedLineEnd && !elidedLine) ? qMin(uint16(trimmedLineEnd + 2), _t->countBlockEnd(_endBlockIter, _end)) : trimmedLineEnd;
+		const auto extendLeft = (currentBlock->from() < _lineStart)
+			? qMin(_lineStart - currentBlock->from(), 2)
+			: 0;
+		_localFrom = _lineStart - extendLeft;
+		const auto extendedLineEnd = (_endBlock && _endBlock->from() < trimmedLineEnd && !elidedLine)
+			? qMin(uint16(trimmedLineEnd + 2), _t->countBlockEnd(_endBlockIter, _end))
+			: trimmedLineEnd;
 
-		auto lineText = _t->_text.mid(_localFrom, lineEnd - _localFrom);
-		auto lineStart = delta;
+		auto lineText = _t->_text.mid(_localFrom, extendedLineEnd - _localFrom);
+		auto lineStart = extendLeft;
 		auto lineLength = trimmedLineEnd - _lineStart;
 
 		if (elidedLine) {
@@ -1241,21 +1245,30 @@ private:
 		}
 
 		if (_fullWidthSelection) {
-			bool selectFromStart = (_selection.to > _lineStart) && (_lineStart > 0) && (_selection.from <= _lineStart);
-			bool selectTillEnd = (_selection.to >= _lineEnd) && (_lineEnd < _t->_text.size()) && (_selection.from < _lineEnd) && (!_endBlock || _endBlock->type() != TextBlockTSkip);
+			const auto selectFromStart = (_selection.to > _lineStart)
+				&& (_lineStart > 0)
+				&& (_selection.from <= _lineStart);
+			const auto selectTillEnd = (_selection.to > trimmedLineEnd)
+				&& (trimmedLineEnd < _t->_text.size())
+				&& (_selection.from <= trimmedLineEnd)
+				&& (!_endBlock || _endBlock->type() != TextBlockTSkip);
 
-			if ((selectFromStart && _parDirection == Qt::LeftToRight) || (selectTillEnd && _parDirection == Qt::RightToLeft)) {
+			if ((selectFromStart && _parDirection == Qt::LeftToRight)
+				|| (selectTillEnd && _parDirection == Qt::RightToLeft)) {
 				if (x > _x) {
 					fillSelectRange(_x, x);
 				}
 			}
-			if ((selectTillEnd && _parDirection == Qt::LeftToRight) || (selectFromStart && _parDirection == Qt::RightToLeft)) {
+			if ((selectTillEnd && _parDirection == Qt::LeftToRight)
+				|| (selectFromStart && _parDirection == Qt::RightToLeft)) {
 				if (x < _x + _wLeft) {
 					fillSelectRange(x + _w - _wLeft, _x + _w);
 				}
 			}
 		}
-		if (trimmedLineEnd == _lineStart && !elidedLine) return true;
+		if (trimmedLineEnd == _lineStart && !elidedLine) {
+			return true;
+		}
 
 		if (!elidedLine) initParagraphBidi(); // if was not inited