mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Handle clicks on topic jump area.
This commit is contained in:
parent
ede34578da
commit
561e3f4809
11 changed files with 486 additions and 141 deletions
|
@ -156,6 +156,11 @@ InnerWidget::InnerWidget(
|
||||||
_cancelSearchInChat->hide();
|
_cancelSearchInChat->hide();
|
||||||
_cancelSearchFromUser->hide();
|
_cancelSearchFromUser->hide();
|
||||||
|
|
||||||
|
style::PaletteChanged(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_topicJumpCache = nullptr;
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
session().downloaderTaskFinished(
|
session().downloaderTaskFinished(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
update();
|
update();
|
||||||
|
@ -336,7 +341,7 @@ void InnerWidget::refreshWithCollapsedRows(bool toTop) {
|
||||||
_selected = nullptr;
|
_selected = nullptr;
|
||||||
}
|
}
|
||||||
if (_pressed && _pressed->folder() == archive) {
|
if (_pressed && _pressed->folder() == archive) {
|
||||||
setPressed(nullptr);
|
clearPressed();
|
||||||
}
|
}
|
||||||
_skipTopDialog = true;
|
_skipTopDialog = true;
|
||||||
if (!inMainMenu && !_filterId) {
|
if (!inMainMenu && !_filterId) {
|
||||||
|
@ -538,6 +543,9 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
.selected = (_menuRow.key
|
.selected = (_menuRow.key
|
||||||
? (row->key() == _menuRow.key)
|
? (row->key() == _menuRow.key)
|
||||||
: selected),
|
: selected),
|
||||||
|
.topicJumpSelected = (selected
|
||||||
|
&& _selectedTopicJump
|
||||||
|
&& (!_pressed || _pressedTopicJump)),
|
||||||
.paused = videoPaused,
|
.paused = videoPaused,
|
||||||
.narrow = (fullWidth < st::columnMinimalWidthLeft),
|
.narrow = (fullWidth < st::columnMinimalWidthLeft),
|
||||||
});
|
});
|
||||||
|
@ -1142,7 +1150,7 @@ void InnerWidget::clearIrrelevantState() {
|
||||||
_collapsedSelected = -1;
|
_collapsedSelected = -1;
|
||||||
setCollapsedPressed(-1);
|
setCollapsedPressed(-1);
|
||||||
_selected = nullptr;
|
_selected = nullptr;
|
||||||
setPressed(nullptr);
|
clearPressed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1168,9 +1176,16 @@ void InnerWidget::selectByMouse(QPoint globalPosition) {
|
||||||
: (mouseY >= offset)
|
: (mouseY >= offset)
|
||||||
? _shownList->rowAtY(mouseY - offset)
|
? _shownList->rowAtY(mouseY - offset)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
if (_selected != selected || _collapsedSelected != collapsedSelected) {
|
const auto selectedTopicJump = selected
|
||||||
|
&& selected->lookupIsInTopicJump(
|
||||||
|
local.x(),
|
||||||
|
mouseY - offset - selected->top());
|
||||||
|
if (_collapsedSelected != collapsedSelected
|
||||||
|
|| _selected != selected
|
||||||
|
|| _selectedTopicJump != selectedTopicJump) {
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
_selected = selected;
|
_selected = selected;
|
||||||
|
_selectedTopicJump = selectedTopicJump;
|
||||||
_collapsedSelected = collapsedSelected;
|
_collapsedSelected = collapsedSelected;
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
setCursor((_selected || _collapsedSelected)
|
setCursor((_selected || _collapsedSelected)
|
||||||
|
@ -1203,9 +1218,15 @@ void InnerWidget::selectByMouse(QPoint globalPosition) {
|
||||||
if (filteredSelected < 0 || filteredSelected >= _filterResults.size()) {
|
if (filteredSelected < 0 || filteredSelected >= _filterResults.size()) {
|
||||||
filteredSelected = -1;
|
filteredSelected = -1;
|
||||||
}
|
}
|
||||||
if (_filteredSelected != filteredSelected) {
|
const auto selectedTopicJump = (filteredSelected >= 0)
|
||||||
|
&& _filterResults[filteredSelected].row->lookupIsInTopicJump(
|
||||||
|
local.x(),
|
||||||
|
mouseY - skip - _filterResults[filteredSelected].top);
|
||||||
|
if (_filteredSelected != filteredSelected
|
||||||
|
|| _selectedTopicJump != selectedTopicJump) {
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
_filteredSelected = filteredSelected;
|
_filteredSelected = filteredSelected;
|
||||||
|
_selectedTopicJump = selectedTopicJump;
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1243,7 +1264,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
selectByMouse(e->globalPos());
|
selectByMouse(e->globalPos());
|
||||||
|
|
||||||
_pressButton = e->button();
|
_pressButton = e->button();
|
||||||
setPressed(_selected);
|
setPressed(_selected, _selectedTopicJump);
|
||||||
setCollapsedPressed(_collapsedSelected);
|
setCollapsedPressed(_collapsedSelected);
|
||||||
setHashtagPressed(_hashtagSelected);
|
setHashtagPressed(_hashtagSelected);
|
||||||
_hashtagDeletePressed = _hashtagDeleteSelected;
|
_hashtagDeletePressed = _hashtagDeleteSelected;
|
||||||
|
@ -1257,14 +1278,25 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
});
|
});
|
||||||
} else if (_pressed) {
|
} else if (_pressed) {
|
||||||
auto row = _pressed;
|
auto row = _pressed;
|
||||||
row->addRipple(
|
const auto updateCallback = [this, row] {
|
||||||
e->pos() - QPoint(0, dialogsOffset() + _pressed->top()),
|
if (!_pinnedShiftAnimation.animating()) {
|
||||||
QSize(width(), _pressed->height()),
|
row->entry()->updateChatListEntry();
|
||||||
[this, row] {
|
}
|
||||||
if (!_pinnedShiftAnimation.animating()) {
|
};
|
||||||
row->entry()->updateChatListEntry();
|
const auto origin = e->pos()
|
||||||
}
|
- QPoint(0, dialogsOffset() + _pressed->top());
|
||||||
});
|
if (_pressedTopicJump) {
|
||||||
|
row->addTopicJumpRipple(
|
||||||
|
origin,
|
||||||
|
_topicJumpCache.get(),
|
||||||
|
updateCallback);
|
||||||
|
} else {
|
||||||
|
row->clearTopicJumpRipple();
|
||||||
|
row->addRipple(
|
||||||
|
origin,
|
||||||
|
QSize(width(), _pressed->height()),
|
||||||
|
updateCallback);
|
||||||
|
}
|
||||||
_dragStart = e->pos();
|
_dragStart = e->pos();
|
||||||
} else if (base::in_range(_hashtagPressed, 0, _hashtagResults.size()) && !_hashtagDeletePressed) {
|
} else if (base::in_range(_hashtagPressed, 0, _hashtagResults.size()) && !_hashtagDeletePressed) {
|
||||||
auto row = &_hashtagResults[_hashtagPressed]->row;
|
auto row = &_hashtagResults[_hashtagPressed]->row;
|
||||||
|
@ -1543,8 +1575,10 @@ void InnerWidget::mousePressReleased(
|
||||||
|
|
||||||
auto collapsedPressed = _collapsedPressed;
|
auto collapsedPressed = _collapsedPressed;
|
||||||
setCollapsedPressed(-1);
|
setCollapsedPressed(-1);
|
||||||
|
const auto pressedTopicRootId = _pressedTopicJumpRootId;
|
||||||
|
const auto pressedTopicJump = _pressedTopicJump;
|
||||||
auto pressed = _pressed;
|
auto pressed = _pressed;
|
||||||
setPressed(nullptr);
|
clearPressed();
|
||||||
auto hashtagPressed = _hashtagPressed;
|
auto hashtagPressed = _hashtagPressed;
|
||||||
setHashtagPressed(-1);
|
setHashtagPressed(-1);
|
||||||
auto hashtagDeletePressed = _hashtagDeletePressed;
|
auto hashtagDeletePressed = _hashtagDeletePressed;
|
||||||
|
@ -1561,7 +1595,9 @@ void InnerWidget::mousePressReleased(
|
||||||
updateSelectedRow();
|
updateSelectedRow();
|
||||||
if (!wasDragging && button == Qt::LeftButton) {
|
if (!wasDragging && button == Qt::LeftButton) {
|
||||||
if ((collapsedPressed >= 0 && collapsedPressed == _collapsedSelected)
|
if ((collapsedPressed >= 0 && collapsedPressed == _collapsedSelected)
|
||||||
|| (pressed && pressed == _selected)
|
|| (pressed
|
||||||
|
&& pressed == _selected
|
||||||
|
&& pressedTopicJump == _selectedTopicJump)
|
||||||
|| (hashtagPressed >= 0
|
|| (hashtagPressed >= 0
|
||||||
&& hashtagPressed == _hashtagSelected
|
&& hashtagPressed == _hashtagSelected
|
||||||
&& hashtagDeletePressed == _hashtagDeleteSelected)
|
&& hashtagDeletePressed == _hashtagDeleteSelected)
|
||||||
|
@ -1570,7 +1606,7 @@ void InnerWidget::mousePressReleased(
|
||||||
&& peerSearchPressed == _peerSearchSelected)
|
&& peerSearchPressed == _peerSearchSelected)
|
||||||
|| (searchedPressed >= 0
|
|| (searchedPressed >= 0
|
||||||
&& searchedPressed == _searchedSelected)) {
|
&& searchedPressed == _searchedSelected)) {
|
||||||
chooseRow(modifiers);
|
chooseRow(modifiers, pressedTopicRootId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1584,15 +1620,23 @@ void InnerWidget::setCollapsedPressed(int pressed) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::setPressed(Row *pressed) {
|
void InnerWidget::setPressed(Row *pressed, bool pressedTopicJump) {
|
||||||
if (_pressed != pressed) {
|
if (_pressed != pressed || _pressedTopicJump != pressedTopicJump) {
|
||||||
if (_pressed) {
|
if (_pressed) {
|
||||||
_pressed->stopLastRipple();
|
_pressed->stopLastRipple();
|
||||||
}
|
}
|
||||||
_pressed = pressed;
|
_pressed = pressed;
|
||||||
|
_pressedTopicJump = pressedTopicJump;
|
||||||
|
const auto history = pressedTopicJump ? pressed->history() : nullptr;
|
||||||
|
const auto item = history ? history->chatListMessage() : nullptr;
|
||||||
|
_pressedTopicJumpRootId = item ? item->topicRootId() : MsgId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InnerWidget::clearPressed() {
|
||||||
|
setPressed(nullptr, false);
|
||||||
|
}
|
||||||
|
|
||||||
void InnerWidget::setHashtagPressed(int pressed) {
|
void InnerWidget::setHashtagPressed(int pressed) {
|
||||||
if (base::in_range(_hashtagPressed, 0, _hashtagResults.size())) {
|
if (base::in_range(_hashtagPressed, 0, _hashtagResults.size())) {
|
||||||
_hashtagResults[_hashtagPressed]->row.stopLastRipple();
|
_hashtagResults[_hashtagPressed]->row.stopLastRipple();
|
||||||
|
@ -1656,7 +1700,7 @@ void InnerWidget::dialogRowReplaced(
|
||||||
_selected = newRow;
|
_selected = newRow;
|
||||||
}
|
}
|
||||||
if (_pressed == oldRow) {
|
if (_pressed == oldRow) {
|
||||||
setPressed(newRow);
|
setPressed(newRow, _pressedTopicJump);
|
||||||
}
|
}
|
||||||
if (_dragging == oldRow) {
|
if (_dragging == oldRow) {
|
||||||
if (newRow) {
|
if (newRow) {
|
||||||
|
@ -1704,7 +1748,7 @@ void InnerWidget::handleChatListEntryRefreshes() {
|
||||||
_selected = nullptr;
|
_selected = nullptr;
|
||||||
}
|
}
|
||||||
if (_pressed && _pressed->key() == key) {
|
if (_pressed && _pressed->key() == key) {
|
||||||
setPressed(nullptr);
|
clearPressed();
|
||||||
}
|
}
|
||||||
const auto i = ranges::find(
|
const auto i = ranges::find(
|
||||||
_filterResults,
|
_filterResults,
|
||||||
|
@ -3147,7 +3191,9 @@ ChosenRow InnerWidget::computeChosenRow() const {
|
||||||
return ChosenRow();
|
return ChosenRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InnerWidget::chooseRow(Qt::KeyboardModifiers modifiers) {
|
bool InnerWidget::chooseRow(
|
||||||
|
Qt::KeyboardModifiers modifiers,
|
||||||
|
MsgId pressedTopicRootId) {
|
||||||
if (chooseCollapsedRow()) {
|
if (chooseCollapsedRow()) {
|
||||||
return true;
|
return true;
|
||||||
} else if (chooseHashtag()) {
|
} else if (chooseHashtag()) {
|
||||||
|
@ -3161,11 +3207,20 @@ bool InnerWidget::chooseRow(Qt::KeyboardModifiers modifiers) {
|
||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
};
|
};
|
||||||
const auto chosen = modifyChosenRow(computeChosenRow(), modifiers);
|
auto chosen = modifyChosenRow(computeChosenRow(), modifiers);
|
||||||
if (chosen.key) {
|
if (chosen.key) {
|
||||||
if (IsServerMsgId(chosen.message.fullId.msg)) {
|
if (IsServerMsgId(chosen.message.fullId.msg)) {
|
||||||
session().local().saveRecentSearchHashtags(_filter);
|
session().local().saveRecentSearchHashtags(_filter);
|
||||||
}
|
}
|
||||||
|
if (pressedTopicRootId && !chosen.message.fullId) {
|
||||||
|
const auto history = chosen.key.history();
|
||||||
|
if (history->peer->isForum()) {
|
||||||
|
chosen.message.fullId = {
|
||||||
|
history->peer->id,
|
||||||
|
pressedTopicRootId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
_chosenRow.fire_copy(chosen);
|
_chosenRow.fire_copy(chosen);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,9 @@ public:
|
||||||
void refreshEmptyLabel();
|
void refreshEmptyLabel();
|
||||||
void resizeEmptyLabel();
|
void resizeEmptyLabel();
|
||||||
|
|
||||||
bool chooseRow(Qt::KeyboardModifiers modifiers = {});
|
bool chooseRow(
|
||||||
|
Qt::KeyboardModifiers modifiers = {},
|
||||||
|
MsgId pressedTopicRootId = {});
|
||||||
|
|
||||||
void scrollToEntry(const RowDescriptor &entry);
|
void scrollToEntry(const RowDescriptor &entry);
|
||||||
|
|
||||||
|
@ -240,7 +242,8 @@ private:
|
||||||
void scrollToItem(int top, int height);
|
void scrollToItem(int top, int height);
|
||||||
void scrollToDefaultSelected();
|
void scrollToDefaultSelected();
|
||||||
void setCollapsedPressed(int pressed);
|
void setCollapsedPressed(int pressed);
|
||||||
void setPressed(Row *pressed);
|
void setPressed(Row *pressed, bool pressedTopicJump);
|
||||||
|
void clearPressed();
|
||||||
void setHashtagPressed(int pressed);
|
void setHashtagPressed(int pressed);
|
||||||
void setFilteredPressed(int pressed);
|
void setFilteredPressed(int pressed);
|
||||||
void setPeerSearchPressed(int pressed);
|
void setPeerSearchPressed(int pressed);
|
||||||
|
@ -404,6 +407,9 @@ private:
|
||||||
bool _skipTopDialog = false;
|
bool _skipTopDialog = false;
|
||||||
Row *_selected = nullptr;
|
Row *_selected = nullptr;
|
||||||
Row *_pressed = nullptr;
|
Row *_pressed = nullptr;
|
||||||
|
MsgId _pressedTopicJumpRootId;
|
||||||
|
bool _selectedTopicJump = false;
|
||||||
|
bool _pressedTopicJump = false;
|
||||||
|
|
||||||
Row *_dragging = nullptr;
|
Row *_dragging = nullptr;
|
||||||
int _draggingIndex = -1;
|
int _draggingIndex = -1;
|
||||||
|
|
|
@ -35,15 +35,30 @@ void BasicRow::addRipple(
|
||||||
QSize size,
|
QSize size,
|
||||||
Fn<void()> updateCallback) {
|
Fn<void()> updateCallback) {
|
||||||
if (!_ripple) {
|
if (!_ripple) {
|
||||||
auto mask = Ui::RippleAnimation::RectMask(size);
|
addRippleWithMask(
|
||||||
_ripple = std::make_unique<Ui::RippleAnimation>(
|
origin,
|
||||||
st::dialogsRipple,
|
Ui::RippleAnimation::RectMask(size),
|
||||||
std::move(mask),
|
|
||||||
std::move(updateCallback));
|
std::move(updateCallback));
|
||||||
|
} else {
|
||||||
|
_ripple->add(origin);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BasicRow::addRippleWithMask(
|
||||||
|
QPoint origin,
|
||||||
|
QImage mask,
|
||||||
|
Fn<void()> updateCallback) {
|
||||||
|
_ripple = std::make_unique<Ui::RippleAnimation>(
|
||||||
|
st::dialogsRipple,
|
||||||
|
std::move(mask),
|
||||||
|
std::move(updateCallback));
|
||||||
_ripple->add(origin);
|
_ripple->add(origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BasicRow::clearRipple() {
|
||||||
|
_ripple = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void BasicRow::stopLastRipple() {
|
void BasicRow::stopLastRipple() {
|
||||||
if (_ripple) {
|
if (_ripple) {
|
||||||
_ripple->lastStop();
|
_ripple->lastStop();
|
||||||
|
@ -102,10 +117,11 @@ uint64 Row::sortKey(FilterId filterId) const {
|
||||||
void Row::setCornerBadgeShown(
|
void Row::setCornerBadgeShown(
|
||||||
bool shown,
|
bool shown,
|
||||||
Fn<void()> updateCallback) const {
|
Fn<void()> updateCallback) const {
|
||||||
if (_cornerBadgeShown == shown) {
|
const auto value = (shown ? 1 : 0);
|
||||||
|
if (_cornerBadgeShown == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const_cast<Row*>(this)->_cornerBadgeShown = shown;
|
const_cast<Row*>(this)->_cornerBadgeShown = value;
|
||||||
if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) {
|
if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) {
|
||||||
_cornerBadgeUserpic->animation.change(
|
_cornerBadgeUserpic->animation.change(
|
||||||
_cornerBadgeShown ? 1. : 0.,
|
_cornerBadgeShown ? 1. : 0.,
|
||||||
|
@ -279,6 +295,46 @@ void Row::paintUserpic(
|
||||||
p.setOpacity(1.);
|
p.setOpacity(1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Row::lookupIsInTopicJump(int x, int y) const {
|
||||||
|
const auto history = this->history();
|
||||||
|
return history && history->lastItemDialogsView().isInTopicJump(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::stopLastRipple() {
|
||||||
|
BasicRow::stopLastRipple();
|
||||||
|
const auto history = this->history();
|
||||||
|
const auto view = history ? &history->lastItemDialogsView() : nullptr;
|
||||||
|
if (view) {
|
||||||
|
view->stopLastRipple();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::addTopicJumpRipple(
|
||||||
|
QPoint origin,
|
||||||
|
not_null<Ui::TopicJumpCache*> topicJumpCache,
|
||||||
|
Fn<void()> updateCallback) {
|
||||||
|
const auto history = this->history();
|
||||||
|
const auto view = history ? &history->lastItemDialogsView() : nullptr;
|
||||||
|
if (view) {
|
||||||
|
view->addTopicJumpRipple(
|
||||||
|
origin,
|
||||||
|
topicJumpCache,
|
||||||
|
std::move(updateCallback));
|
||||||
|
_topicJumpRipple = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::clearTopicJumpRipple() {
|
||||||
|
if (_topicJumpRipple) {
|
||||||
|
clearRipple();
|
||||||
|
_topicJumpRipple = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Row::topicJumpRipple() const {
|
||||||
|
return _topicJumpRipple != 0;
|
||||||
|
}
|
||||||
|
|
||||||
FakeRow::FakeRow(
|
FakeRow::FakeRow(
|
||||||
Key searchInChat,
|
Key searchInChat,
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
|
|
|
@ -33,6 +33,7 @@ using namespace ::Ui;
|
||||||
class RowPainter;
|
class RowPainter;
|
||||||
class VideoUserpic;
|
class VideoUserpic;
|
||||||
struct PaintContext;
|
struct PaintContext;
|
||||||
|
struct TopicJumpCache;
|
||||||
} // namespace Dialogs::Ui
|
} // namespace Dialogs::Ui
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
@ -52,7 +53,12 @@ public:
|
||||||
const Ui::PaintContext &context) const;
|
const Ui::PaintContext &context) const;
|
||||||
|
|
||||||
void addRipple(QPoint origin, QSize size, Fn<void()> updateCallback);
|
void addRipple(QPoint origin, QSize size, Fn<void()> updateCallback);
|
||||||
void stopLastRipple();
|
virtual void stopLastRipple();
|
||||||
|
void addRippleWithMask(
|
||||||
|
QPoint origin,
|
||||||
|
QImage mask,
|
||||||
|
Fn<void()> updateCallback);
|
||||||
|
void clearRipple();
|
||||||
|
|
||||||
void paintRipple(
|
void paintRipple(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
|
@ -96,6 +102,15 @@ public:
|
||||||
History *historyForCornerBadge,
|
History *historyForCornerBadge,
|
||||||
const Ui::PaintContext &context) const final override;
|
const Ui::PaintContext &context) const final override;
|
||||||
|
|
||||||
|
[[nodiscard]] bool lookupIsInTopicJump(int x, int y) const;
|
||||||
|
void stopLastRipple() override;
|
||||||
|
void addTopicJumpRipple(
|
||||||
|
QPoint origin,
|
||||||
|
not_null<Ui::TopicJumpCache*> topicJumpCache,
|
||||||
|
Fn<void()> updateCallback);
|
||||||
|
void clearTopicJumpRipple();
|
||||||
|
[[nodiscard]] bool topicJumpRipple() const;
|
||||||
|
|
||||||
[[nodiscard]] Key key() const {
|
[[nodiscard]] Key key() const {
|
||||||
return _id;
|
return _id;
|
||||||
}
|
}
|
||||||
|
@ -149,8 +164,9 @@ private:
|
||||||
mutable std::unique_ptr<CornerBadgeUserpic> _cornerBadgeUserpic;
|
mutable std::unique_ptr<CornerBadgeUserpic> _cornerBadgeUserpic;
|
||||||
int _top = 0;
|
int _top = 0;
|
||||||
int _height = 0;
|
int _height = 0;
|
||||||
int _index = 0;
|
int _index : 30 = 0;
|
||||||
bool _cornerBadgeShown = false;
|
int _cornerBadgeShown : 1 = 0;
|
||||||
|
int _topicJumpRipple : 1 = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -415,8 +415,19 @@ void Widget::chosenRow(const ChosenRow &row) {
|
||||||
const auto openSearchResult = !controller()->selectingPeer()
|
const auto openSearchResult = !controller()->selectingPeer()
|
||||||
&& row.filteredRow;
|
&& row.filteredRow;
|
||||||
const auto history = row.key.history();
|
const auto history = row.key.history();
|
||||||
if (const auto topic = row.key.topic()) {
|
const auto topicJump = history
|
||||||
controller()->content()->chooseThread(topic, row.message.fullId.msg);
|
? history->peer->forumTopicFor(row.message.fullId.msg)
|
||||||
|
: nullptr;
|
||||||
|
if (topicJump) {
|
||||||
|
controller()->openForum(history->peer->asChannel());
|
||||||
|
controller()->content()->chooseThread(
|
||||||
|
topicJump,
|
||||||
|
ShowAtUnreadMsgId);
|
||||||
|
return;
|
||||||
|
} else if (const auto topic = row.key.topic()) {
|
||||||
|
controller()->content()->chooseThread(
|
||||||
|
topic,
|
||||||
|
row.message.fullId.msg);
|
||||||
} else if (history && history->peer->isForum() && !row.message.fullId) {
|
} else if (history && history->peer->isForum() && !row.message.fullId) {
|
||||||
controller()->openForum(history->peer->asChannel());
|
controller()->openForum(history->peer->asChannel());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -232,6 +232,7 @@ enum class Flag {
|
||||||
SavedMessages = 0x08,
|
SavedMessages = 0x08,
|
||||||
RepliesMessages = 0x10,
|
RepliesMessages = 0x10,
|
||||||
AllowUserOnline = 0x20,
|
AllowUserOnline = 0x20,
|
||||||
|
TopicJumpRipple = 0x40,
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(Flag) { return true; }
|
inline constexpr bool is_flag_type(Flag) { return true; }
|
||||||
|
|
||||||
|
@ -264,11 +265,13 @@ void PaintRow(
|
||||||
: context.selected
|
: context.selected
|
||||||
? st::dialogsBgOver
|
? st::dialogsBgOver
|
||||||
: st::dialogsBg;
|
: st::dialogsBg;
|
||||||
auto ripple = context.active
|
|
||||||
? st::dialogsRippleBgActive
|
|
||||||
: st::dialogsRippleBg;
|
|
||||||
p.fillRect(geometry, bg);
|
p.fillRect(geometry, bg);
|
||||||
row->paintRipple(p, 0, 0, context.width, &ripple->c);
|
if (!(flags & Flag::TopicJumpRipple)) {
|
||||||
|
auto ripple = context.active
|
||||||
|
? st::dialogsRippleBgActive
|
||||||
|
: st::dialogsRippleBg;
|
||||||
|
row->paintRipple(p, 0, 0, context.width, &ripple->c);
|
||||||
|
}
|
||||||
|
|
||||||
const auto history = entry->asHistory();
|
const auto history = entry->asHistory();
|
||||||
const auto thread = entry->asThread();
|
const auto thread = entry->asThread();
|
||||||
|
@ -883,7 +886,8 @@ void RowPainter::Paint(
|
||||||
const auto allowUserOnline = !context.narrow || badgesState.empty();
|
const auto allowUserOnline = !context.narrow || badgesState.empty();
|
||||||
const auto flags = (allowUserOnline ? Flag::AllowUserOnline : Flag(0))
|
const auto flags = (allowUserOnline ? Flag::AllowUserOnline : Flag(0))
|
||||||
| (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0))
|
| (peer && peer->isSelf() ? Flag::SavedMessages : Flag(0))
|
||||||
| (peer && peer->isRepliesChat() ? Flag::RepliesMessages : Flag(0));
|
| (peer && peer->isRepliesChat() ? Flag::RepliesMessages : Flag(0))
|
||||||
|
| (row->topicJumpRipple() ? Flag::TopicJumpRipple : Flag(0));
|
||||||
const auto paintItemCallback = [&](int nameleft, int namewidth) {
|
const auto paintItemCallback = [&](int nameleft, int namewidth) {
|
||||||
const auto texttop = context.st->textTop;
|
const auto texttop = context.st->textTop;
|
||||||
const auto availableWidth = PaintWideCounter(
|
const auto availableWidth = PaintWideCounter(
|
||||||
|
|
|
@ -48,6 +48,7 @@ struct TopicJumpCorners {
|
||||||
struct TopicJumpCache {
|
struct TopicJumpCache {
|
||||||
TopicJumpCorners corners;
|
TopicJumpCorners corners;
|
||||||
TopicJumpCorners over;
|
TopicJumpCorners over;
|
||||||
|
TopicJumpCorners selected;
|
||||||
TopicJumpCorners rippleMask;
|
TopicJumpCorners rippleMask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ struct PaintContext {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
bool active = false;
|
bool active = false;
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
|
bool topicJumpSelected = false;
|
||||||
bool paused = false;
|
bool paused = false;
|
||||||
bool search = false;
|
bool search = false;
|
||||||
bool narrow = false;
|
bool narrow = false;
|
||||||
|
|
|
@ -200,6 +200,28 @@ void MessageView::prepare(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MessageView::isInTopicJump(int x, int y) const {
|
||||||
|
return _topics && _topics->isInTopicJumpArea(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageView::addTopicJumpRipple(
|
||||||
|
QPoint origin,
|
||||||
|
not_null<TopicJumpCache*> topicJumpCache,
|
||||||
|
Fn<void()> updateCallback) {
|
||||||
|
if (_topics) {
|
||||||
|
_topics->addTopicJumpRipple(
|
||||||
|
origin,
|
||||||
|
topicJumpCache,
|
||||||
|
std::move(updateCallback));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageView::stopLastRipple() {
|
||||||
|
if (_topics) {
|
||||||
|
_topics->stopLastRipple();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int MessageView::countWidth() const {
|
int MessageView::countWidth() const {
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
if (!_senderCache.isEmpty()) {
|
if (!_senderCache.isEmpty()) {
|
||||||
|
@ -248,6 +270,8 @@ void MessageView::paint(
|
||||||
const auto jump1 = checkJump ? _topics->jumpToTopicWidth() : 0;
|
const auto jump1 = checkJump ? _topics->jumpToTopicWidth() : 0;
|
||||||
if (jump1) {
|
if (jump1) {
|
||||||
paintJumpToLast(p, rect, context, jump1);
|
paintJumpToLast(p, rect, context, jump1);
|
||||||
|
} else if (_topics) {
|
||||||
|
_topics->clearTopicJumpGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (withTopic) {
|
if (withTopic) {
|
||||||
|
@ -320,9 +344,11 @@ void MessageView::paintJumpToLast(
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
int width1) const {
|
int width1) const {
|
||||||
if (!context.topicJumpCache) {
|
if (!context.topicJumpCache) {
|
||||||
|
_topics->clearTopicJumpGeometry();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FillJumpToLastBg(p, {
|
const auto width2 = countWidth() + st::forumDialogJumpArrowSkip;
|
||||||
|
const auto geometry = FillJumpToLastBg(p, {
|
||||||
.st = context.st,
|
.st = context.st,
|
||||||
.corners = (context.selected
|
.corners = (context.selected
|
||||||
? &context.topicJumpCache->over
|
? &context.topicJumpCache->over
|
||||||
|
@ -332,97 +358,23 @@ void MessageView::paintJumpToLast(
|
||||||
? st::dialogsRippleBg
|
? st::dialogsRippleBg
|
||||||
: st::dialogsBgOver),
|
: st::dialogsBgOver),
|
||||||
.width1 = width1,
|
.width1 = width1,
|
||||||
.width2 = countWidth() + st::forumDialogJumpArrowSkip,
|
.width2 = width2,
|
||||||
});
|
});
|
||||||
}
|
if (context.topicJumpSelected) {
|
||||||
|
p.setOpacity(0.1);
|
||||||
void FillJumpToLastBg(QPainter &p, JumpToLastBg context) {
|
FillJumpToLastPrepared(p, {
|
||||||
const auto availableWidth = context.geometry.width();
|
.st = context.st,
|
||||||
const auto use1 = std::min(context.width1, availableWidth);
|
.corners = &context.topicJumpCache->selected,
|
||||||
const auto use2 = std::min(context.width2, availableWidth);
|
.bg = st::dialogsTextFg,
|
||||||
const auto padding = st::forumDialogJumpPadding;
|
.prepared = geometry,
|
||||||
const auto radius = st::forumDialogJumpRadius;
|
|
||||||
const auto &bg = context.bg;
|
|
||||||
auto &normal = context.corners->normal;
|
|
||||||
auto &inverted = context.corners->inverted;
|
|
||||||
auto &small = context.corners->small;
|
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
p.setBrush(bg);
|
|
||||||
const auto origin = context.geometry.topLeft();
|
|
||||||
const auto delta = std::abs(use1 - use2);
|
|
||||||
if (delta <= context.st->topicsSkip / 2) {
|
|
||||||
if (normal.p[0].isNull()) {
|
|
||||||
normal = Ui::PrepareCornerPixmaps(radius, bg);
|
|
||||||
}
|
|
||||||
const auto w = std::max(use1, use2);
|
|
||||||
const auto h = context.st->topicsHeight + st::normalFont->height;
|
|
||||||
const auto fill = QRect(origin, QSize(w, h));
|
|
||||||
Ui::FillRoundRect(p, fill.marginsAdded(padding), bg, normal);
|
|
||||||
} else {
|
|
||||||
const auto h1 = context.st->topicsHeight;
|
|
||||||
const auto h2 = st::normalFont->height;
|
|
||||||
const auto hmin = std::min(h1, h2);
|
|
||||||
const auto wantedInvertedRadius = hmin - radius;
|
|
||||||
const auto invertedr = std::min(wantedInvertedRadius, delta / 2);
|
|
||||||
const auto smallr = std::min(radius, delta - invertedr);
|
|
||||||
const auto smallkey = (use1 < use2) ? smallr : (-smallr);
|
|
||||||
if (normal.p[0].isNull()) {
|
|
||||||
normal = Ui::PrepareCornerPixmaps(radius, bg);
|
|
||||||
}
|
|
||||||
if (inverted.p[0].isNull()
|
|
||||||
|| context.corners->invertedRadius != invertedr) {
|
|
||||||
context.corners->invertedRadius = invertedr;
|
|
||||||
inverted = Ui::PrepareInvertedCornerPixmaps(invertedr, bg);
|
|
||||||
}
|
|
||||||
if (smallr != radius
|
|
||||||
&& (small.isNull() || context.corners->smallKey != smallkey)) {
|
|
||||||
context.corners->smallKey = smallr;
|
|
||||||
auto pixmaps = Ui::PrepareCornerPixmaps(smallr, bg);
|
|
||||||
small = pixmaps.p[(use1 < use2) ? 1 : 3];
|
|
||||||
}
|
|
||||||
const auto rect1 = QRect(origin, QSize(use1, h1));
|
|
||||||
auto no1 = normal;
|
|
||||||
no1.p[2] = QPixmap();
|
|
||||||
if (use1 < use2) {
|
|
||||||
no1.p[3] = QPixmap();
|
|
||||||
} else if (smallr != radius) {
|
|
||||||
no1.p[3] = small;
|
|
||||||
}
|
|
||||||
auto fill1 = rect1.marginsAdded({
|
|
||||||
padding.left(),
|
|
||||||
padding.top(),
|
|
||||||
padding.right(),
|
|
||||||
(use1 < use2 ? -padding.top() : padding.bottom()),
|
|
||||||
});
|
});
|
||||||
Ui::FillRoundRect(p, fill1, bg, no1);
|
p.setOpacity(1.);
|
||||||
if (use1 < use2) {
|
}
|
||||||
p.drawPixmap(
|
if (!_topics->changeTopicJumpGeometry(geometry)) {
|
||||||
fill1.x() + fill1.width(),
|
auto color = st::dialogsTextFg->c;
|
||||||
fill1.y() + fill1.height() - invertedr,
|
color.setAlpha(color.alpha() / 10);
|
||||||
inverted.p[3]);
|
if (color.alpha() > 0) {
|
||||||
}
|
_topics->paintRipple(p, 0, 0, context.width, &color);
|
||||||
const auto add = QPoint(0, h1);
|
|
||||||
const auto rect2 = QRect(origin + add, QSize(use2, h2));
|
|
||||||
const auto fill2 = rect2.marginsAdded({
|
|
||||||
padding.left(),
|
|
||||||
(use2 < use1 ? -padding.bottom() : padding.top()),
|
|
||||||
padding.right(),
|
|
||||||
padding.bottom(),
|
|
||||||
});
|
|
||||||
auto no2 = normal;
|
|
||||||
no2.p[0] = QPixmap();
|
|
||||||
if (use2 < use1) {
|
|
||||||
no2.p[1] = QPixmap();
|
|
||||||
} else if (smallr != radius) {
|
|
||||||
no2.p[1] = small;
|
|
||||||
}
|
|
||||||
Ui::FillRoundRect(p, fill2, bg, no2);
|
|
||||||
if (use2 < use1) {
|
|
||||||
p.drawPixmap(
|
|
||||||
fill2.x() + fill2.width(),
|
|
||||||
fill2.y(),
|
|
||||||
inverted.p[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,8 @@ namespace Dialogs::Ui {
|
||||||
using namespace ::Ui;
|
using namespace ::Ui;
|
||||||
|
|
||||||
struct PaintContext;
|
struct PaintContext;
|
||||||
|
struct TopicJumpCache;
|
||||||
class TopicsView;
|
class TopicsView;
|
||||||
struct TopicJumpCorners;
|
|
||||||
|
|
||||||
[[nodiscard]] TextWithEntities DialogsPreviewText(TextWithEntities text);
|
[[nodiscard]] TextWithEntities DialogsPreviewText(TextWithEntities text);
|
||||||
|
|
||||||
|
@ -66,6 +66,13 @@ public:
|
||||||
const QRect &geometry,
|
const QRect &geometry,
|
||||||
const PaintContext &context) const;
|
const PaintContext &context) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isInTopicJump(int x, int y) const;
|
||||||
|
void addTopicJumpRipple(
|
||||||
|
QPoint origin,
|
||||||
|
not_null<TopicJumpCache*> topicJumpCache,
|
||||||
|
Fn<void()> updateCallback);
|
||||||
|
void stopLastRipple();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct LoadingContext;
|
struct LoadingContext;
|
||||||
|
|
||||||
|
@ -90,14 +97,4 @@ private:
|
||||||
const QString &sender,
|
const QString &sender,
|
||||||
TextWithEntities topic);
|
TextWithEntities topic);
|
||||||
|
|
||||||
struct JumpToLastBg {
|
|
||||||
not_null<const style::DialogRow*> st;
|
|
||||||
not_null<TopicJumpCorners*> corners;
|
|
||||||
QRect geometry;
|
|
||||||
const style::color &bg;
|
|
||||||
int width1 = 0;
|
|
||||||
int width2 = 0;
|
|
||||||
};
|
|
||||||
void FillJumpToLastBg(QPainter &p, JumpToLastBg context);
|
|
||||||
|
|
||||||
} // namespace Dialogs::Ui
|
} // namespace Dialogs::Ui
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
namespace Dialogs::Ui {
|
namespace Dialogs::Ui {
|
||||||
|
@ -142,4 +143,198 @@ void TopicsView::paint(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TopicsView::changeTopicJumpGeometry(JumpToLastGeometry geometry) {
|
||||||
|
if (_lastTopicJumpGeometry != geometry) {
|
||||||
|
_lastTopicJumpGeometry = geometry;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopicsView::clearTopicJumpGeometry() {
|
||||||
|
changeTopicJumpGeometry({});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TopicsView::isInTopicJumpArea(int x, int y) const {
|
||||||
|
return _lastTopicJumpGeometry.area1.contains(x, y)
|
||||||
|
|| _lastTopicJumpGeometry.area2.contains(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopicsView::addTopicJumpRipple(
|
||||||
|
QPoint origin,
|
||||||
|
not_null<TopicJumpCache*> topicJumpCache,
|
||||||
|
Fn<void()> updateCallback) {
|
||||||
|
auto mask = topicJumpRippleMask(topicJumpCache);
|
||||||
|
if (mask.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ripple = std::make_unique<Ui::RippleAnimation>(
|
||||||
|
st::dialogsRipple,
|
||||||
|
std::move(mask),
|
||||||
|
std::move(updateCallback));
|
||||||
|
_ripple->add(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopicsView::stopLastRipple() {
|
||||||
|
if (_ripple) {
|
||||||
|
_ripple->lastStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopicsView::paintRipple(
|
||||||
|
QPainter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int outerWidth,
|
||||||
|
const QColor *colorOverride) const {
|
||||||
|
if (_ripple) {
|
||||||
|
_ripple->paint(p, x, y, outerWidth, colorOverride);
|
||||||
|
if (_ripple->empty()) {
|
||||||
|
_ripple.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage TopicsView::topicJumpRippleMask(
|
||||||
|
not_null<TopicJumpCache*> topicJumpCache) const {
|
||||||
|
const auto &st = st::forumDialogRow;
|
||||||
|
const auto area1 = _lastTopicJumpGeometry.area1;
|
||||||
|
if (area1.isEmpty()) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
const auto area2 = _lastTopicJumpGeometry.area2;
|
||||||
|
const auto drawer = [&](QPainter &p) {
|
||||||
|
const auto white = style::complex_color([] { return Qt::white; });
|
||||||
|
// p.setOpacity(.1);
|
||||||
|
FillJumpToLastPrepared(p, {
|
||||||
|
.st = &st,
|
||||||
|
.corners = &topicJumpCache->rippleMask,
|
||||||
|
.bg = white.color(),
|
||||||
|
.prepared = _lastTopicJumpGeometry,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return Ui::RippleAnimation::MaskByDrawer(
|
||||||
|
QRect(0, 0, 1, 1).united(area1).united(area2).size(),
|
||||||
|
false,
|
||||||
|
drawer);
|
||||||
|
}
|
||||||
|
|
||||||
|
JumpToLastGeometry FillJumpToLastBg(QPainter &p, JumpToLastBg context) {
|
||||||
|
const auto availableWidth = context.geometry.width();
|
||||||
|
const auto use1 = std::min(context.width1, availableWidth);
|
||||||
|
const auto use2 = std::min(context.width2, availableWidth);
|
||||||
|
const auto padding = st::forumDialogJumpPadding;
|
||||||
|
const auto radius = st::forumDialogJumpRadius;
|
||||||
|
const auto origin = context.geometry.topLeft();
|
||||||
|
const auto delta = std::abs(use1 - use2);
|
||||||
|
if (delta <= context.st->topicsSkip / 2) {
|
||||||
|
const auto w = std::max(use1, use2);
|
||||||
|
const auto h = context.st->topicsHeight + st::normalFont->height;
|
||||||
|
const auto fill = QRect(origin, QSize(w, h));
|
||||||
|
const auto full = fill.marginsAdded(padding);
|
||||||
|
auto result = JumpToLastGeometry{ full };
|
||||||
|
FillJumpToLastPrepared(p, {
|
||||||
|
.st = context.st,
|
||||||
|
.corners = context.corners,
|
||||||
|
.bg = context.bg,
|
||||||
|
.prepared = result,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const auto h1 = context.st->topicsHeight;
|
||||||
|
const auto h2 = st::normalFont->height;
|
||||||
|
const auto rect1 = QRect(origin, QSize(use1, h1));
|
||||||
|
const auto fill1 = rect1.marginsAdded({
|
||||||
|
padding.left(),
|
||||||
|
padding.top(),
|
||||||
|
padding.right(),
|
||||||
|
(use1 < use2 ? -padding.top() : padding.bottom()),
|
||||||
|
});
|
||||||
|
const auto add = QPoint(0, h1);
|
||||||
|
const auto rect2 = QRect(origin + add, QSize(use2, h2));
|
||||||
|
const auto fill2 = rect2.marginsAdded({
|
||||||
|
padding.left(),
|
||||||
|
(use2 < use1 ? -padding.bottom() : padding.top()),
|
||||||
|
padding.right(),
|
||||||
|
padding.bottom(),
|
||||||
|
});
|
||||||
|
auto result = JumpToLastGeometry{ fill1, fill2 };
|
||||||
|
FillJumpToLastPrepared(p, {
|
||||||
|
.st = context.st,
|
||||||
|
.corners = context.corners,
|
||||||
|
.bg = context.bg,
|
||||||
|
.prepared = result,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillJumpToLastPrepared(QPainter &p, JumpToLastPrepared context) {
|
||||||
|
auto &normal = context.corners->normal;
|
||||||
|
auto &inverted = context.corners->inverted;
|
||||||
|
auto &small = context.corners->small;
|
||||||
|
const auto radius = st::forumDialogJumpRadius;
|
||||||
|
const auto &bg = context.bg;
|
||||||
|
const auto area1 = context.prepared.area1;
|
||||||
|
const auto area2 = context.prepared.area2;
|
||||||
|
if (area2.isNull()) {
|
||||||
|
if (normal.p[0].isNull()) {
|
||||||
|
normal = Ui::PrepareCornerPixmaps(radius, bg);
|
||||||
|
}
|
||||||
|
Ui::FillRoundRect(p, area1, bg, normal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto width1 = area1.width();
|
||||||
|
const auto width2 = area2.width();
|
||||||
|
const auto delta = std::abs(width1 - width2);
|
||||||
|
const auto h1 = context.st->topicsHeight;
|
||||||
|
const auto h2 = st::normalFont->height;
|
||||||
|
const auto hmin = std::min(h1, h2);
|
||||||
|
const auto wantedInvertedRadius = hmin - radius;
|
||||||
|
const auto invertedr = std::min(wantedInvertedRadius, delta / 2);
|
||||||
|
const auto smallr = std::min(radius, delta - invertedr);
|
||||||
|
const auto smallkey = (width1 < width2) ? smallr : (-smallr);
|
||||||
|
if (normal.p[0].isNull()) {
|
||||||
|
normal = Ui::PrepareCornerPixmaps(radius, bg);
|
||||||
|
}
|
||||||
|
if (inverted.p[0].isNull()
|
||||||
|
|| context.corners->invertedRadius != invertedr) {
|
||||||
|
context.corners->invertedRadius = invertedr;
|
||||||
|
inverted = Ui::PrepareInvertedCornerPixmaps(invertedr, bg);
|
||||||
|
}
|
||||||
|
if (smallr != radius
|
||||||
|
&& (small.isNull() || context.corners->smallKey != smallkey)) {
|
||||||
|
context.corners->smallKey = smallr;
|
||||||
|
auto pixmaps = Ui::PrepareCornerPixmaps(smallr, bg);
|
||||||
|
small = pixmaps.p[(width1 < width2) ? 1 : 3];
|
||||||
|
}
|
||||||
|
auto no1 = normal;
|
||||||
|
no1.p[2] = QPixmap();
|
||||||
|
if (width1 < width2) {
|
||||||
|
no1.p[3] = QPixmap();
|
||||||
|
} else if (smallr != radius) {
|
||||||
|
no1.p[3] = small;
|
||||||
|
}
|
||||||
|
Ui::FillRoundRect(p, area1, bg, no1);
|
||||||
|
if (width1 < width2) {
|
||||||
|
p.drawPixmap(
|
||||||
|
area1.x() + width1,
|
||||||
|
area1.y() + area1.height() - invertedr,
|
||||||
|
inverted.p[3]);
|
||||||
|
}
|
||||||
|
auto no2 = normal;
|
||||||
|
no2.p[0] = QPixmap();
|
||||||
|
if (width2 < width1) {
|
||||||
|
no2.p[1] = QPixmap();
|
||||||
|
} else if (smallr != radius) {
|
||||||
|
no2.p[1] = small;
|
||||||
|
}
|
||||||
|
Ui::FillRoundRect(p, area2, bg, no2);
|
||||||
|
if (width2 < width1) {
|
||||||
|
p.drawPixmap(
|
||||||
|
area2.x() + width2,
|
||||||
|
area2.y(),
|
||||||
|
inverted.p[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dialogs::Ui
|
} // namespace Dialogs::Ui
|
||||||
|
|
|
@ -19,6 +19,7 @@ class ForumTopic;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
class RippleAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Dialogs::Ui {
|
namespace Dialogs::Ui {
|
||||||
|
@ -26,6 +27,34 @@ namespace Dialogs::Ui {
|
||||||
using namespace ::Ui;
|
using namespace ::Ui;
|
||||||
|
|
||||||
struct PaintContext;
|
struct PaintContext;
|
||||||
|
struct TopicJumpCache;
|
||||||
|
struct TopicJumpCorners;
|
||||||
|
|
||||||
|
struct JumpToLastBg {
|
||||||
|
not_null<const style::DialogRow*> st;
|
||||||
|
not_null<TopicJumpCorners*> corners;
|
||||||
|
QRect geometry;
|
||||||
|
const style::color &bg;
|
||||||
|
int width1 = 0;
|
||||||
|
int width2 = 0;
|
||||||
|
};
|
||||||
|
struct JumpToLastGeometry {
|
||||||
|
QRect area1;
|
||||||
|
QRect area2;
|
||||||
|
|
||||||
|
friend inline auto operator<=>(
|
||||||
|
const JumpToLastGeometry&,
|
||||||
|
const JumpToLastGeometry&) = default;
|
||||||
|
};
|
||||||
|
JumpToLastGeometry FillJumpToLastBg(QPainter &p, JumpToLastBg context);
|
||||||
|
|
||||||
|
struct JumpToLastPrepared {
|
||||||
|
not_null<const style::DialogRow*> st;
|
||||||
|
not_null<TopicJumpCorners*> corners;
|
||||||
|
const style::color &bg;
|
||||||
|
const JumpToLastGeometry &prepared;
|
||||||
|
};
|
||||||
|
void FillJumpToLastPrepared(QPainter &p, JumpToLastPrepared context);
|
||||||
|
|
||||||
class TopicsView final {
|
class TopicsView final {
|
||||||
public:
|
public:
|
||||||
|
@ -46,6 +75,22 @@ public:
|
||||||
const QRect &geometry,
|
const QRect &geometry,
|
||||||
const PaintContext &context) const;
|
const PaintContext &context) const;
|
||||||
|
|
||||||
|
bool changeTopicJumpGeometry(JumpToLastGeometry geometry);
|
||||||
|
void clearTopicJumpGeometry();
|
||||||
|
[[nodiscard]] bool isInTopicJumpArea(int x, int y) const;
|
||||||
|
void addTopicJumpRipple(
|
||||||
|
QPoint origin,
|
||||||
|
not_null<TopicJumpCache*> topicJumpCache,
|
||||||
|
Fn<void()> updateCallback);
|
||||||
|
void paintRipple(
|
||||||
|
QPainter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int outerWidth,
|
||||||
|
const QColor *colorOverride) const;
|
||||||
|
void clearRipple();
|
||||||
|
void stopLastRipple();
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
return _lifetime;
|
return _lifetime;
|
||||||
}
|
}
|
||||||
|
@ -57,9 +102,15 @@ private:
|
||||||
int version = -1;
|
int version = -1;
|
||||||
bool unread = false;
|
bool unread = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] QImage topicJumpRippleMask(
|
||||||
|
not_null<TopicJumpCache*> topicJumpCache) const;
|
||||||
|
|
||||||
const not_null<Data::Forum*> _forum;
|
const not_null<Data::Forum*> _forum;
|
||||||
|
|
||||||
mutable std::vector<Title> _titles;
|
mutable std::vector<Title> _titles;
|
||||||
|
mutable std::unique_ptr<RippleAnimation> _ripple;
|
||||||
|
JumpToLastGeometry _lastTopicJumpGeometry;
|
||||||
int _version = -1;
|
int _version = -1;
|
||||||
bool _jumpToTopic = false;
|
bool _jumpToTopic = false;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue