mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Support nice Reply button in the corner.
This commit is contained in:
@@ -957,6 +957,8 @@ PRIVATE
|
|||||||
history/view/history_view_reaction_preview.h
|
history/view/history_view_reaction_preview.h
|
||||||
history/view/history_view_reply.cpp
|
history/view/history_view_reply.cpp
|
||||||
history/view/history_view_reply.h
|
history/view/history_view_reply.h
|
||||||
|
history/view/history_view_reply_button.cpp
|
||||||
|
history/view/history_view_reply_button.h
|
||||||
history/view/history_view_requests_bar.cpp
|
history/view/history_view_requests_bar.cpp
|
||||||
history/view/history_view_requests_bar.h
|
history/view/history_view_requests_bar.h
|
||||||
history/view/history_view_schedule_box.cpp
|
history/view/history_view_schedule_box.cpp
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
@@ -872,10 +873,17 @@ void EditRestrictedBox::prepare() {
|
|||||||
restrictWeak->closeBox();
|
restrictWeak->closeBox();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const auto savedUser = user();
|
||||||
|
const auto savedPeer = peer();
|
||||||
const auto done = [=](
|
const auto done = [=](
|
||||||
ChatAdminRightsInfo,
|
ChatAdminRightsInfo newRights,
|
||||||
const QString &) {
|
const QString &rank) {
|
||||||
closeBoth();
|
closeBoth();
|
||||||
|
savedUser->session().changes().chatAdminChanged(
|
||||||
|
savedPeer,
|
||||||
|
savedUser,
|
||||||
|
newRights.flags,
|
||||||
|
rank);
|
||||||
};
|
};
|
||||||
const auto fail = closeBoth;
|
const auto fail = closeBoth;
|
||||||
adminBox->setSaveCallback(
|
adminBox->setSaveCallback(
|
||||||
|
|||||||
@@ -2013,6 +2013,11 @@ void ParticipantsBoxController::editAdminDone(
|
|||||||
if (_editParticipantBox) {
|
if (_editParticipantBox) {
|
||||||
_editParticipantBox->closeBox();
|
_editParticipantBox->closeBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_additional.applyAdminLocally(user, rights, rank);
|
||||||
|
recomputeTypeFor(user);
|
||||||
|
refreshRows();
|
||||||
|
|
||||||
const auto flags = rights.flags;
|
const auto flags = rights.flags;
|
||||||
user->session().changes().chatAdminChanged(_peer, user, flags, rank);
|
user->session().changes().chatAdminChanged(_peer, user, flags, rank);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ ResolveWindow ResolveWindowDefault() {
|
|||||||
return [](not_null<Main::Session*> session)
|
return [](not_null<Main::Session*> session)
|
||||||
-> Window::SessionController* {
|
-> Window::SessionController* {
|
||||||
const auto check = [&](Window::Controller *window) {
|
const auto check = [&](Window::Controller *window) {
|
||||||
if (const auto controller = window->sessionController()) {
|
const auto controller = window
|
||||||
if (&controller->session() == session) {
|
? window->sessionController()
|
||||||
return controller;
|
: nullptr;
|
||||||
}
|
return (controller && (&controller->session() == session))
|
||||||
}
|
? controller
|
||||||
return (Window::SessionController*)nullptr;
|
: nullptr;
|
||||||
};
|
};
|
||||||
auto &app = Core::App();
|
auto &app = Core::App();
|
||||||
const auto account = not_null(&session->account());
|
const auto account = not_null(&session->account());
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
#include "history/view/history_view_service_message.h"
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
#include "history/view/history_view_reply_button.h"
|
||||||
#include "history/view/history_view_context_menu.h"
|
#include "history/view/history_view_context_menu.h"
|
||||||
#include "history/view/history_view_reaction_preview.h"
|
#include "history/view/history_view_reaction_preview.h"
|
||||||
#include "history/view/history_view_quick_action.h"
|
#include "history/view/history_view_quick_action.h"
|
||||||
@@ -331,6 +332,9 @@ HistoryInner::HistoryInner(
|
|||||||
std::make_unique<HistoryView::Reactions::Manager>(
|
std::make_unique<HistoryView::Reactions::Manager>(
|
||||||
this,
|
this,
|
||||||
[=](QRect updated) { update(updated); }))
|
[=](QRect updated) { update(updated); }))
|
||||||
|
, _replyButtonManager(
|
||||||
|
std::make_unique<HistoryView::ReplyButton::Manager>(
|
||||||
|
[=](QRect updated) { update(updated); }))
|
||||||
, _touchSelectTimer([=] { onTouchSelect(); })
|
, _touchSelectTimer([=] { onTouchSelect(); })
|
||||||
, _touchScrollTimer([=] { onTouchScrollTimer(); })
|
, _touchScrollTimer([=] { onTouchScrollTimer(); })
|
||||||
, _middleClickAutoscroll(
|
, _middleClickAutoscroll(
|
||||||
@@ -1475,6 +1479,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_replyButtonManager->paint(p, context);
|
||||||
_reactionsManager->paint(p, context);
|
_reactionsManager->paint(p, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2086,6 +2091,7 @@ void HistoryInner::performDrag() {
|
|||||||
if (auto mimeData = prepareDrag()) {
|
if (auto mimeData = prepareDrag()) {
|
||||||
// This call enters event loop and can destroy any QObject.
|
// This call enters event loop and can destroy any QObject.
|
||||||
_reactionsManager->updateButton({});
|
_reactionsManager->updateButton({});
|
||||||
|
_replyButtonManager->updateButton({});
|
||||||
_controller->widget()->launchDrag(
|
_controller->widget()->launchDrag(
|
||||||
std::move(mimeData),
|
std::move(mimeData),
|
||||||
crl::guard(this, [=] { mouseActionUpdate(QCursor::pos()); }));
|
crl::guard(this, [=] { mouseActionUpdate(QCursor::pos()); }));
|
||||||
@@ -2104,6 +2110,7 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
|
|||||||
}
|
}
|
||||||
_animatedStickersPlayed.remove(item);
|
_animatedStickersPlayed.remove(item);
|
||||||
_reactionsManager->remove(item->fullId());
|
_reactionsManager->remove(item->fullId());
|
||||||
|
_replyButtonManager->remove(item->fullId());
|
||||||
|
|
||||||
auto i = _selected.find(item);
|
auto i = _selected.find(item);
|
||||||
if (i != _selected.cend()) {
|
if (i != _selected.cend()) {
|
||||||
@@ -3937,6 +3944,7 @@ void HistoryInner::enterEventHook(QEnterEvent *e) {
|
|||||||
|
|
||||||
void HistoryInner::leaveEventHook(QEvent *e) {
|
void HistoryInner::leaveEventHook(QEvent *e) {
|
||||||
_reactionsManager->updateButton({ .cursorLeft = true });
|
_reactionsManager->updateButton({ .cursorLeft = true });
|
||||||
|
_replyButtonManager->updateButton({});
|
||||||
if (auto item = Element::Hovered()) {
|
if (auto item = Element::Hovered()) {
|
||||||
repaintItem(item);
|
repaintItem(item);
|
||||||
Element::Hovered(nullptr);
|
Element::Hovered(nullptr);
|
||||||
@@ -4293,7 +4301,28 @@ auto HistoryInner::reactionButtonParameters(
|
|||||||
auto result = view->reactionButtonParameters(
|
auto result = view->reactionButtonParameters(
|
||||||
position,
|
position,
|
||||||
reactionState
|
reactionState
|
||||||
).translated({ 0, itemTop(view) });
|
).translated({ 0, top });
|
||||||
|
result.visibleTop = _visibleAreaTop;
|
||||||
|
result.visibleBottom = _visibleAreaBottom;
|
||||||
|
result.globalPointer = _mousePosition;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto HistoryInner::replyButtonParameters(
|
||||||
|
not_null<const Element*> view,
|
||||||
|
QPoint position,
|
||||||
|
const HistoryView::TextState &replyState) const
|
||||||
|
-> HistoryView::ReplyButton::ButtonParameters {
|
||||||
|
const auto top = itemTop(view);
|
||||||
|
if (top < 0
|
||||||
|
|| _mouseAction == MouseAction::Dragging
|
||||||
|
|| inSelectionMode().inSelectionMode) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto result = view->replyButtonParameters(
|
||||||
|
position,
|
||||||
|
replyState
|
||||||
|
).translated({ 0, top });
|
||||||
result.visibleTop = _visibleAreaTop;
|
result.visibleTop = _visibleAreaTop;
|
||||||
result.visibleBottom = _visibleAreaBottom;
|
result.visibleBottom = _visibleAreaBottom;
|
||||||
result.globalPointer = _mousePosition;
|
result.globalPointer = _mousePosition;
|
||||||
@@ -4315,8 +4344,15 @@ void HistoryInner::mouseActionUpdate() {
|
|||||||
const auto reactionState = _reactionsManager->buttonTextState(point);
|
const auto reactionState = _reactionsManager->buttonTextState(point);
|
||||||
const auto reactionItem = session().data().message(reactionState.itemId);
|
const auto reactionItem = session().data().message(reactionState.itemId);
|
||||||
const auto reactionView = viewByItem(reactionItem);
|
const auto reactionView = viewByItem(reactionItem);
|
||||||
|
const auto replyBtnState = reactionView
|
||||||
|
? HistoryView::TextState()
|
||||||
|
: _replyButtonManager->buttonTextState(point);
|
||||||
|
const auto replyBtnItem = session().data().message(replyBtnState.itemId);
|
||||||
|
const auto replyBtnView = viewByItem(replyBtnItem);
|
||||||
const auto view = reactionView
|
const auto view = reactionView
|
||||||
? reactionView
|
? reactionView
|
||||||
|
: replyBtnView
|
||||||
|
? replyBtnView
|
||||||
: (_aboutView
|
: (_aboutView
|
||||||
&& _aboutView->view()
|
&& _aboutView->view()
|
||||||
&& point.y() >= _aboutView->top
|
&& point.y() >= _aboutView->top
|
||||||
@@ -4342,6 +4378,10 @@ void HistoryInner::mouseActionUpdate() {
|
|||||||
view,
|
view,
|
||||||
m,
|
m,
|
||||||
reactionState));
|
reactionState));
|
||||||
|
_replyButtonManager->updateButton(replyButtonParameters(
|
||||||
|
view,
|
||||||
|
m,
|
||||||
|
replyBtnState));
|
||||||
if (changed) {
|
if (changed) {
|
||||||
_reactionsItem = item;
|
_reactionsItem = item;
|
||||||
}
|
}
|
||||||
@@ -4361,6 +4401,7 @@ void HistoryInner::mouseActionUpdate() {
|
|||||||
Element::Moused(nullptr);
|
Element::Moused(nullptr);
|
||||||
}
|
}
|
||||||
_reactionsManager->updateButton({});
|
_reactionsManager->updateButton({});
|
||||||
|
_replyButtonManager->updateButton({});
|
||||||
}
|
}
|
||||||
if (_mouseActionItem && !viewByItem(_mouseActionItem)) {
|
if (_mouseActionItem && !viewByItem(_mouseActionItem)) {
|
||||||
mouseActionCancel();
|
mouseActionCancel();
|
||||||
@@ -4374,9 +4415,13 @@ void HistoryInner::mouseActionUpdate() {
|
|||||||
&& !_selected.empty()
|
&& !_selected.empty()
|
||||||
&& (_selected.cbegin()->second != FullSelection);
|
&& (_selected.cbegin()->second != FullSelection);
|
||||||
const auto overReaction = reactionView && reactionState.link;
|
const auto overReaction = reactionView && reactionState.link;
|
||||||
|
const auto overReplyBtn = replyBtnView && replyBtnState.link;
|
||||||
if (overReaction) {
|
if (overReaction) {
|
||||||
dragState = reactionState;
|
dragState = reactionState;
|
||||||
lnkhost = reactionView;
|
lnkhost = reactionView;
|
||||||
|
} else if (overReplyBtn) {
|
||||||
|
dragState = replyBtnState;
|
||||||
|
lnkhost = replyBtnView;
|
||||||
} else if (item) {
|
} else if (item) {
|
||||||
if (item != _mouseActionItem || ((m + selectionViewOffset) - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
|
if (item != _mouseActionItem || ((m + selectionViewOffset) - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
|
||||||
if (_mouseAction == MouseAction::PrepareDrag) {
|
if (_mouseAction == MouseAction::PrepareDrag) {
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ struct ChosenReaction;
|
|||||||
struct ButtonParameters;
|
struct ButtonParameters;
|
||||||
} // namespace HistoryView::Reactions
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
|
namespace HistoryView::ReplyButton {
|
||||||
|
class Manager;
|
||||||
|
struct ButtonParameters;
|
||||||
|
} // namespace HistoryView::ReplyButton
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class SessionController;
|
class SessionController;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
@@ -439,6 +444,11 @@ private:
|
|||||||
QPoint position,
|
QPoint position,
|
||||||
const HistoryView::TextState &reactionState) const
|
const HistoryView::TextState &reactionState) const
|
||||||
-> HistoryView::Reactions::ButtonParameters;
|
-> HistoryView::Reactions::ButtonParameters;
|
||||||
|
[[nodiscard]] auto replyButtonParameters(
|
||||||
|
not_null<const Element*> view,
|
||||||
|
QPoint position,
|
||||||
|
const HistoryView::TextState &replyState) const
|
||||||
|
-> HistoryView::ReplyButton::ButtonParameters;
|
||||||
void toggleFavoriteReaction(not_null<Element*> view) const;
|
void toggleFavoriteReaction(not_null<Element*> view) const;
|
||||||
void reactionChosen(const ChosenReaction &reaction);
|
void reactionChosen(const ChosenReaction &reaction);
|
||||||
|
|
||||||
@@ -512,6 +522,7 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<HistoryView::Reactions::Manager> _reactionsManager;
|
std::unique_ptr<HistoryView::Reactions::Manager> _reactionsManager;
|
||||||
rpl::variable<HistoryItem*> _reactionsItem;
|
rpl::variable<HistoryItem*> _reactionsItem;
|
||||||
|
std::unique_ptr<HistoryView::ReplyButton::Manager> _replyButtonManager;
|
||||||
HistoryItem *_pinnedItem = nullptr;
|
HistoryItem *_pinnedItem = nullptr;
|
||||||
|
|
||||||
MouseAction _mouseAction = MouseAction::None;
|
MouseAction _mouseAction = MouseAction::None;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "history/view/media/history_view_no_forwards_request.h"
|
#include "history/view/media/history_view_no_forwards_request.h"
|
||||||
#include "history/view/media/history_view_suggest_decision.h"
|
#include "history/view/media/history_view_suggest_decision.h"
|
||||||
#include "history/view/reactions/history_view_reactions_button.h"
|
#include "history/view/reactions/history_view_reactions_button.h"
|
||||||
|
#include "history/view/history_view_reply_button.h"
|
||||||
#include "history/view/reactions/history_view_reactions.h"
|
#include "history/view/reactions/history_view_reactions.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/history_view_reply.h"
|
#include "history/view/history_view_reply.h"
|
||||||
@@ -2800,6 +2801,12 @@ Reactions::ButtonParameters Element::reactionButtonParameters(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReplyButton::ButtonParameters Element::replyButtonParameters(
|
||||||
|
QPoint position,
|
||||||
|
const TextState &replyState) const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
int Element::reactionsOptimalWidth() const {
|
int Element::reactionsOptimalWidth() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ struct ButtonParameters;
|
|||||||
class InlineList;
|
class InlineList;
|
||||||
} // namespace HistoryView::Reactions
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
|
namespace HistoryView::ReplyButton {
|
||||||
|
struct ButtonParameters;
|
||||||
|
} // namespace HistoryView::ReplyButton
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
using PaintContext = Ui::ChatPaintContext;
|
using PaintContext = Ui::ChatPaintContext;
|
||||||
@@ -542,6 +546,9 @@ public:
|
|||||||
[[nodiscard]] virtual auto reactionButtonParameters(
|
[[nodiscard]] virtual auto reactionButtonParameters(
|
||||||
QPoint position,
|
QPoint position,
|
||||||
const TextState &reactionState) const -> Reactions::ButtonParameters;
|
const TextState &reactionState) const -> Reactions::ButtonParameters;
|
||||||
|
[[nodiscard]] virtual auto replyButtonParameters(
|
||||||
|
QPoint position,
|
||||||
|
const TextState &replyState) const -> ReplyButton::ButtonParameters;
|
||||||
[[nodiscard]] virtual int reactionsOptimalWidth() const;
|
[[nodiscard]] virtual int reactionsOptimalWidth() const;
|
||||||
|
|
||||||
// ClickHandlerHost interface.
|
// ClickHandlerHost interface.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "history/view/media/history_view_sticker.h"
|
#include "history/view/media/history_view_sticker.h"
|
||||||
#include "history/view/reactions/history_view_reactions.h"
|
#include "history/view/reactions/history_view_reactions.h"
|
||||||
#include "history/view/reactions/history_view_reactions_button.h"
|
#include "history/view/reactions/history_view_reactions_button.h"
|
||||||
|
#include "history/view/history_view_reply_button.h"
|
||||||
#include "history/view/reactions/history_view_reactions_selector.h"
|
#include "history/view/reactions/history_view_reactions_selector.h"
|
||||||
#include "history/view/history_view_context_menu.h"
|
#include "history/view/history_view_context_menu.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
@@ -405,6 +406,8 @@ ListWidget::ListWidget(
|
|||||||
, _reactionsManager(_delegate->listMakeReactionsManager(
|
, _reactionsManager(_delegate->listMakeReactionsManager(
|
||||||
this,
|
this,
|
||||||
[=](QRect updated) { update(updated); }))
|
[=](QRect updated) { update(updated); }))
|
||||||
|
, _replyButtonManager(std::make_unique<ReplyButton::Manager>(
|
||||||
|
[=](QRect updated) { update(updated); }))
|
||||||
, _translateTracker(MaybeTranslateTracker(_delegate->listTranslateHistory()))
|
, _translateTracker(MaybeTranslateTracker(_delegate->listTranslateHistory()))
|
||||||
, _scrollDateCheck([this] { scrollDateCheck(); })
|
, _scrollDateCheck([this] { scrollDateCheck(); })
|
||||||
, _applyUpdatedScrollState([this] { applyUpdatedScrollState(); })
|
, _applyUpdatedScrollState([this] { applyUpdatedScrollState(); })
|
||||||
@@ -2384,6 +2387,9 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||||||
paintUserpics(p, context, clip);
|
paintUserpics(p, context, clip);
|
||||||
paintDates(p, context, clip);
|
paintDates(p, context, clip);
|
||||||
|
|
||||||
|
if (_replyButtonManager) {
|
||||||
|
_replyButtonManager->paint(p, context);
|
||||||
|
}
|
||||||
if (_reactionsManager) {
|
if (_reactionsManager) {
|
||||||
_reactionsManager->paint(p, context);
|
_reactionsManager->paint(p, context);
|
||||||
}
|
}
|
||||||
@@ -3298,6 +3304,9 @@ void ListWidget::leaveEventHook(QEvent *e) {
|
|||||||
if (_reactionsManager) {
|
if (_reactionsManager) {
|
||||||
_reactionsManager->updateButton({ .cursorLeft = true });
|
_reactionsManager->updateButton({ .cursorLeft = true });
|
||||||
}
|
}
|
||||||
|
if (_replyButtonManager) {
|
||||||
|
_replyButtonManager->updateButton({});
|
||||||
|
}
|
||||||
if (const auto view = _overElement) {
|
if (const auto view = _overElement) {
|
||||||
if (_overState.pointState != PointState::Outside) {
|
if (_overState.pointState != PointState::Outside) {
|
||||||
repaintItem(view);
|
repaintItem(view);
|
||||||
@@ -3604,7 +3613,27 @@ Reactions::ButtonParameters ListWidget::reactionButtonParameters(
|
|||||||
auto result = view->reactionButtonParameters(
|
auto result = view->reactionButtonParameters(
|
||||||
position,
|
position,
|
||||||
reactionState
|
reactionState
|
||||||
).translated({ 0, itemTop(view) });
|
).translated({ 0, top });
|
||||||
|
result.visibleTop = _visibleTop;
|
||||||
|
result.visibleBottom = _visibleBottom;
|
||||||
|
result.globalPointer = _mousePosition;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplyButton::ButtonParameters ListWidget::replyButtonParameters(
|
||||||
|
not_null<const Element*> view,
|
||||||
|
QPoint position,
|
||||||
|
const TextState &replyState) const {
|
||||||
|
const auto top = itemTop(view);
|
||||||
|
if (top < 0
|
||||||
|
|| _mouseAction == MouseAction::Dragging
|
||||||
|
|| inSelectionMode().inSelectionMode) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto result = view->replyButtonParameters(
|
||||||
|
position,
|
||||||
|
replyState
|
||||||
|
).translated({ 0, top });
|
||||||
result.visibleTop = _visibleTop;
|
result.visibleTop = _visibleTop;
|
||||||
result.visibleBottom = _visibleBottom;
|
result.visibleBottom = _visibleBottom;
|
||||||
result.globalPointer = _mousePosition;
|
result.globalPointer = _mousePosition;
|
||||||
@@ -3757,8 +3786,15 @@ void ListWidget::mouseActionUpdate() {
|
|||||||
: TextState();
|
: TextState();
|
||||||
const auto reactionItem = session().data().message(reactionState.itemId);
|
const auto reactionItem = session().data().message(reactionState.itemId);
|
||||||
const auto reactionView = viewForItem(reactionItem);
|
const auto reactionView = viewForItem(reactionItem);
|
||||||
|
const auto replyBtnState = (_replyButtonManager && !reactionView)
|
||||||
|
? _replyButtonManager->buttonTextState(point)
|
||||||
|
: TextState();
|
||||||
|
const auto replyBtnItem = session().data().message(replyBtnState.itemId);
|
||||||
|
const auto replyBtnView = viewForItem(replyBtnItem);
|
||||||
const auto view = reactionView
|
const auto view = reactionView
|
||||||
? reactionView
|
? reactionView
|
||||||
|
: replyBtnView
|
||||||
|
? replyBtnView
|
||||||
: strictFindItemByY(point.y());
|
: strictFindItemByY(point.y());
|
||||||
const auto item = view ? view->data().get() : nullptr;
|
const auto item = view ? view->data().get() : nullptr;
|
||||||
if (view) {
|
if (view) {
|
||||||
@@ -3785,6 +3821,11 @@ void ListWidget::mouseActionUpdate() {
|
|||||||
reactionState)
|
reactionState)
|
||||||
: Reactions::ButtonParameters());
|
: Reactions::ButtonParameters());
|
||||||
}
|
}
|
||||||
|
if (_replyButtonManager) {
|
||||||
|
_replyButtonManager->updateButton(view
|
||||||
|
? replyButtonParameters(view, itemPoint, replyBtnState)
|
||||||
|
: ReplyButton::ButtonParameters());
|
||||||
|
}
|
||||||
if (viewChanged && view) {
|
if (viewChanged && view) {
|
||||||
_reactionsItem = item;
|
_reactionsItem = item;
|
||||||
}
|
}
|
||||||
@@ -3795,10 +3836,14 @@ void ListWidget::mouseActionUpdate() {
|
|||||||
&& (_overState.itemId == _pressState.itemId)
|
&& (_overState.itemId == _pressState.itemId)
|
||||||
&& hasSelectedText();
|
&& hasSelectedText();
|
||||||
auto dragStateUserpic = false;
|
auto dragStateUserpic = false;
|
||||||
|
const auto overReplyBtn = replyBtnView && replyBtnState.link;
|
||||||
const auto overReaction = reactionView && reactionState.link;
|
const auto overReaction = reactionView && reactionState.link;
|
||||||
if (overReaction) {
|
if (overReaction) {
|
||||||
dragState = reactionState;
|
dragState = reactionState;
|
||||||
lnkhost = reactionView;
|
lnkhost = reactionView;
|
||||||
|
} else if (overReplyBtn) {
|
||||||
|
dragState = replyBtnState;
|
||||||
|
lnkhost = replyBtnView;
|
||||||
} else if (view) {
|
} else if (view) {
|
||||||
auto cursorDeltaLength = [&] {
|
auto cursorDeltaLength = [&] {
|
||||||
auto cursorDelta = (_overState.point - _pressState.point);
|
auto cursorDelta = (_overState.point - _pressState.point);
|
||||||
@@ -4301,6 +4346,9 @@ void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
|||||||
if (_reactionsManager) {
|
if (_reactionsManager) {
|
||||||
_reactionsManager->remove(item->fullId());
|
_reactionsManager->remove(item->fullId());
|
||||||
}
|
}
|
||||||
|
if (_replyButtonManager) {
|
||||||
|
_replyButtonManager->remove(item->fullId());
|
||||||
|
}
|
||||||
updateItemsGeometry();
|
updateItemsGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ struct ChosenReaction;
|
|||||||
struct ButtonParameters;
|
struct ButtonParameters;
|
||||||
} // namespace HistoryView::Reactions
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
|
namespace HistoryView::ReplyButton {
|
||||||
|
class Manager;
|
||||||
|
struct ButtonParameters;
|
||||||
|
} // namespace HistoryView::ReplyButton
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
struct SectionShow;
|
struct SectionShow;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
@@ -388,6 +393,10 @@ public:
|
|||||||
not_null<const Element*> view,
|
not_null<const Element*> view,
|
||||||
QPoint position,
|
QPoint position,
|
||||||
const TextState &reactionState) const;
|
const TextState &reactionState) const;
|
||||||
|
[[nodiscard]] ReplyButton::ButtonParameters replyButtonParameters(
|
||||||
|
not_null<const Element*> view,
|
||||||
|
QPoint position,
|
||||||
|
const TextState &replyState) const;
|
||||||
void toggleFavoriteReaction(not_null<Element*> view) const;
|
void toggleFavoriteReaction(not_null<Element*> view) const;
|
||||||
|
|
||||||
|
|
||||||
@@ -781,6 +790,8 @@ private:
|
|||||||
rpl::variable<HistoryItem*> _reactionsItem;
|
rpl::variable<HistoryItem*> _reactionsItem;
|
||||||
bool _useCornerReaction = false;
|
bool _useCornerReaction = false;
|
||||||
|
|
||||||
|
std::unique_ptr<ReplyButton::Manager> _replyButtonManager;
|
||||||
|
|
||||||
std::unique_ptr<TranslateTracker> _translateTracker;
|
std::unique_ptr<TranslateTracker> _translateTracker;
|
||||||
|
|
||||||
int _minHeight = 0;
|
int _minHeight = 0;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "history/view/media/history_view_suggest_decision.h"
|
#include "history/view/media/history_view_suggest_decision.h"
|
||||||
#include "history/view/reactions/history_view_reactions.h"
|
#include "history/view/reactions/history_view_reactions.h"
|
||||||
#include "history/view/reactions/history_view_reactions_button.h"
|
#include "history/view/reactions/history_view_reactions_button.h"
|
||||||
|
#include "history/view/history_view_reply_button.h"
|
||||||
#include "history/view/history_view_group_call_bar.h" // UserpicInRow.
|
#include "history/view/history_view_group_call_bar.h" // UserpicInRow.
|
||||||
#include "history/view/history_view_reply.h"
|
#include "history/view/history_view_reply.h"
|
||||||
#include "history/view/history_view_transcribe_button.h"
|
#include "history/view/history_view_transcribe_button.h"
|
||||||
@@ -63,19 +64,6 @@ constexpr auto kSummarizeThreshold = 512;
|
|||||||
constexpr auto kPlayStatusLimit = 2;
|
constexpr auto kPlayStatusLimit = 2;
|
||||||
const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_";
|
const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_";
|
||||||
|
|
||||||
QString FastForwardText() {
|
|
||||||
return u"Forward"_q;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FastReplyText() {
|
|
||||||
return tr::lng_fast_reply(tr::now);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShowFastForwardFor(const QString &username) {
|
|
||||||
return !username.compare(u"ReviewInsightsBot"_q, Qt::CaseInsensitive)
|
|
||||||
|| !username.compare(u"reviews_bot"_q, Qt::CaseInsensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] ClickHandlerPtr MakeTopicButtonLink(
|
[[nodiscard]] ClickHandlerPtr MakeTopicButtonLink(
|
||||||
not_null<Data::ForumTopic*> topic,
|
not_null<Data::ForumTopic*> topic,
|
||||||
MsgId messageId) {
|
MsgId messageId) {
|
||||||
@@ -754,16 +742,8 @@ QSize Message::performCountOptimalSize() {
|
|||||||
namew += st::msgServiceFont->spacew + via->maxWidth
|
namew += st::msgServiceFont->spacew + via->maxWidth
|
||||||
+ (_fromNameStatus ? st::msgServiceFont->spacew : 0);
|
+ (_fromNameStatus ? st::msgServiceFont->spacew : 0);
|
||||||
}
|
}
|
||||||
const auto replyWidth = hasFastForward()
|
|
||||||
? st::msgFont->width(FastForwardText())
|
|
||||||
: hasFastReply()
|
|
||||||
? st::msgFont->width(FastReplyText())
|
|
||||||
: 0;
|
|
||||||
if (Has<RightBadge>()) {
|
if (Has<RightBadge>()) {
|
||||||
namew += st::msgPadding.right()
|
namew += st::msgPadding.right() + rightBadgeWidth();
|
||||||
+ std::max(rightBadgeWidth(), replyWidth);
|
|
||||||
} else if (replyWidth) {
|
|
||||||
namew += st::msgPadding.right() + replyWidth;
|
|
||||||
}
|
}
|
||||||
accumulate_max(maxWidth, namew);
|
accumulate_max(maxWidth, namew);
|
||||||
} else if (via && !displayForwardedFrom()) {
|
} else if (via && !displayForwardedFrom()) {
|
||||||
@@ -1649,21 +1629,10 @@ void Message::paintFromName(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto badgeWidth = rightBadgeWidth();
|
const auto badgeWidth = rightBadgeWidth();
|
||||||
const auto replyWidth = [&] {
|
|
||||||
if (isUnderCursor()) {
|
|
||||||
if (displayFastForward()) {
|
|
||||||
return st::msgFont->width(FastForwardText());
|
|
||||||
} else if (displayFastReply()) {
|
|
||||||
return st::msgFont->width(FastReplyText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}();
|
|
||||||
const auto rightWidth = replyWidth ? replyWidth : badgeWidth;
|
|
||||||
auto availableLeft = trect.left();
|
auto availableLeft = trect.left();
|
||||||
auto availableWidth = trect.width();
|
auto availableWidth = trect.width();
|
||||||
if (rightWidth) {
|
if (badgeWidth) {
|
||||||
availableWidth -= st::msgPadding.right() + rightWidth;
|
availableWidth -= st::msgPadding.right() + badgeWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto stm = context.messageStyle();
|
const auto stm = context.messageStyle();
|
||||||
@@ -1746,17 +1715,9 @@ void Message::paintFromName(
|
|||||||
availableLeft += skipWidth;
|
availableLeft += skipWidth;
|
||||||
availableWidth -= skipWidth;
|
availableWidth -= skipWidth;
|
||||||
}
|
}
|
||||||
if (rightWidth) {
|
if (badgeWidth) {
|
||||||
p.setPen(stm->msgDateFg);
|
p.setPen(stm->msgDateFg);
|
||||||
if (replyWidth) {
|
if (const auto badge = Get<RightBadge>()) {
|
||||||
p.setFont(ClickHandler::showAsActive(_fastReplyLink)
|
|
||||||
? st::msgFont->underline()
|
|
||||||
: st::msgFont);
|
|
||||||
p.drawText(
|
|
||||||
trect.left() + trect.width() - rightWidth,
|
|
||||||
trect.top() + st::msgFont->ascent,
|
|
||||||
hasFastForward() ? FastForwardText() : FastReplyText());
|
|
||||||
} else if (const auto badge = Get<RightBadge>()) {
|
|
||||||
const auto badgeColor = (badge->role == BadgeRole::Creator)
|
const auto badgeColor = (badge->role == BadgeRole::Creator)
|
||||||
? st::rankOwnerFg->c
|
? st::rankOwnerFg->c
|
||||||
: (badge->role == BadgeRole::Admin)
|
: (badge->role == BadgeRole::Admin)
|
||||||
@@ -2779,29 +2740,12 @@ bool Message::getStateFromName(
|
|||||||
if (!displayFromName()) {
|
if (!displayFromName()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto replyWidth = [&] {
|
|
||||||
if (isUnderCursor()) {
|
|
||||||
if (displayFastForward()) {
|
|
||||||
return st::msgFont->width(FastForwardText());
|
|
||||||
} else if (displayFastReply()) {
|
|
||||||
return st::msgFont->width(FastReplyText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}();
|
|
||||||
if (replyWidth
|
|
||||||
&& point.x() >= trect.left() + trect.width() - replyWidth
|
|
||||||
&& point.x() < trect.left() + trect.width() + st::msgPadding.right()
|
|
||||||
&& point.y() >= trect.top() - st::msgPadding.top()
|
|
||||||
&& point.y() < trect.top() + st::msgServiceFont->height) {
|
|
||||||
outResult->link = fastReplyLink();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (point.y() >= trect.top() && point.y() < trect.top() + st::msgNameFont->height) {
|
if (point.y() >= trect.top() && point.y() < trect.top() + st::msgNameFont->height) {
|
||||||
auto availableLeft = trect.left();
|
auto availableLeft = trect.left();
|
||||||
auto availableWidth = trect.width();
|
auto availableWidth = trect.width();
|
||||||
if (replyWidth) {
|
const auto badgeWidth = rightBadgeWidth();
|
||||||
availableWidth -= st::msgPadding.right() + replyWidth;
|
if (badgeWidth) {
|
||||||
|
availableWidth -= st::msgPadding.right() + badgeWidth;
|
||||||
}
|
}
|
||||||
const auto item = data();
|
const auto item = data();
|
||||||
const auto from = item->displayFrom();
|
const auto from = item->displayFrom();
|
||||||
@@ -2849,6 +2793,50 @@ bool Message::getStateFromName(
|
|||||||
outResult->link = via->link;
|
outResult->link = via->link;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (badgeWidth) {
|
||||||
|
const auto badge = Get<RightBadge>();
|
||||||
|
const auto badgeLeft = trect.left()
|
||||||
|
+ trect.width()
|
||||||
|
- badgeWidth;
|
||||||
|
const auto badgeRight = trect.left()
|
||||||
|
+ trect.width()
|
||||||
|
+ st::msgPadding.right();
|
||||||
|
const auto boostTextWidth = (badge && !badge->boosts.isEmpty())
|
||||||
|
? badge->boosts.maxWidth()
|
||||||
|
: 0;
|
||||||
|
const auto boostLeft = boostTextWidth
|
||||||
|
? (trect.left() + trect.width() - boostTextWidth)
|
||||||
|
: 0;
|
||||||
|
if (boostTextWidth
|
||||||
|
&& point.x() >= boostLeft
|
||||||
|
&& point.x() < badgeRight) {
|
||||||
|
if (!badge->boostsLink) {
|
||||||
|
badge->boostsLink = std::make_shared<LambdaClickHandler>([=](
|
||||||
|
ClickContext context) {
|
||||||
|
if (const auto controller = ExtractController(context)) {
|
||||||
|
controller->showToast(u"Boosts clicked"_q);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
outResult->link = badge->boostsLink;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto tagRight = boostTextWidth
|
||||||
|
? (boostLeft - st::msgTagBadgeBoostSkip)
|
||||||
|
: badgeRight;
|
||||||
|
if (point.x() >= badgeLeft && point.x() < tagRight) {
|
||||||
|
if (!badge->tagLink) {
|
||||||
|
badge->tagLink = std::make_shared<LambdaClickHandler>([=](
|
||||||
|
ClickContext context) {
|
||||||
|
if (const auto controller = ExtractController(context)) {
|
||||||
|
controller->showToast(u"Tag clicked"_q);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
outResult->link = badge->tagLink;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
trect.setTop(trect.top() + st::msgNameFont->height);
|
trect.setTop(trect.top() + st::msgNameFont->height);
|
||||||
return false;
|
return false;
|
||||||
@@ -3400,6 +3388,31 @@ Reactions::ButtonParameters Message::reactionButtonParameters(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReplyButton::ButtonParameters Message::replyButtonParameters(
|
||||||
|
QPoint position,
|
||||||
|
const TextState &replyState) const {
|
||||||
|
using namespace ReplyButton;
|
||||||
|
if (!displayFastReply() || unwrapped()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto result = ButtonParameters{ .context = data()->fullId() };
|
||||||
|
const auto geometry = countGeometry();
|
||||||
|
result.pointer = position;
|
||||||
|
const auto reactionInnerRight = st::reactionCornerCenter.x()
|
||||||
|
+ st::reactionCornerSize.width() / 2;
|
||||||
|
const auto replyInnerWidth = ReplyButton::ComputeInnerWidth();
|
||||||
|
const auto relativeCenter = QPoint(
|
||||||
|
geometry.width() + reactionInnerRight - replyInnerWidth,
|
||||||
|
st::replyCornerCenter.y());
|
||||||
|
result.center = geometry.topLeft() + relativeCenter;
|
||||||
|
if (replyState.itemId != result.context
|
||||||
|
&& !geometry.contains(position)) {
|
||||||
|
result.outside = true;
|
||||||
|
}
|
||||||
|
result.link = fastReplyLink();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int Message::reactionsOptimalWidth() const {
|
int Message::reactionsOptimalWidth() const {
|
||||||
return _reactions ? _reactions->countNiceWidth() : 0;
|
return _reactions ? _reactions->countNiceWidth() : 0;
|
||||||
}
|
}
|
||||||
@@ -3856,22 +3869,6 @@ bool Message::hasFastReply() const {
|
|||||||
return !hasOutLayout() && (peer->isChat() || peer->isMegagroup());
|
return !hasOutLayout() && (peer->isChat() || peer->isMegagroup());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Message::hasFastForward() const {
|
|
||||||
if (context() != Context::History) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto item = data();
|
|
||||||
const auto from = item->from()->asUser();
|
|
||||||
if (!from || !from->isBot() || !ShowFastForwardFor(from->username())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto peer = item->history()->peer;
|
|
||||||
if (!peer->isChat() && !peer->isMegagroup()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !hasOutLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Message::displayFastReply() const {
|
bool Message::displayFastReply() const {
|
||||||
const auto canSendAnything = [&] {
|
const auto canSendAnything = [&] {
|
||||||
const auto item = data();
|
const auto item = data();
|
||||||
@@ -3888,12 +3885,6 @@ bool Message::displayFastReply() const {
|
|||||||
&& !delegate()->elementInSelectionMode(this).inSelectionMode;
|
&& !delegate()->elementInSelectionMode(this).inSelectionMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Message::displayFastForward() const {
|
|
||||||
return hasFastForward()
|
|
||||||
&& data()->allowsForward()
|
|
||||||
&& !delegate()->elementInSelectionMode(this).inSelectionMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Message::displayRightActionComments() const {
|
bool Message::displayRightActionComments() const {
|
||||||
return !isPinnedContext()
|
return !isPinnedContext()
|
||||||
&& (context() != Context::SavedSublist)
|
&& (context() != Context::SavedSublist)
|
||||||
@@ -4178,22 +4169,9 @@ ClickHandlerPtr Message::fastReplyLink() const {
|
|||||||
return _fastReplyLink;
|
return _fastReplyLink;
|
||||||
}
|
}
|
||||||
const auto itemId = data()->fullId();
|
const auto itemId = data()->fullId();
|
||||||
const auto sessionId = data()->history()->session().uniqueId();
|
_fastReplyLink = std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
||||||
_fastReplyLink = hasFastForward()
|
delegate()->elementReplyTo({ itemId });
|
||||||
? std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
}));
|
||||||
const auto controller = ExtractController(context);
|
|
||||||
const auto session = controller
|
|
||||||
? &controller->session()
|
|
||||||
: nullptr;
|
|
||||||
if (!session || session->uniqueId() != sessionId) {
|
|
||||||
return;
|
|
||||||
} else if (const auto item = session->data().message(itemId)) {
|
|
||||||
FastShareMessage(controller, item);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
: std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
|
||||||
delegate()->elementReplyTo({ itemId });
|
|
||||||
}));
|
|
||||||
return _fastReplyLink;
|
return _fastReplyLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4282,16 +4260,8 @@ void Message::updateMediaInBubbleState() {
|
|||||||
|
|
||||||
void Message::fromNameUpdated(int width) const {
|
void Message::fromNameUpdated(int width) const {
|
||||||
const auto item = data();
|
const auto item = data();
|
||||||
const auto replyWidth = hasFastForward()
|
|
||||||
? st::msgFont->width(FastForwardText())
|
|
||||||
: hasFastReply()
|
|
||||||
? st::msgFont->width(FastReplyText())
|
|
||||||
: 0;
|
|
||||||
if (Has<RightBadge>()) {
|
if (Has<RightBadge>()) {
|
||||||
width -= st::msgPadding.right()
|
width -= st::msgPadding.right() + rightBadgeWidth();
|
||||||
+ std::max(rightBadgeWidth(), replyWidth);
|
|
||||||
} else if (replyWidth) {
|
|
||||||
width -= st::msgPadding.right() + replyWidth;
|
|
||||||
}
|
}
|
||||||
const auto from = item->displayFrom();
|
const auto from = item->displayFrom();
|
||||||
validateFromNameText(from);
|
validateFromNameText(from);
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ namespace Reactions {
|
|||||||
class InlineList;
|
class InlineList;
|
||||||
} // namespace Reactions
|
} // namespace Reactions
|
||||||
|
|
||||||
|
namespace ReplyButton {
|
||||||
|
struct ButtonParameters;
|
||||||
|
} // namespace ReplyButton
|
||||||
|
|
||||||
// Special type of Component for the channel actions log.
|
// Special type of Component for the channel actions log.
|
||||||
struct LogEntryOriginal : RuntimeComponent<LogEntryOriginal, Element> {
|
struct LogEntryOriginal : RuntimeComponent<LogEntryOriginal, Element> {
|
||||||
LogEntryOriginal();
|
LogEntryOriginal();
|
||||||
@@ -68,6 +72,8 @@ enum class BadgeRole : uchar {
|
|||||||
struct RightBadge : RuntimeComponent<RightBadge, Element> {
|
struct RightBadge : RuntimeComponent<RightBadge, Element> {
|
||||||
Ui::Text::String tag;
|
Ui::Text::String tag;
|
||||||
Ui::Text::String boosts;
|
Ui::Text::String boosts;
|
||||||
|
mutable ClickHandlerPtr tagLink;
|
||||||
|
mutable ClickHandlerPtr boostsLink;
|
||||||
int width = 0;
|
int width = 0;
|
||||||
BadgeRole role = BadgeRole::User;
|
BadgeRole role = BadgeRole::User;
|
||||||
bool overridden = false;
|
bool overridden = false;
|
||||||
@@ -126,6 +132,9 @@ public:
|
|||||||
Reactions::ButtonParameters reactionButtonParameters(
|
Reactions::ButtonParameters reactionButtonParameters(
|
||||||
QPoint position,
|
QPoint position,
|
||||||
const TextState &reactionState) const override;
|
const TextState &reactionState) const override;
|
||||||
|
ReplyButton::ButtonParameters replyButtonParameters(
|
||||||
|
QPoint position,
|
||||||
|
const TextState &replyState) const override;
|
||||||
int reactionsOptimalWidth() const override;
|
int reactionsOptimalWidth() const override;
|
||||||
|
|
||||||
void unloadHeavyPart() override;
|
void unloadHeavyPart() override;
|
||||||
@@ -300,9 +309,7 @@ private:
|
|||||||
[[nodiscard]] bool needInfoDisplay() const;
|
[[nodiscard]] bool needInfoDisplay() const;
|
||||||
[[nodiscard]] bool invertMedia() const;
|
[[nodiscard]] bool invertMedia() const;
|
||||||
[[nodiscard]] bool hasFastReply() const;
|
[[nodiscard]] bool hasFastReply() const;
|
||||||
[[nodiscard]] bool hasFastForward() const;
|
|
||||||
[[nodiscard]] bool displayFastReply() const;
|
[[nodiscard]] bool displayFastReply() const;
|
||||||
[[nodiscard]] bool displayFastForward() const;
|
|
||||||
|
|
||||||
[[nodiscard]] bool isPinnedContext() const;
|
[[nodiscard]] bool isPinnedContext() const;
|
||||||
[[nodiscard]] bool isCommentsRootView() const;
|
[[nodiscard]] bool isCommentsRootView() const;
|
||||||
|
|||||||
@@ -0,0 +1,350 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "history/view/history_view_reply_button.h"
|
||||||
|
|
||||||
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
|
namespace HistoryView::ReplyButton {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kToggleDuration = crl::time(120);
|
||||||
|
constexpr auto kButtonShowDelay = crl::time(300);
|
||||||
|
constexpr auto kButtonHideDelay = crl::time(300);
|
||||||
|
|
||||||
|
[[nodiscard]] float64 ScaleForState(ButtonState state) {
|
||||||
|
switch (state) {
|
||||||
|
case ButtonState::Hidden: return 0.;
|
||||||
|
case ButtonState::Shown:
|
||||||
|
case ButtonState::Active:
|
||||||
|
case ButtonState::Inside: return 1.;
|
||||||
|
}
|
||||||
|
Unexpected("State in ReplyButton::ScaleForState.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] float64 OpacityForScale(float64 scale) {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QSize ComputeInnerSize() {
|
||||||
|
return QSize(ComputeInnerWidth(), st::replyCornerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QSize ComputeOuterSize() {
|
||||||
|
return QRect(
|
||||||
|
QPoint(),
|
||||||
|
ComputeInnerSize()
|
||||||
|
).marginsAdded(st::replyCornerShadow).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int ComputeInnerWidth() {
|
||||||
|
struct Cached {
|
||||||
|
QString text;
|
||||||
|
int result = 0;
|
||||||
|
};
|
||||||
|
static auto cached = Cached();
|
||||||
|
const auto &text = tr::lng_fast_reply(tr::now);
|
||||||
|
if (cached.text != text) {
|
||||||
|
const auto &padding = st::replyCornerTextPadding;
|
||||||
|
const auto textWidth = st::msgDateTextStyle.font->width(text);
|
||||||
|
cached.result = padding.left() + textWidth + padding.right();
|
||||||
|
cached.text = text;
|
||||||
|
}
|
||||||
|
return cached.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button::Button(
|
||||||
|
Fn<void(QRect)> update,
|
||||||
|
ButtonParameters parameters,
|
||||||
|
Fn<void()> hide,
|
||||||
|
QSize outer)
|
||||||
|
: _update(std::move(update))
|
||||||
|
, _finalScale(ScaleForState(_state))
|
||||||
|
, _collapsed(QPoint(), outer)
|
||||||
|
, _geometry(_collapsed)
|
||||||
|
, _hideTimer(hide) {
|
||||||
|
applyParameters(parameters, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button::~Button() = default;
|
||||||
|
|
||||||
|
bool Button::isHidden() const {
|
||||||
|
return (_state == ButtonState::Hidden)
|
||||||
|
&& !_opacityAnimation.animating();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Button::geometry() const {
|
||||||
|
return _geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
float64 Button::currentScale() const {
|
||||||
|
return _scaleAnimation.value(_finalScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
float64 Button::currentOpacity() const {
|
||||||
|
return _opacityAnimation.value(
|
||||||
|
OpacityForScale(ScaleForState(_state)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::applyParameters(ButtonParameters parameters) {
|
||||||
|
applyParameters(std::move(parameters), _update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::applyParameters(
|
||||||
|
ButtonParameters parameters,
|
||||||
|
Fn<void(QRect)> update) {
|
||||||
|
const auto shift = parameters.center - _collapsed.center();
|
||||||
|
_collapsed = _collapsed.translated(shift);
|
||||||
|
updateGeometry(update);
|
||||||
|
applyState(ButtonState::Shown, update);
|
||||||
|
if (parameters.outside) {
|
||||||
|
_hideTimer.callOnce(kButtonHideDelay);
|
||||||
|
} else {
|
||||||
|
_hideTimer.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::updateGeometry(Fn<void(QRect)> update) {
|
||||||
|
if (_geometry != _collapsed) {
|
||||||
|
if (update) {
|
||||||
|
update(_geometry);
|
||||||
|
}
|
||||||
|
_geometry = _collapsed;
|
||||||
|
if (update) {
|
||||||
|
update(_geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::applyState(ButtonState state) {
|
||||||
|
applyState(state, _update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::applyState(ButtonState state, Fn<void(QRect)> update) {
|
||||||
|
if (state == ButtonState::Hidden) {
|
||||||
|
_hideTimer.cancel();
|
||||||
|
}
|
||||||
|
updateGeometry(update);
|
||||||
|
if (_state == state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto finalScale = ScaleForState(state);
|
||||||
|
_opacityAnimation.start(
|
||||||
|
[=] { _update(_geometry); },
|
||||||
|
OpacityForScale(ScaleForState(_state)),
|
||||||
|
OpacityForScale(ScaleForState(state)),
|
||||||
|
kToggleDuration,
|
||||||
|
anim::sineInOut);
|
||||||
|
if (state != ButtonState::Hidden && _finalScale != finalScale) {
|
||||||
|
_scaleAnimation.start(
|
||||||
|
[=] { _update(_geometry); },
|
||||||
|
_finalScale,
|
||||||
|
finalScale,
|
||||||
|
kToggleDuration,
|
||||||
|
anim::sineInOut);
|
||||||
|
_finalScale = finalScale;
|
||||||
|
}
|
||||||
|
_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Manager::Manager(Fn<void(QRect)> buttonUpdate)
|
||||||
|
: _outer(ComputeOuterSize())
|
||||||
|
, _inner(QRect(QPoint(), ComputeInnerSize()))
|
||||||
|
, _cachedRound(
|
||||||
|
ComputeInnerSize(),
|
||||||
|
st::replyCornerShadow,
|
||||||
|
ComputeInnerSize().height())
|
||||||
|
, _buttonShowTimer([=] { showButtonDelayed(); })
|
||||||
|
, _buttonUpdate(std::move(buttonUpdate))
|
||||||
|
, _text(st::msgDateTextStyle.font->width(tr::lng_fast_reply(tr::now))) {
|
||||||
|
_text.setText(st::msgDateTextStyle, tr::lng_fast_reply(tr::now));
|
||||||
|
_inner.translate(
|
||||||
|
QRect(QPoint(), _outer).center() - _inner.center());
|
||||||
|
}
|
||||||
|
|
||||||
|
Manager::~Manager() = default;
|
||||||
|
|
||||||
|
void Manager::updateButton(ButtonParameters parameters) {
|
||||||
|
const auto contextChanged = (_buttonContext != parameters.context);
|
||||||
|
if (contextChanged) {
|
||||||
|
if (_button) {
|
||||||
|
_button->applyState(ButtonState::Hidden);
|
||||||
|
_buttonHiding.push_back(std::move(_button));
|
||||||
|
}
|
||||||
|
_buttonShowTimer.cancel();
|
||||||
|
_scheduledParameters = std::nullopt;
|
||||||
|
}
|
||||||
|
_buttonContext = parameters.context;
|
||||||
|
if (parameters.link) {
|
||||||
|
_link = parameters.link;
|
||||||
|
}
|
||||||
|
if (!_buttonContext) {
|
||||||
|
return;
|
||||||
|
} else if (_button) {
|
||||||
|
_button->applyParameters(parameters);
|
||||||
|
return;
|
||||||
|
} else if (parameters.outside) {
|
||||||
|
_buttonShowTimer.cancel();
|
||||||
|
_scheduledParameters = std::nullopt;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto globalPositionChanged = _scheduledParameters
|
||||||
|
&& (_scheduledParameters->globalPointer
|
||||||
|
!= parameters.globalPointer);
|
||||||
|
const auto positionChanged = _scheduledParameters
|
||||||
|
&& (_scheduledParameters->pointer != parameters.pointer);
|
||||||
|
_scheduledParameters = parameters;
|
||||||
|
if ((_buttonShowTimer.isActive() && positionChanged)
|
||||||
|
|| globalPositionChanged) {
|
||||||
|
_buttonShowTimer.callOnce(kButtonShowDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::showButtonDelayed() {
|
||||||
|
clearAppearAnimations();
|
||||||
|
_button = std::make_unique<Button>(
|
||||||
|
_buttonUpdate,
|
||||||
|
*_scheduledParameters,
|
||||||
|
[=] { updateButton({}); },
|
||||||
|
_outer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::paint(QPainter &p, const PaintContext &context) {
|
||||||
|
removeStaleButtons();
|
||||||
|
for (const auto &button : _buttonHiding) {
|
||||||
|
paintButton(p, context, button.get());
|
||||||
|
}
|
||||||
|
if (const auto current = _button.get()) {
|
||||||
|
if (context.gestureHorizontal.ratio) {
|
||||||
|
current->applyState(ButtonState::Hidden);
|
||||||
|
_buttonHiding.push_back(std::move(_button));
|
||||||
|
}
|
||||||
|
paintButton(p, context, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::paintButton(
|
||||||
|
QPainter &p,
|
||||||
|
const PaintContext &context,
|
||||||
|
not_null<Button*> button) {
|
||||||
|
const auto geometry = button->geometry();
|
||||||
|
if (!context.clip.intersects(geometry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
constexpr auto kFramesCount = Ui::RoundAreaWithShadow::kFramesCount;
|
||||||
|
const auto scale = button->currentScale();
|
||||||
|
const auto scaleMin = ScaleForState(ButtonState::Hidden);
|
||||||
|
const auto scaleMax = ScaleForState(ButtonState::Shown);
|
||||||
|
const auto progress = (scale - scaleMin) / (scaleMax - scaleMin);
|
||||||
|
const auto frame = int(
|
||||||
|
base::SafeRound(progress * (kFramesCount - 1)));
|
||||||
|
const auto useScale = scaleMin
|
||||||
|
+ (frame / float64(kFramesCount - 1))
|
||||||
|
* (scaleMax - scaleMin);
|
||||||
|
paintButton(p, context, button, frame, useScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::paintButton(
|
||||||
|
QPainter &p,
|
||||||
|
const PaintContext &context,
|
||||||
|
not_null<Button*> button,
|
||||||
|
int frameIndex,
|
||||||
|
float64 scale) {
|
||||||
|
const auto opacity = button->currentOpacity();
|
||||||
|
if (opacity == 0.) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto geometry = button->geometry();
|
||||||
|
const auto position = geometry.topLeft();
|
||||||
|
if (opacity != 1.) {
|
||||||
|
p.setOpacity(opacity);
|
||||||
|
}
|
||||||
|
const auto shadow = context.st->shadowFg()->c;
|
||||||
|
const auto background = context.st->windowBg()->c;
|
||||||
|
_cachedRound.setShadowColor(shadow);
|
||||||
|
_cachedRound.setBackgroundColor(background);
|
||||||
|
const auto radius = _inner.height() / 2.;
|
||||||
|
const auto frame = _cachedRound.validateFrame(
|
||||||
|
frameIndex,
|
||||||
|
scale,
|
||||||
|
radius);
|
||||||
|
p.drawImage(position, *frame.image, frame.rect);
|
||||||
|
|
||||||
|
const auto textLeft = position.x()
|
||||||
|
+ _inner.x()
|
||||||
|
+ st::replyCornerTextPadding.left();
|
||||||
|
const auto textTop = position.y()
|
||||||
|
+ _inner.y()
|
||||||
|
+ (_inner.height() - st::msgDateTextStyle.font->height) / 2;
|
||||||
|
const auto &incomingStyle = context.st->messageStyle(false, false);
|
||||||
|
p.setPen(incomingStyle.msgDateFg);
|
||||||
|
p.setFont(st::msgDateTextStyle.font);
|
||||||
|
_text.draw(p, {
|
||||||
|
.position = QPoint(textLeft, textTop),
|
||||||
|
.availableWidth = _text.maxWidth(),
|
||||||
|
});
|
||||||
|
if (opacity != 1.) {
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextState Manager::buttonTextState(QPoint position) const {
|
||||||
|
if (overCurrentButton(position)) {
|
||||||
|
auto result = TextState(nullptr, _link);
|
||||||
|
result.itemId = _buttonContext;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Manager::overCurrentButton(QPoint position) const {
|
||||||
|
if (!_button) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return buttonInner().contains(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMargins Manager::innerMargins() const {
|
||||||
|
return {
|
||||||
|
_inner.x(),
|
||||||
|
_inner.y(),
|
||||||
|
_outer.width() - _inner.x() - _inner.width(),
|
||||||
|
_outer.height() - _inner.y() - _inner.height(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Manager::buttonInner() const {
|
||||||
|
return buttonInner(_button.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Manager::buttonInner(not_null<Button*> button) const {
|
||||||
|
return button->geometry().marginsRemoved(innerMargins());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::remove(FullMsgId context) {
|
||||||
|
if (_buttonContext == context) {
|
||||||
|
_buttonContext = {};
|
||||||
|
_button = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::removeStaleButtons() {
|
||||||
|
_buttonHiding.erase(
|
||||||
|
ranges::remove_if(_buttonHiding, &Button::isHidden),
|
||||||
|
end(_buttonHiding));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::clearAppearAnimations() {
|
||||||
|
_buttonHiding.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HistoryView::ReplyButton
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/timer.h"
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
#include "ui/effects/round_area_with_shadow.h"
|
||||||
|
#include "ui/text/text.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
struct ChatPaintContext;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
using PaintContext = Ui::ChatPaintContext;
|
||||||
|
struct TextState;
|
||||||
|
} // namespace HistoryView
|
||||||
|
|
||||||
|
namespace HistoryView::ReplyButton {
|
||||||
|
|
||||||
|
struct ButtonParameters {
|
||||||
|
[[nodiscard]] ButtonParameters translated(QPoint delta) const {
|
||||||
|
auto result = *this;
|
||||||
|
result.center += delta;
|
||||||
|
result.pointer += delta;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FullMsgId context;
|
||||||
|
QPoint center;
|
||||||
|
QPoint pointer;
|
||||||
|
QPoint globalPointer;
|
||||||
|
ClickHandlerPtr link;
|
||||||
|
int visibleTop = 0;
|
||||||
|
int visibleBottom = 0;
|
||||||
|
bool outside = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] int ComputeInnerWidth();
|
||||||
|
|
||||||
|
enum class ButtonState {
|
||||||
|
Hidden,
|
||||||
|
Shown,
|
||||||
|
Active,
|
||||||
|
Inside,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Button final {
|
||||||
|
public:
|
||||||
|
Button(
|
||||||
|
Fn<void(QRect)> update,
|
||||||
|
ButtonParameters parameters,
|
||||||
|
Fn<void()> hide,
|
||||||
|
QSize outer);
|
||||||
|
~Button();
|
||||||
|
|
||||||
|
void applyParameters(ButtonParameters parameters);
|
||||||
|
void applyState(ButtonState state);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isHidden() const;
|
||||||
|
[[nodiscard]] QRect geometry() const;
|
||||||
|
[[nodiscard]] float64 currentScale() const;
|
||||||
|
[[nodiscard]] float64 currentOpacity() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateGeometry(Fn<void(QRect)> update);
|
||||||
|
void applyState(ButtonState state, Fn<void(QRect)> update);
|
||||||
|
void applyParameters(
|
||||||
|
ButtonParameters parameters,
|
||||||
|
Fn<void(QRect)> update);
|
||||||
|
|
||||||
|
const Fn<void(QRect)> _update;
|
||||||
|
|
||||||
|
ButtonState _state = ButtonState::Hidden;
|
||||||
|
float64 _finalScale = 0.;
|
||||||
|
Ui::Animations::Simple _scaleAnimation;
|
||||||
|
Ui::Animations::Simple _opacityAnimation;
|
||||||
|
|
||||||
|
QRect _collapsed;
|
||||||
|
QRect _geometry;
|
||||||
|
|
||||||
|
base::Timer _hideTimer;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Manager final : public base::has_weak_ptr {
|
||||||
|
public:
|
||||||
|
Manager(Fn<void(QRect)> buttonUpdate);
|
||||||
|
~Manager();
|
||||||
|
|
||||||
|
void updateButton(ButtonParameters parameters);
|
||||||
|
void paint(QPainter &p, const PaintContext &context);
|
||||||
|
[[nodiscard]] TextState buttonTextState(QPoint position) const;
|
||||||
|
void remove(FullMsgId context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void showButtonDelayed();
|
||||||
|
[[nodiscard]] bool overCurrentButton(QPoint position) const;
|
||||||
|
void paintButton(
|
||||||
|
QPainter &p,
|
||||||
|
const PaintContext &context,
|
||||||
|
not_null<Button*> button);
|
||||||
|
void paintButton(
|
||||||
|
QPainter &p,
|
||||||
|
const PaintContext &context,
|
||||||
|
not_null<Button*> button,
|
||||||
|
int frame,
|
||||||
|
float64 scale);
|
||||||
|
void removeStaleButtons();
|
||||||
|
void clearAppearAnimations();
|
||||||
|
[[nodiscard]] QMargins innerMargins() const;
|
||||||
|
[[nodiscard]] QRect buttonInner() const;
|
||||||
|
[[nodiscard]] QRect buttonInner(not_null<Button*> button) const;
|
||||||
|
|
||||||
|
QSize _outer;
|
||||||
|
QRect _inner;
|
||||||
|
Ui::RoundAreaWithShadow _cachedRound;
|
||||||
|
|
||||||
|
ClickHandlerPtr _link;
|
||||||
|
FullMsgId _buttonContext;
|
||||||
|
|
||||||
|
std::optional<ButtonParameters> _scheduledParameters;
|
||||||
|
base::Timer _buttonShowTimer;
|
||||||
|
const Fn<void(QRect)> _buttonUpdate;
|
||||||
|
std::unique_ptr<Button> _button;
|
||||||
|
std::vector<std::unique_ptr<Button>> _buttonHiding;
|
||||||
|
|
||||||
|
Ui::Text::String _text;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView::ReplyButton
|
||||||
@@ -218,7 +218,7 @@ UnwrappedMedia::SurroundingInfo UnwrappedMedia::surroundingInfo(
|
|||||||
panelHeight += reply->height()
|
panelHeight += reply->height()
|
||||||
- ((forwarded || via) ? 0 : replyMargins.top())
|
- ((forwarded || via) ? 0 : replyMargins.top())
|
||||||
- replyMargins.bottom();
|
- replyMargins.bottom();
|
||||||
} else {
|
} else if (panelHeight) {
|
||||||
panelHeight += st::msgReplyPadding.bottom();
|
panelHeight += st::msgReplyPadding.bottom();
|
||||||
}
|
}
|
||||||
const auto total = (topicSize.isEmpty() ? 0 : topicSize.height())
|
const auto total = (topicSize.isEmpty() ? 0 : topicSize.height())
|
||||||
|
|||||||
@@ -916,6 +916,11 @@ reactionMainAppearShift: 20px;
|
|||||||
reactionCollapseFadeThreshold: 40px;
|
reactionCollapseFadeThreshold: 40px;
|
||||||
reactionFlyUp: 50px;
|
reactionFlyUp: 50px;
|
||||||
|
|
||||||
|
replyCornerHeight: 20px;
|
||||||
|
replyCornerShadow: margins(4px, 8px, 4px, 8px);
|
||||||
|
replyCornerCenter: point(7px, -2px);
|
||||||
|
replyCornerTextPadding: margins(6px, 0px, 6px, 0px);
|
||||||
|
|
||||||
effectInfoImage: 12px;
|
effectInfoImage: 12px;
|
||||||
|
|
||||||
searchInChatMultiSelectItem: MultiSelectItem(defaultMultiSelectItem) {
|
searchInChatMultiSelectItem: MultiSelectItem(defaultMultiSelectItem) {
|
||||||
|
|||||||
Reference in New Issue
Block a user