-
-
Notifications
You must be signed in to change notification settings - Fork 449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix double click to select full words #5243
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using IDs for words is a good idea. However, I think the concept shouldn't "leak" beyond MessageLayout
, i.e. ChannelView
and *MessageBuilder
shouldn't know about it.
One bug this implementation has is that it assumes that one TextElement
has at most one word. This isn't the case. The EmotePopup
for example, adds an element with multiple words:
chatterino2/src/widgets/dialogs/EmotePopup.cpp
Lines 53 to 55 in b9b1b8c
builder.emplace<TextElement>("no emotes available", | |
MessageElementFlag::Text, | |
MessageColor::System); |
MessageLayoutContainer
should generate the IDs. When a TextElement
adds its elements to the container, it generates a new ID for each word by calling something like container.nextWordID()
. This way, message builders don't need to know about word-IDs. (if you put the next ID after containsRTL
, it doesn't consume extra space)
Getting the word bounds should hide the concept of word-IDs too. It should have a single method like layout.getWordBoundsAt(QPoint)
which finds the layout element and finds the bounds of all layout elements with that word-ID (or returns the element's bounds if it has an ID of -1).
wordEnd += element->hasTrailingSpace() | ||
? element->getSelectionIndexCount() - 1 | ||
: element->getSelectionIndexCount(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works1 because elements created from one word don't have trailing spaces in between. However, this should be handled here. Only if the last element had a trailing space, the selection index should be reduced by one.
Footnotes
-
Actually, it doesn't work here, as this can be called with -1 in which case multiple elements can be selected and the selection won't span the entire group. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now this is done after the loop checking the last element
src/widgets/helper/ChannelView.cpp
Outdated
@@ -1817,7 +1817,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) | |||
if (this->isDoubleClick_ && hoverLayoutElement) | |||
{ | |||
auto [wordStart, wordEnd] = | |||
getWordBounds(layout.get(), hoverLayoutElement, relativePos); | |||
layout->getWordBounds(hoverLayoutElement->getWordId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can pass -1 to getWordBounds
. Example:
- Open emote popup in a channel that that doesn't have BTTV, FFZ or 7TV emotes
- Click on
Channel
, findno emotes available
- Double click next to the text (a few pixels)
- Quickly move the mouse over the text
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now we choose which logic to use inside layout->getWordBounds
/** | ||
* Elements that are part of the same word originally have the same id | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(See top level comment)
/** | |
* Elements that are part of the same word originally have the same id | |
*/ | |
/// @brief ID of a word inside its container | |
/// | |
/// One word has exactly one ID that is used to identify elements created | |
/// from the same word (due to wrapping). | |
/// IDs are unique in a MessageLayoutContainer. |
Btw, if you put this before text_
, it won't consume extra space and fit into the padding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
I tested this a bit more and everything seems to be working. Not sure if there is any point in documenting the tests as they would be just screenshots of random selections but let me know if I should. I'll remove the draft as it seems the final version will be something close to this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed some stuff, mostly small things. Could you review the comments I added to make sure I understood the code correctly?
if (element->getWordId() != -1) | ||
{ | ||
return this->container_.getWordBounds(element); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a comment here for what this significes? It's not clear to me what it means when the element we're looking at has a word id of -1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1 basically means that the element can't span across multiple lines (like an emote, for example). I agree the code is not very self explanatory here. Do you want a comment here or should I encapsulate that inside a function like element->canBeMultiline()
(name is kinda bad but you get the idea)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a comment here is fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
this->layout->layout(WIDTH, 1, MessageElementFlag::Text, false); | ||
} | ||
|
||
MockApplication mockApplication; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't quite understand how constructing MockApplication
here makes it so any all to getIApp
gets the MockApplication. Also this should probably only be done once instead of each time a test is created
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MockApplication inherits from mocks::EmptyApplicatino, which inherits from IApplication which sets a singleton to point to itself (check mocks/EmptyApplication & src/Application.cpp)
Testing RTL selections - it's very scuffed. Luckily, it's already scuffed before this PR, which means I don't think it's a blocker. |
Leaving this as a draft because I want some opinions if this is a good approach or not. Heavily inspired by this comment from @Nerixyz, but adding an id to each element instead of the prev/next approach. Currently works on basic tests, haven't tested more advanced stuff (RTL text, CJK characters, etc)