From 6fa64c92216dc79a40b938709ace75779bc96d6e Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 00:10:05 +0200 Subject: [PATCH 1/9] simplify double click selection --- src/messages/Selection.hpp | 18 +-- src/widgets/helper/ChannelView.cpp | 220 ++++++----------------------- src/widgets/helper/ChannelView.hpp | 9 +- 3 files changed, 52 insertions(+), 195 deletions(-) diff --git a/src/messages/Selection.hpp b/src/messages/Selection.hpp index 5af3612bc59..82fa06fe745 100644 --- a/src/messages/Selection.hpp +++ b/src/messages/Selection.hpp @@ -72,6 +72,13 @@ struct Selection { return !this->operator==(b); } + //union of both selections + Selection operator|=(const Selection &b) const + { + return {std::min(this->selectionMin, b.selectionMin), + std::max(this->selectionMax, b.selectionMax)}; + } + bool isEmpty() const { return this->start == this->end; @@ -127,15 +134,4 @@ struct Selection { } } }; - -struct DoubleClickSelection { - uint32_t originalStart{0}; - uint32_t originalEnd{0}; - uint32_t origMessageIndex{0}; - bool selectingLeft{false}; - bool selectingRight{false}; - SelectionItem origStartItem; - SelectionItem origEndItem; -}; - } // namespace chatterino diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index de185eb6292..43034f640bd 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1088,10 +1088,8 @@ void ChannelView::resizeEvent(QResizeEvent *) this->update(); } -void ChannelView::setSelection(const SelectionItem &start, - const SelectionItem &end) +void ChannelView::setSelection(const Selection &newSelection) { - auto newSelection = Selection(start, end); if (this->selection_ != newSelection) { this->selection_ = newSelection; @@ -1100,6 +1098,12 @@ void ChannelView::setSelection(const SelectionItem &start, } } +void ChannelView::setSelection(const SelectionItem &start, + const SelectionItem &end) +{ + this->setSelection({start, end}); +} + MessageElementFlags ChannelView::getFlags() const { auto app = getApp(); @@ -1512,15 +1516,30 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) this->currentMousePosition_ = event->screenPos(); } - // is selecting + // check for word underneath cursor + const MessageLayoutElement *hoverLayoutElement = + layout->getElementAt(relativePos); + + //selecting single characters if (this->isLeftMouseDown_) { auto index = layout->getSelectionIndex(relativePos); - this->setSelection(this->selection_.start, SelectionItem(messageIndex, index)); } + //selecting whole words + if (this->isDoubleClick_ && hoverLayoutElement) + { + auto [wordStart, wordEnd] = + this->getWordBounds(layout.get(), hoverLayoutElement, relativePos); + auto wordSelection = Selection{SelectionItem(messageIndex, wordStart), + SelectionItem(messageIndex, wordEnd)}; + auto selectUnion = this->doubleClickSelection_ |= wordSelection; + + this->setSelection(selectUnion); + } + // message under cursor is collapsed if (layout->flags.has(MessageLayoutFlag::Collapsed)) { @@ -1529,10 +1548,6 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) return; } - // check if word underneath cursor - const MessageLayoutElement *hoverLayoutElement = - layout->getElementAt(relativePos); - if (hoverLayoutElement == nullptr) { this->setCursor(Qt::ArrowCursor); @@ -1540,142 +1555,6 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) return; } - if (this->isDoubleClick_) - { - int wordStart; - int wordEnd; - this->getWordBounds(layout.get(), hoverLayoutElement, relativePos, - wordStart, wordEnd); - SelectionItem newStart(messageIndex, wordStart); - SelectionItem newEnd(messageIndex, wordEnd); - - // Selection changed in same message - if (messageIndex == this->doubleClickSelection_.origMessageIndex) - { - // Selecting to the left - if (wordStart < this->selection_.start.charIndex && - !this->doubleClickSelection_.selectingRight) - { - this->doubleClickSelection_.selectingLeft = true; - // Ensure that the original word stays selected(Edge case) - if (wordStart > this->doubleClickSelection_.originalEnd) - { - this->setSelection( - this->doubleClickSelection_.origStartItem, newEnd); - } - else - { - this->setSelection(newStart, this->selection_.end); - } - // Selecting to the right - } - else if (wordEnd > this->selection_.end.charIndex && - !this->doubleClickSelection_.selectingLeft) - { - this->doubleClickSelection_.selectingRight = true; - // Ensure that the original word stays selected(Edge case) - if (wordEnd < this->doubleClickSelection_.originalStart) - { - this->setSelection(newStart, - this->doubleClickSelection_.origEndItem); - } - else - { - this->setSelection(this->selection_.start, newEnd); - } - } - // Swapping from selecting left to selecting right - if (wordStart > this->selection_.start.charIndex && - !this->doubleClickSelection_.selectingRight) - { - if (wordStart > this->doubleClickSelection_.originalEnd) - { - this->doubleClickSelection_.selectingLeft = false; - this->doubleClickSelection_.selectingRight = true; - this->setSelection( - this->doubleClickSelection_.origStartItem, newEnd); - } - else - { - this->setSelection(newStart, this->selection_.end); - } - // Swapping from selecting right to selecting left - } - else if (wordEnd < this->selection_.end.charIndex && - !this->doubleClickSelection_.selectingLeft) - { - if (wordEnd < this->doubleClickSelection_.originalStart) - { - this->doubleClickSelection_.selectingLeft = true; - this->doubleClickSelection_.selectingRight = false; - this->setSelection(newStart, - this->doubleClickSelection_.origEndItem); - } - else - { - this->setSelection(this->selection_.start, newEnd); - } - } - // Selection changed in a different message - } - else - { - // Message over the original - if (messageIndex < this->selection_.start.messageIndex) - { - // Swapping from left to right selecting - if (!this->doubleClickSelection_.selectingLeft) - { - this->doubleClickSelection_.selectingLeft = true; - this->doubleClickSelection_.selectingRight = false; - } - if (wordStart < this->selection_.start.charIndex && - !this->doubleClickSelection_.selectingRight) - { - this->doubleClickSelection_.selectingLeft = true; - } - this->setSelection(newStart, - this->doubleClickSelection_.origEndItem); - // Message under the original - } - else if (messageIndex > this->selection_.end.messageIndex) - { - // Swapping from right to left selecting - if (!this->doubleClickSelection_.selectingRight) - { - this->doubleClickSelection_.selectingLeft = false; - this->doubleClickSelection_.selectingRight = true; - } - if (wordEnd > this->selection_.end.charIndex && - !this->doubleClickSelection_.selectingLeft) - { - this->doubleClickSelection_.selectingRight = true; - } - this->setSelection(this->doubleClickSelection_.origStartItem, - newEnd); - // Selection changed in non original message - } - else - { - if (this->doubleClickSelection_.selectingLeft) - { - this->setSelection(newStart, this->selection_.end); - } - else - { - this->setSelection(this->selection_.start, newEnd); - } - } - } - // Reset direction of selection - if (wordStart == this->doubleClickSelection_.originalStart && - wordEnd == this->doubleClickSelection_.originalEnd) - { - this->doubleClickSelection_.selectingLeft = - this->doubleClickSelection_.selectingRight = false; - } - } - auto element = &hoverLayoutElement->getCreator(); bool isLinkValid = hoverLayoutElement->getLink().isValid(); auto emoteElement = dynamic_cast(element); @@ -1950,8 +1829,6 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) // check if mouse was pressed if (event->button() == Qt::LeftButton) { - this->doubleClickSelection_.selectingLeft = - this->doubleClickSelection_.selectingRight = false; if (this->isDoubleClick_) { this->isDoubleClick_ = false; @@ -2597,6 +2474,10 @@ void ChannelView::mouseDoubleClickEvent(QMouseEvent *event) return; } + this->isDoubleClick_ = true; + this->lastDClickPosition_ = event->screenPos(); + this->clickTimer_->start(); + // message under cursor is collapsed if (layout->flags.has(MessageLayoutFlag::Collapsed)) { @@ -2605,38 +2486,17 @@ void ChannelView::mouseDoubleClickEvent(QMouseEvent *event) const MessageLayoutElement *hoverLayoutElement = layout->getElementAt(relativePos); - this->lastDClickPosition_ = event->screenPos(); if (hoverLayoutElement == nullptr) { - // Possibility for triple click which doesn't have to be over an - // existing layout element - this->clickTimer_->start(); return; } - if (!this->isLeftMouseDown_) - { - this->isDoubleClick_ = true; - - int wordStart; - int wordEnd; - this->getWordBounds(layout.get(), hoverLayoutElement, relativePos, - wordStart, wordEnd); - - this->clickTimer_->start(); - - SelectionItem wordMin(messageIndex, wordStart); - SelectionItem wordMax(messageIndex, wordEnd); - - this->doubleClickSelection_.originalStart = wordStart; - this->doubleClickSelection_.originalEnd = wordEnd; - this->doubleClickSelection_.origMessageIndex = messageIndex; - this->doubleClickSelection_.origStartItem = wordMin; - this->doubleClickSelection_.origEndItem = wordMax; - - this->setSelection(wordMin, wordMax); - } + auto [wordStart, wordEnd] = + this->getWordBounds(layout.get(), hoverLayoutElement, relativePos); + this->doubleClickSelection_ = {SelectionItem(messageIndex, wordStart), + SelectionItem(messageIndex, wordEnd)}; + this->setSelection(this->doubleClickSelection_); if (getSettings()->linksDoubleClickOnly) { @@ -2892,17 +2752,17 @@ void ChannelView::selectWholeMessage(MessageLayout *layout, int &messageIndex) this->setSelection(msgStart, msgEnd); } -void ChannelView::getWordBounds(MessageLayout *layout, - const MessageLayoutElement *element, - const QPoint &relativePos, int &wordStart, - int &wordEnd) +std::pair ChannelView::getWordBounds( + MessageLayout *layout, const MessageLayoutElement *element, + const QPoint &relativePos) { - const int mouseInWordIndex = element->getMouseOverIndex(relativePos); - wordStart = layout->getSelectionIndex(relativePos) - mouseInWordIndex; + const int wordStart = layout->getSelectionIndex(relativePos) - + element->getMouseOverIndex(relativePos); const int selectionLength = element->getSelectionIndexCount(); const int length = element->hasTrailingSpace() ? selectionLength - 1 : selectionLength; - wordEnd = wordStart + length; + + return {wordStart, wordStart + length}; } void ChannelView::enableScrolling(const QPointF &scrollStart) diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 27ae83cfe14..9f7d56df5e6 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -210,10 +210,11 @@ class ChannelView final : public BaseWidget void drawMessages(QPainter &painter); void setSelection(const SelectionItem &start, const SelectionItem &end); + void setSelection(const Selection &newSelection); void selectWholeMessage(MessageLayout *layout, int &messageIndex); - void getWordBounds(MessageLayout *layout, - const MessageLayoutElement *element, - const QPoint &relativePos, int &wordStart, int &wordEnd); + std::pair getWordBounds(MessageLayout *layout, + const MessageLayoutElement *element, + const QPoint &relativePos); void handleMouseClick(QMouseEvent *event, const MessageLayoutElement *hoveredElement, @@ -307,7 +308,7 @@ class ChannelView final : public BaseWidget bool isLeftMouseDown_ = false; bool isRightMouseDown_ = false; bool isDoubleClick_ = false; - DoubleClickSelection doubleClickSelection_; + Selection doubleClickSelection_{}; QPointF lastLeftPressPosition_; QPointF lastRightPressPosition_; QPointF lastDClickPosition_; From 1a0b611b3c23de9d382162c9589f89c137b5194d Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 00:39:48 +0200 Subject: [PATCH 2/9] fix doubleClickSelection pause offset --- src/widgets/helper/ChannelView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 43034f640bd..a408d8e3501 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -385,6 +385,7 @@ void ChannelView::unpaused() { /// Move selection this->selection_.shiftMessageIndex(this->pauseSelectionOffset_); + this->doubleClickSelection_.shiftMessageIndex(this->pauseSelectionOffset_); this->pauseSelectionOffset_ = 0; } @@ -927,6 +928,7 @@ void ChannelView::messageAppended(MessagePtr &message, this->scrollBar_->scrollToBottom(false); } this->selection_.shiftMessageIndex(1); + this->doubleClickSelection_.shiftMessageIndex(1); } } From 0144d9e998ac3bfc992bb332367556d267ed5772 Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 01:02:38 +0200 Subject: [PATCH 3/9] changelog for bugfix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af1f47a8807..a9ec44b19d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Bugfix: Fixed issue on Windows preventing the title bar from being dragged in the top left corner. (#4873) - Bugfix: Fixed an issue where reply context didn't render correctly if an emoji was touching text. (#4875) - Bugfix: Fixed the input completion popup from disappearing when clicking on it on Windows and macOS. (#4876) +- Bugfix: Fixed double-click text selection moving its position with each new message. (#4898) - Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791) - Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767) - Dev: Tests now run on Ubuntu 22.04 instead of 20.04 to loosen C++ restrictions in tests. (#4774) From c5b56b6cab63402f8c8a1109d20e185defea490d Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 02:20:13 +0200 Subject: [PATCH 4/9] add header, fix narrowing --- src/messages/Selection.hpp | 1 + src/widgets/helper/ChannelView.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/messages/Selection.hpp b/src/messages/Selection.hpp index 82fa06fe745..026e16e3441 100644 --- a/src/messages/Selection.hpp +++ b/src/messages/Selection.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index a408d8e3501..45be533244f 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -2758,8 +2758,8 @@ std::pair ChannelView::getWordBounds( MessageLayout *layout, const MessageLayoutElement *element, const QPoint &relativePos) { - const int wordStart = layout->getSelectionIndex(relativePos) - - element->getMouseOverIndex(relativePos); + const auto wordStart = layout->getSelectionIndex(relativePos) - + element->getMouseOverIndex(relativePos); const int selectionLength = element->getSelectionIndexCount(); const int length = element->hasTrailingSpace() ? selectionLength - 1 : selectionLength; From ce10b811b024fe625c0f7b8c3f997b7c3aff659f Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:41:22 +0200 Subject: [PATCH 5/9] getWordBounds, comment and size_t use --- src/widgets/helper/ChannelView.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 45be533244f..624ac3c0366 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -2754,14 +2754,15 @@ void ChannelView::selectWholeMessage(MessageLayout *layout, int &messageIndex) this->setSelection(msgStart, msgEnd); } +/// @returns [wordStart, wordEnd] position indexes for word hovered by mouse std::pair ChannelView::getWordBounds( MessageLayout *layout, const MessageLayoutElement *element, const QPoint &relativePos) { const auto wordStart = layout->getSelectionIndex(relativePos) - element->getMouseOverIndex(relativePos); - const int selectionLength = element->getSelectionIndexCount(); - const int length = + const auto selectionLength = element->getSelectionIndexCount(); + const auto length = element->hasTrailingSpace() ? selectionLength - 1 : selectionLength; return {wordStart, wordStart + length}; From 008fa5b5ad36782c835b2451f43d9b22cc602099 Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:52:40 +0200 Subject: [PATCH 6/9] change op type --- src/messages/Selection.hpp | 2 +- src/widgets/helper/ChannelView.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/messages/Selection.hpp b/src/messages/Selection.hpp index 026e16e3441..b04ad167368 100644 --- a/src/messages/Selection.hpp +++ b/src/messages/Selection.hpp @@ -74,7 +74,7 @@ struct Selection { } //union of both selections - Selection operator|=(const Selection &b) const + Selection operator|(const Selection &b) const { return {std::min(this->selectionMin, b.selectionMin), std::max(this->selectionMax, b.selectionMax)}; diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 624ac3c0366..582ef546d9b 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1537,7 +1537,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) this->getWordBounds(layout.get(), hoverLayoutElement, relativePos); auto wordSelection = Selection{SelectionItem(messageIndex, wordStart), SelectionItem(messageIndex, wordEnd)}; - auto selectUnion = this->doubleClickSelection_ |= wordSelection; + auto selectUnion = this->doubleClickSelection_ | wordSelection; this->setSelection(selectUnion); } From 0ccafd27533f8c0fc39b7dac9871803032e4523d Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:53:50 +0200 Subject: [PATCH 7/9] rename lastDClickPosition_ --- src/widgets/helper/ChannelView.cpp | 6 +++--- src/widgets/helper/ChannelView.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 582ef546d9b..ffa9e5dfe81 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1835,7 +1835,7 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) { this->isDoubleClick_ = false; // Was actually not a wanted triple-click - if (fabsf(distanceBetweenPoints(this->lastDClickPosition_, + if (fabsf(distanceBetweenPoints(this->lastDoubleClickPosition_, event->screenPos())) > 10.f) { this->clickTimer_->stop(); @@ -1854,7 +1854,7 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) // Triple-clicking a message selects the whole message if (foundElement && this->clickTimer_->isActive() && - (fabsf(distanceBetweenPoints(this->lastDClickPosition_, + (fabsf(distanceBetweenPoints(this->lastDoubleClickPosition_, event->screenPos())) < 10.f)) { this->selectWholeMessage(layout.get(), messageIndex); @@ -2477,7 +2477,7 @@ void ChannelView::mouseDoubleClickEvent(QMouseEvent *event) } this->isDoubleClick_ = true; - this->lastDClickPosition_ = event->screenPos(); + this->lastDoubleClickPosition_ = event->screenPos(); this->clickTimer_->start(); // message under cursor is collapsed diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 9f7d56df5e6..953774cd337 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -311,7 +311,7 @@ class ChannelView final : public BaseWidget Selection doubleClickSelection_{}; QPointF lastLeftPressPosition_; QPointF lastRightPressPosition_; - QPointF lastDClickPosition_; + QPointF lastDoubleClickPosition_; QTimer *clickTimer_; bool isScrolling_ = false; From a034cef8b03b6399dcc2d1f259247d875b1c0122 Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:54:44 +0200 Subject: [PATCH 8/9] move doubleClickSelection_ next to selection_ --- src/widgets/helper/ChannelView.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 953774cd337..88b66ad2186 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -308,7 +308,6 @@ class ChannelView final : public BaseWidget bool isLeftMouseDown_ = false; bool isRightMouseDown_ = false; bool isDoubleClick_ = false; - Selection doubleClickSelection_{}; QPointF lastLeftPressPosition_; QPointF lastRightPressPosition_; QPointF lastDoubleClickPosition_; @@ -331,6 +330,7 @@ class ChannelView final : public BaseWidget } cursors_; Selection selection_; + Selection doubleClickSelection_; const Context context_; From 3126d1dc2aa561075cd96545e52218ca06a0f384 Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:02:51 +0200 Subject: [PATCH 9/9] make selectUnion more clear --- src/widgets/helper/ChannelView.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index ffa9e5dfe81..c80b1ae50a8 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1522,7 +1522,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) const MessageLayoutElement *hoverLayoutElement = layout->getElementAt(relativePos); - //selecting single characters + // selecting single characters if (this->isLeftMouseDown_) { auto index = layout->getSelectionIndex(relativePos); @@ -1530,14 +1530,15 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) SelectionItem(messageIndex, index)); } - //selecting whole words + // selecting whole words if (this->isDoubleClick_ && hoverLayoutElement) { auto [wordStart, wordEnd] = this->getWordBounds(layout.get(), hoverLayoutElement, relativePos); - auto wordSelection = Selection{SelectionItem(messageIndex, wordStart), - SelectionItem(messageIndex, wordEnd)}; - auto selectUnion = this->doubleClickSelection_ | wordSelection; + auto hoveredWord = Selection{SelectionItem(messageIndex, wordStart), + SelectionItem(messageIndex, wordEnd)}; + // combined selection spanning from initially selected word to hoveredWord + auto selectUnion = this->doubleClickSelection_ | hoveredWord; this->setSelection(selectUnion); }