From 7b4bd5696bec22d26d46cfadd86c8db79e14de5f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 11 Oct 2024 23:04:56 +0300 Subject: [PATCH] Added check views to selection mode of messages. --- .../admin_log/history_admin_log_inner.cpp | 4 +- .../admin_log/history_admin_log_inner.h | 2 +- .../history/history_inner_widget.cpp | 92 ++++++++++++++----- .../history/history_inner_widget.h | 10 +- .../history/view/history_view_element.cpp | 24 ++++- .../history/view/history_view_element.h | 13 ++- .../history/view/history_view_list_widget.cpp | 50 +++++++++- .../history/view/history_view_list_widget.h | 11 ++- .../history/view/history_view_message.cpp | 66 ++++++++++++- .../history/view/history_view_message.h | 2 + .../view/history_view_replies_section.cpp | 2 +- Telegram/SourceFiles/ui/chat/chat.style | 5 + 12 files changed, 242 insertions(+), 39 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 1edf0d158..b67629f92 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -662,8 +662,8 @@ bool InnerWidget::elementUnderCursor( return (Element::Hovered() == view); } -bool InnerWidget::elementInSelectionMode() { - return false; +HistoryView::SelectionModeResult InnerWidget::elementInSelectionMode() { + return {}; } bool InnerWidget::elementIntersectsRange( diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index ed13def82..db52cac1c 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -94,7 +94,7 @@ public: HistoryView::Context elementContext() override; bool elementUnderCursor( not_null view) override; - bool elementInSelectionMode() override; + HistoryView::SelectionModeResult elementInSelectionMode() override; bool elementIntersectsRange( not_null view, int from, diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index fe91dbf4c..22de853c0 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -43,8 +43,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/report_box_graphics.h" #include "ui/layers/generic_box.h" #include "ui/controls/delete_message_context_action.h" -#include "ui/painter.h" #include "ui/inactive_press.h" +#include "ui/painter.h" +#include "ui/rect.h" #include "ui/ui_utility.h" #include "window/window_session_controller.h" #include "window/window_controller.h" @@ -210,8 +211,10 @@ public: not_null view) override { return (Element::Moused() == view); } - bool elementInSelectionMode() override { - return _widget ? _widget->inSelectionMode() : false; + HistoryView::SelectionModeResult elementInSelectionMode() override { + return _widget + ? _widget->inSelectionMode() + : HistoryView::SelectionModeResult(); } bool elementIntersectsRange( not_null view, @@ -599,7 +602,7 @@ void HistoryInner::setupSwipeReply() { } }, [=, show = _controller->uiShow()](int cursorTop) { auto result = HistoryView::SwipeHandlerFinishData(); - if (inSelectionMode()) { + if (inSelectionMode().inSelectionMode) { return result; } enumerateItems([&]( @@ -1369,6 +1372,18 @@ bool HistoryInner::eventHook(QEvent *e) { return RpWidget::eventHook(e); } +int HistoryInner::SelectionViewOffset( + not_null inner, + not_null view) { + if (inner->_lastInSelectionMode) { + const auto translation + = Element::AdditionalSpaceForSelectionCheckbox(view); + const auto progress = inner->_inSelectionModeAnimation.value(1.); + return translation * progress; + } + return 0; +} + HistoryInner::VideoUserpic *HistoryInner::validateVideoUserpic( not_null peer) { if (!peer->isPremium() @@ -1705,7 +1720,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but && (!Element::Hovered() || !Element::Hovered()->allowTextSelectionByHandler(pressed))) { _mouseAction = MouseAction::PrepareDrag; - } else if (inSelectionMode()) { + } else if (inSelectionMode().inSelectionMode) { if (_dragStateItem && _selected.find(_dragStateItem) != _selected.cend() && Element::Hovered()) { @@ -1993,7 +2008,7 @@ void HistoryInner::mouseActionFinish( // toggle selection instead of activating the pressed link if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive - && inSelectionMode() + && inSelectionMode().inSelectionMode && button != Qt::RightButton) { if (const auto view = viewByItem(_mouseActionItem)) { if (view->toggleSelectionByHandlerClick(activated)) { @@ -2026,7 +2041,7 @@ void HistoryInner::mouseActionFinish( } if ((_mouseAction == MouseAction::PrepareSelect) && !_pressWasInactive - && inSelectionMode()) { + && inSelectionMode().inSelectionMode) { changeSelectionAsGroup( &_selected, _mouseActionItem, @@ -2043,7 +2058,7 @@ void HistoryInner::mouseActionFinish( } else if ((i == _selected.cend()) && !_dragStateItem->isService() && _dragStateItem->isRegular() - && inSelectionMode()) { + && inSelectionMode().inSelectionMode) { if (_selected.size() < MaxSelectedItems) { _selected.emplace(_dragStateItem, FullSelection); repaintItem(_mouseActionItem); @@ -2127,7 +2142,7 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { && !ClickHandler::getPressed() && (_mouseCursorState == CursorState::None || _mouseCursorState == CursorState::Date) - && !inSelectionMode() + && !inSelectionMode().inSelectionMode && !_emptyPainter && e->button() == Qt::LeftButton) { if (const auto view = Element::Moused()) { @@ -3638,17 +3653,46 @@ bool HistoryInner::canDeleteSelected() const { && (selectedState.count == selectedState.canDeleteCount); } -bool HistoryInner::inSelectionMode() const { - if (hasSelectedItems()) { - return true; - } else if (_mouseAction == MouseAction::Selecting - && _dragSelFrom - && _dragSelTo) { - return true; - } else if (_chooseForReportReason.has_value()) { - return true; +HistoryView::SelectionModeResult HistoryInner::inSelectionMode() const { + const auto inSelectionMode = [&] { + if (hasSelectedItems()) { + return true; + } else if (_mouseAction == MouseAction::Selecting + && _dragSelFrom + && _dragSelTo) { + return true; + } else if (_chooseForReportReason.has_value()) { + return true; + } + return false; + }(); + const auto now = inSelectionMode; + if (_lastInSelectionMode != now) { + _lastInSelectionMode = now; + if (_inSelectionModeAnimation.animating()) { + const auto progress = !now + ? _inSelectionModeAnimation.value(0.) + : 1. - _inSelectionModeAnimation.value(0.); + _inSelectionModeAnimation.change( + now ? 1. : 0., + st::universalDuration * (1. - progress)); + } else { + _inSelectionModeAnimation.stop(); + _inSelectionModeAnimation.start( + [this] { + const_cast(this)->update( + QRect( + 0, + _visibleAreaTop, + width(), + _visibleAreaBottom - _visibleAreaTop)); + }, + now ? 0. : 1., + now ? 1. : 0., + st::universalDuration); + } } - return false; + return { now, _inSelectionModeAnimation.value(now ? 1. : 0.) }; } bool HistoryInner::elementIntersectsRange( @@ -3835,7 +3879,7 @@ auto HistoryInner::reactionButtonParameters( || !view->data()->canReact() || _mouseAction == MouseAction::Dragging || _mouseAction == MouseAction::Selecting - || inSelectionMode()) { + || inSelectionMode().inSelectionMode) { return {}; } auto result = view->reactionButtonParameters( @@ -3856,7 +3900,7 @@ void HistoryInner::mouseActionUpdate() { auto mousePos = mapFromGlobal(_mousePosition); auto point = _widget->clampMousePosition(mousePos); - QPoint m; + auto m = QPoint(); adjustCurrent(point.y()); const auto reactionState = _reactionsManager->buttonTextState(point); @@ -3873,6 +3917,10 @@ void HistoryInner::mouseActionUpdate() { ? _curHistory->blocks[_curBlock]->messages[_curItem].get() : nullptr; const auto item = view ? view->data().get() : nullptr; + const auto selectionViewOffset = view + ? QPoint(SelectionViewOffset(this, view), 0) + : QPoint(0, 0); + point -= selectionViewOffset; if (view) { const auto changed = (Element::Moused() != view); if (changed) { @@ -3920,7 +3968,7 @@ void HistoryInner::mouseActionUpdate() { dragState = reactionState; lnkhost = reactionView; } else if (item) { - if (item != _mouseActionItem || (m - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { + if (item != _mouseActionItem || ((m + selectionViewOffset) - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { if (_mouseAction == MouseAction::PrepareDrag) { _mouseAction = MouseAction::Dragging; InvokeQueued(this, [=] { performDrag(); }); diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 554188432..c19c3b8b0 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -30,6 +30,7 @@ namespace HistoryView { class ElementDelegate; class EmojiInteractions; struct TextState; +struct SelectionModeResult; struct StateRequest; enum class CursorState : char; enum class PointState : char; @@ -135,7 +136,7 @@ public: void clearSelected(bool onlyTextSelection = false); [[nodiscard]] MessageIdsList getSelectedItems() const; [[nodiscard]] bool hasSelectedItems() const; - [[nodiscard]] bool inSelectionMode() const; + [[nodiscard]] HistoryView::SelectionModeResult inSelectionMode() const; [[nodiscard]] bool elementIntersectsRange( not_null view, int from, @@ -243,6 +244,10 @@ private: void onTouchSelect(); void onTouchScrollTimer(); + [[nodiscard]] static int SelectionViewOffset( + not_null inner, + not_null view); + using ChosenReaction = HistoryView::Reactions::ChosenReaction; using VideoUserpic = Dialogs::Ui::VideoUserpic; using SelectedItems = std::map>; @@ -508,6 +513,9 @@ private: bool _dragSelecting = false; bool _wasSelectedText = false; // was some text selected in current drag action + mutable bool _lastInSelectionMode = false; + mutable Ui::Animations::Simple _inSelectionModeAnimation; + // scroll by touch support (at least Windows Surface tablets) bool _touchScroll = false; bool _touchSelect = false; diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 42174145c..880b58f75 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/item_text_options.h" #include "ui/painter.h" +#include "ui/rect.h" #include "data/components/sponsored_messages.h" #include "data/data_session.h" #include "data/data_forum.h" @@ -110,8 +111,8 @@ bool DefaultElementDelegate::elementUnderCursor( return false; } -bool DefaultElementDelegate::elementInSelectionMode() { - return false; +SelectionModeResult DefaultElementDelegate::elementInSelectionMode() { + return {}; } bool DefaultElementDelegate::elementIntersectsRange( @@ -740,6 +741,25 @@ not_null Element::enforcePurchasedTag() { return Get(); } +int Element::AdditionalSpaceForSelectionCheckbox( + not_null view, + QRect countedGeometry) { + if (!view->hasOutLayout() || view->delegate()->elementIsChatWide()) { + return 0; + } + if (countedGeometry.isEmpty()) { + countedGeometry = view->innerGeometry(); + } + const auto diff = view->width() + - (countedGeometry.x() + countedGeometry.width()) + - st::msgPadding.right() + - st::msgSelectionOffset + - view->rightActionSize().value_or(QSize()).width(); + return (diff < 0) + ? -(std::min(st::msgSelectionOffset, -diff)) + : 0; +} + void Element::refreshMedia(Element *replacing) { if (_flags & Flag::MediaOverriden) { return; diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index d1ef6c282..be41e07dc 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -70,12 +70,17 @@ enum class OnlyEmojiAndSpaces : char { No, }; +struct SelectionModeResult { + bool inSelectionMode = false; + float64 progress = 0.0; +}; + class Element; class ElementDelegate { public: virtual Context elementContext() = 0; virtual bool elementUnderCursor(not_null view) = 0; - virtual bool elementInSelectionMode() = 0; + virtual SelectionModeResult elementInSelectionMode() = 0; virtual bool elementIntersectsRange( not_null view, int from, @@ -131,7 +136,7 @@ public: class DefaultElementDelegate : public ElementDelegate { public: bool elementUnderCursor(not_null view) override; - bool elementInSelectionMode() override; + SelectionModeResult elementInSelectionMode() override; bool elementIntersectsRange( not_null view, int from, @@ -571,6 +576,10 @@ public: [[nodiscard]] not_null enforcePurchasedTag(); + [[nodiscard]] static int AdditionalSpaceForSelectionCheckbox( + not_null view, + QRect countedGeometry = QRect()); + virtual bool consumeHorizontalScroll(QPoint position, int delta) { return false; } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index cef7612e9..d300453f8 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1229,8 +1229,34 @@ bool ListWidget::hasSelectedItems() const { return !_selected.empty(); } -bool ListWidget::inSelectionMode() const { - return hasSelectedItems() || !_dragSelected.empty(); +SelectionModeResult ListWidget::inSelectionMode() const { + const auto now = hasSelectedItems() || !_dragSelected.empty(); + if (_lastInSelectionMode != now) { + _lastInSelectionMode = now; + if (_inSelectionModeAnimation.animating()) { + const auto progress = !now + ? _inSelectionModeAnimation.value(0.) + : 1. - _inSelectionModeAnimation.value(0.); + _inSelectionModeAnimation.change( + now ? 1. : 0., + st::universalDuration * (1. - progress)); + } else { + _inSelectionModeAnimation.stop(); + _inSelectionModeAnimation.start( + [this] { + const_cast(this)->update( + QRect( + 0, + _visibleTop, + width(), + _visibleBottom - _visibleTop)); + }, + now ? 0. : 1., + now ? 1. : 0., + st::universalDuration); + } + } + return { now, _inSelectionModeAnimation.value(now ? 1. : 0.) }; } bool ListWidget::overSelectedItems() const { @@ -1751,7 +1777,7 @@ bool ListWidget::elementUnderCursor( return (_overElement == view); } -bool ListWidget::elementInSelectionMode() { +SelectionModeResult ListWidget::elementInSelectionMode() { return inSelectionMode(); } @@ -3410,7 +3436,7 @@ Reactions::ButtonParameters ListWidget::reactionButtonParameters( if (top < 0 || !view->data()->canReact() || _mouseAction == MouseAction::Dragging - || inSelectionMode()) { + || inSelectionMode().inSelectionMode) { return {}; } auto result = view->reactionButtonParameters( @@ -3536,6 +3562,19 @@ ClickHandlerContext ListWidget::prepareClickHandlerContext(FullMsgId id) { }; } +int ListWidget::SelectionViewOffset( + not_null inner, + not_null view) { + if (inner->_lastInSelectionMode) { + const auto translation + = Element::AdditionalSpaceForSelectionCheckbox(view); + const auto progress = inner->_inSelectionModeAnimation.value(1.); + return translation * progress; + } + return 0; +} + + void ListWidget::mouseActionUpdate() { auto mousePosition = mapFromGlobal(_mousePosition); auto point = QPoint( @@ -3551,6 +3590,9 @@ void ListWidget::mouseActionUpdate() { ? reactionView : strictFindItemByY(point.y()); const auto item = view ? view->data().get() : nullptr; + if (view) { + point -= QPoint(SelectionViewOffset(this, view), 0); + } const auto itemPoint = mapPointToItem(point, view); _overState = MouseState( item ? item->fullId() : FullMsgId(), diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 6be426406..02ec9a370 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -389,7 +389,7 @@ public: // ElementDelegate interface. Context elementContext() override; bool elementUnderCursor(not_null view) override; - bool elementInSelectionMode() override; + SelectionModeResult elementInSelectionMode() override; bool elementIntersectsRange( not_null view, int from, @@ -459,6 +459,10 @@ protected: int resizeGetHeight(int newWidth) override; private: + [[nodiscard]] static int SelectionViewOffset( + not_null inner, + not_null view); + using ScrollTopState = ListMemento::ScrollTopState; using PointState = HistoryView::PointState; using CursorState = HistoryView::CursorState; @@ -618,7 +622,7 @@ private: const SelectedMap::const_iterator &i); bool hasSelectedText() const; bool hasSelectedItems() const; - bool inSelectionMode() const; + SelectionModeResult inSelectionMode() const; bool overSelectedItems() const; void clearTextSelection(); void clearSelected(); @@ -830,6 +834,9 @@ private: ElementHighlighter _highlighter; + mutable bool _lastInSelectionMode = false; + mutable Ui::Animations::Simple _inSelectionModeAnimation; + // scroll by touch support (at least Windows Surface tablets) bool _touchScroll = false; bool _touchSelect = false; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 25a579507..7c189084e 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -1100,6 +1100,14 @@ void Message::draw(Painter &p, const PaintContext &context) const { if (hasGesture) { p.translate(context.gestureHorizontal.translation, 0); } + const auto selectionModeResult = delegate()->elementInSelectionMode(); + const auto selectionTranslation = (selectionModeResult.progress > 0) + ? (selectionModeResult.progress + * AdditionalSpaceForSelectionCheckbox(this, g)) + : 0; + if (selectionTranslation) { + p.translate(selectionTranslation, 0); + } if (item->hasUnrequestedFactcheck()) { item->history()->session().factchecks().requestFor(item); @@ -1449,7 +1457,14 @@ void Message::draw(Painter &p, const PaintContext &context) const { const auto fastShareTop = data()->isSponsored() ? g.top() + fastShareSkip : g.top() + g.height() - fastShareSkip - size->height(); + const auto o = p.opacity(); + if (selectionModeResult.progress > 0) { + p.setOpacity(1. - selectionModeResult.progress); + } drawRightAction(p, context, fastShareLeft, fastShareTop, width()); + if (selectionModeResult.progress > 0) { + p.setOpacity(o); + } } if (media) { @@ -1570,6 +1585,48 @@ void Message::draw(Painter &p, const PaintContext &context) const { } p.restore(); } + if (selectionTranslation) { + p.translate(-selectionTranslation, 0); + } + if (selectionModeResult.progress) { + const auto progress = selectionModeResult.progress; + if (progress <= 1.) { + if (context.selected()) { + if (!_selectionRoundCheckbox) { + _selectionRoundCheckbox + = std::make_unique( + st::msgSelectionCheck, + [this] { repaint(); }); + } + } + if (_selectionRoundCheckbox) { + _selectionRoundCheckbox->setChecked( + context.selected(), + anim::type::normal); + } + const auto o = ScopedPainterOpacity(p, progress); + const auto &st = st::msgSelectionCheck; + const auto pos = QPoint( + (width() + - (st::msgSelectionOffset * progress - st.size) / 2 + - st::msgPadding.right() / 2 + - st.size), + g.y() + (g.height() - st.size) / 2); + { + p.setPen(QPen(st.border, st.width)); + p.setBrush(context.st->msgServiceBg()); + auto hq = PainterHighQualityEnabler(p); + p.drawEllipse(QRect(pos, Size(st.size))); + } + if (_selectionRoundCheckbox) { + _selectionRoundCheckbox->paint(p, pos.x(), pos.y(), width()); + } + } else { + _selectionRoundCheckbox = nullptr; + } + } else { + _selectionRoundCheckbox = nullptr; + } } void Message::paintCommentsButton( @@ -2959,7 +3016,9 @@ bool Message::getStateText( void Message::updatePressed(QPoint point) { const auto item = data(); const auto media = this->media(); - if (!media) return; + if (!media) { + return; + } auto g = countGeometry(); auto keyboard = item->inlineReplyKeyboard(); @@ -3792,7 +3851,7 @@ bool Message::displayFastReply() const { return hasFastReply() && data()->isRegular() && canSendAnything() - && !delegate()->elementInSelectionMode(); + && !delegate()->elementInSelectionMode().inSelectionMode; } bool Message::displayRightActionComments() const { @@ -3956,6 +4015,9 @@ void Message::drawRightAction( ClickHandlerPtr Message::rightActionLink( std::optional pressPoint) const { + if (delegate()->elementInSelectionMode().progress > 0) { + return nullptr; + } ensureRightAction(); if (!_rightAction->link) { _rightAction->link = prepareRightActionLink(); diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index af97b8b95..dc35277df 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -22,6 +22,7 @@ struct ReactionId; namespace Ui { struct BubbleRounding; +class RoundCheckbox; } // namespace Ui namespace HistoryView { @@ -313,6 +314,7 @@ private: mutable Ui::Text::String _fromName; mutable std::unique_ptr _fromNameStatus; + mutable std::unique_ptr _selectionRoundCheckbox; Ui::Text::String _rightBadge; mutable int _fromNameVersion = 0; uint32 _bubbleWidthLimit : 28 = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 1ecf43c29..1ae6f36c5 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -898,7 +898,7 @@ void RepliesWidget::setupSwipeReply() { } }, [=, show = controller()->uiShow()](int cursorTop) { auto result = HistoryView::SwipeHandlerFinishData(); - if (_inner->elementInSelectionMode()) { + if (_inner->elementInSelectionMode().inSelectionMode) { return result; } const auto view = _inner->lookupItemByY(cursorTop); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 3369b5fb2..cb30fdf47 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -26,6 +26,7 @@ msgPadding: margins(11px, 8px, 11px, 8px); msgMargin: margins(16px, 6px, 56px, 2px); msgMarginTopAttached: 0px; msgShadow: 2px; +msgSelectionOffset: 30px; historyReplyTop: 2px; historyReplyBottom: 2px; @@ -1149,3 +1150,7 @@ factcheckField: InputField(defaultInputField) { style: defaultTextStyle; } purchasedTagPadding: margins(3px, 2px, 6px, 2px); + +msgSelectionCheck: RoundCheckbox(defaultPeerListCheck) { + bgActive: boxTextFgGood; +}