mirror of
https://github.com/Qz3rK/tdesktop.git
synced 2026-07-03 14:15:08 +02:00
Added initial ability to reorder sub tabs for gifts collection.
This commit is contained in:
@@ -103,6 +103,10 @@ rpl::producer<QString> SubTabs::contextMenuRequests() const {
|
||||
return _contextMenuRequests.events();
|
||||
}
|
||||
|
||||
rpl::producer<SubTabs::ReorderUpdate> SubTabs::reorderUpdates() const {
|
||||
return _reorderUpdates.events();
|
||||
}
|
||||
|
||||
void SubTabs::setReorderEnabled(bool enabled) {
|
||||
_reorderEnable = enabled;
|
||||
if (enabled) {
|
||||
@@ -203,6 +207,27 @@ bool SubTabs::eventHook(QEvent *e) {
|
||||
void SubTabs::mouseMoveEvent(QMouseEvent *e) {
|
||||
const auto mousex = e->pos().x();
|
||||
const auto drag = QApplication::startDragDistance();
|
||||
|
||||
if (_reorderEnable && _reorderIndex >= 0) {
|
||||
if (_reorderState != ReorderUpdate::State::Started) {
|
||||
const auto shift = e->globalPos().x() - _reorderStart;
|
||||
if (std::abs(shift) > drag) {
|
||||
_reorderState = ReorderUpdate::State::Started;
|
||||
_reorderStart += (shift > 0) ? drag : -drag;
|
||||
_reorderDesiredIndex = _reorderIndex;
|
||||
_reorderUpdates.fire({
|
||||
_buttons[_reorderIndex].tab.id,
|
||||
_reorderIndex,
|
||||
_reorderIndex,
|
||||
_reorderState
|
||||
});
|
||||
}
|
||||
} else {
|
||||
updateReorder(e->globalPos());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_reorderEnable) {
|
||||
if (_dragx > 0) {
|
||||
_scrollAnimation.stop();
|
||||
@@ -268,12 +293,22 @@ void SubTabs::mousePressEvent(QMouseEvent *e) {
|
||||
}
|
||||
_pressed = _selected;
|
||||
_pressx = e->pos().x();
|
||||
|
||||
if (_reorderEnable && _selected >= 0 && !isIndexPinned(_selected)) {
|
||||
startReorder(_selected, e->globalPos());
|
||||
}
|
||||
}
|
||||
|
||||
void SubTabs::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (e->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_reorderEnable && _reorderIndex >= 0) {
|
||||
finishReorder();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto dragx = std::exchange(_dragx, 0);
|
||||
const auto pressed = std::exchange(_pressed, -1);
|
||||
_pressx = 0;
|
||||
@@ -306,17 +341,18 @@ void SubTabs::paintEvent(QPaintEvent *e) {
|
||||
shakeTransform(p, i, geometry.topLeft(), now);
|
||||
}
|
||||
|
||||
const auto shiftedGeometry = geometry.translated(button.shift, 0);
|
||||
if (button.active) {
|
||||
p.setBrush(st::giftBoxTabBgActive);
|
||||
p.setPen(Qt::NoPen);
|
||||
const auto radius = geometry.height() / 2.;
|
||||
p.drawRoundedRect(geometry, radius, radius);
|
||||
const auto radius = shiftedGeometry.height() / 2.;
|
||||
p.drawRoundedRect(shiftedGeometry, radius, radius);
|
||||
p.setPen(st::giftBoxTabFgActive);
|
||||
} else {
|
||||
p.setPen(st::giftBoxTabFg);
|
||||
}
|
||||
button.text.draw(p, {
|
||||
.position = geometry.marginsRemoved(padding).topLeft(),
|
||||
.position = shiftedGeometry.marginsRemoved(padding).topLeft(),
|
||||
.availableWidth = button.text.maxWidth(),
|
||||
});
|
||||
|
||||
@@ -347,6 +383,193 @@ QPoint SubTabs::scroll() const {
|
||||
return QPoint(int(base::SafeRound(_scroll)) - _fullShift, 0);
|
||||
}
|
||||
|
||||
void SubTabs::startReorder(int index, QPoint globalPos) {
|
||||
cancelReorder();
|
||||
_reorderIndex = index;
|
||||
_reorderStart = globalPos.x();
|
||||
_reorderState = ReorderUpdate::State::Cancelled;
|
||||
}
|
||||
|
||||
void SubTabs::updateReorder(QPoint globalPos) {
|
||||
if (_reorderIndex < 0 || isIndexPinned(_reorderIndex)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto shift = globalPos.x() - _reorderStart;
|
||||
auto ¤t = _buttons[_reorderIndex];
|
||||
current.shiftAnimation.stop();
|
||||
current.shift = current.finalShift = shift;
|
||||
|
||||
const auto count = _buttons.size();
|
||||
const auto currentWidth = current.geometry.width();
|
||||
const auto currentMiddle = current.geometry.x()
|
||||
+ shift
|
||||
+ currentWidth / 2;
|
||||
_reorderDesiredIndex = _reorderIndex;
|
||||
|
||||
if (shift > 0) {
|
||||
for (auto next = _reorderIndex + 1; next < count; ++next) {
|
||||
if (isIndexPinned(next)) {
|
||||
break;
|
||||
}
|
||||
const auto &e = _buttons[next];
|
||||
if (currentMiddle < e.geometry.x() + e.geometry.width() / 2) {
|
||||
moveToShift(next, 0);
|
||||
} else {
|
||||
_reorderDesiredIndex = next;
|
||||
moveToShift(next, -currentWidth);
|
||||
}
|
||||
}
|
||||
for (auto prev = _reorderIndex - 1; prev >= 0; --prev) {
|
||||
moveToShift(prev, 0);
|
||||
}
|
||||
} else {
|
||||
for (auto next = _reorderIndex + 1; next < count; ++next) {
|
||||
moveToShift(next, 0);
|
||||
}
|
||||
for (auto prev = _reorderIndex - 1; prev >= 0; --prev) {
|
||||
if (isIndexPinned(prev)) {
|
||||
break;
|
||||
}
|
||||
const auto &e = _buttons[prev];
|
||||
if (currentMiddle >= e.geometry.x() + e.geometry.width() / 2) {
|
||||
moveToShift(prev, 0);
|
||||
} else {
|
||||
_reorderDesiredIndex = prev;
|
||||
moveToShift(prev, currentWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void SubTabs::finishReorder() {
|
||||
if (_reorderIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = _reorderIndex;
|
||||
const auto result = _reorderDesiredIndex;
|
||||
const auto id = _buttons[index].tab.id;
|
||||
|
||||
if (result == index || _reorderState != ReorderUpdate::State::Started) {
|
||||
cancelReorder();
|
||||
return;
|
||||
}
|
||||
|
||||
_reorderState = ReorderUpdate::State::Applied;
|
||||
_reorderIndex = -1;
|
||||
_dragx = 0;
|
||||
_pressx = 0;
|
||||
_dragscroll = 0.;
|
||||
|
||||
auto ¤t = _buttons[index];
|
||||
const auto width = current.geometry.width();
|
||||
|
||||
if (index < result) {
|
||||
auto sum = 0;
|
||||
for (auto i = index; i < result; ++i) {
|
||||
auto &entry = _buttons[i + 1];
|
||||
entry.deltaShift += width;
|
||||
updateShift(i + 1);
|
||||
sum += entry.geometry.width();
|
||||
}
|
||||
current.finalShift -= sum;
|
||||
} else if (index > result) {
|
||||
auto sum = 0;
|
||||
for (auto i = result; i < index; ++i) {
|
||||
auto &entry = _buttons[i];
|
||||
entry.deltaShift -= width;
|
||||
updateShift(i);
|
||||
sum += entry.geometry.width();
|
||||
}
|
||||
current.finalShift += sum;
|
||||
}
|
||||
|
||||
if (!(current.finalShift + current.deltaShift)) {
|
||||
current.shift = 0;
|
||||
}
|
||||
|
||||
base::reorder(_buttons, index, result);
|
||||
|
||||
auto x = st::giftBoxTabsMargin.left();
|
||||
const auto y = st::giftBoxTabsMargin.top();
|
||||
const auto padding = st::giftBoxTabPadding;
|
||||
for (auto i = 0; i < _buttons.size(); ++i) {
|
||||
auto &button = _buttons[i];
|
||||
const auto width = button.text.maxWidth();
|
||||
const auto height = st::giftBoxTabStyle.font->height;
|
||||
const auto r = QRect(0, 0, width, height).marginsAdded(padding);
|
||||
button.geometry = QRect(QPoint(x, y), r.size());
|
||||
x += r.width() + st::giftBoxTabSkip;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < _buttons.size(); ++i) {
|
||||
moveToShift(i, 0);
|
||||
}
|
||||
|
||||
_reorderUpdates.fire(
|
||||
{ id, index, result, ReorderUpdate::State::Applied });
|
||||
}
|
||||
|
||||
void SubTabs::cancelReorder() {
|
||||
if (_reorderIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = _reorderIndex;
|
||||
const auto id = _buttons[index].tab.id;
|
||||
|
||||
if (_reorderState == ReorderUpdate::State::Started) {
|
||||
_reorderState = ReorderUpdate::State::Cancelled;
|
||||
_reorderUpdates.fire({ id, index, index, _reorderState });
|
||||
}
|
||||
|
||||
_reorderIndex = -1;
|
||||
_dragx = 0;
|
||||
_pressx = 0;
|
||||
_dragscroll = 0.;
|
||||
for (auto i = 0; i < _buttons.size(); ++i) {
|
||||
moveToShift(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SubTabs::moveToShift(int index, int shift) {
|
||||
if (index < 0 || index >= _buttons.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &entry = _buttons[index];
|
||||
if (entry.finalShift + entry.deltaShift == shift) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry.shiftAnimation.start(
|
||||
[=, this] { updateShift(index); },
|
||||
entry.finalShift,
|
||||
shift - entry.deltaShift,
|
||||
150);
|
||||
entry.finalShift = shift - entry.deltaShift;
|
||||
}
|
||||
|
||||
void SubTabs::updateShift(int index) {
|
||||
if (index < 0 || index >= _buttons.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &entry = _buttons[index];
|
||||
entry.shift = base::SafeRound(
|
||||
entry.shiftAnimation.value(entry.finalShift)
|
||||
) + entry.deltaShift;
|
||||
|
||||
if (entry.deltaShift && !entry.shiftAnimation.animating()) {
|
||||
entry.finalShift += entry.deltaShift;
|
||||
entry.deltaShift = 0;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void SubTabs::shakeTransform(
|
||||
QPainter &p,
|
||||
int index,
|
||||
|
||||
@@ -57,12 +57,28 @@ public:
|
||||
[[nodiscard]] rpl::producer<QString> activated() const;
|
||||
[[nodiscard]] rpl::producer<QString> contextMenuRequests() const;
|
||||
|
||||
struct ReorderUpdate {
|
||||
QString id;
|
||||
int oldPosition = 0;
|
||||
int newPosition = 0;
|
||||
enum class State : uchar {
|
||||
Started,
|
||||
Applied,
|
||||
Cancelled,
|
||||
} state = State::Started;
|
||||
};
|
||||
[[nodiscard]] rpl::producer<ReorderUpdate> reorderUpdates() const;
|
||||
|
||||
private:
|
||||
struct Button {
|
||||
Tab tab;
|
||||
QRect geometry;
|
||||
Text::String text;
|
||||
bool active = false;
|
||||
Ui::Animations::Simple shiftAnimation;
|
||||
int shift = 0;
|
||||
int finalShift = 0;
|
||||
int deltaShift = 0;
|
||||
};
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
@@ -80,10 +96,18 @@ private:
|
||||
void shakeTransform(QPainter &p, int index, QPoint position, crl::time now) const;
|
||||
[[nodiscard]] bool isIndexPinned(int index) const;
|
||||
|
||||
void startReorder(int index, QPoint globalPos);
|
||||
void updateReorder(QPoint globalPos);
|
||||
void finishReorder();
|
||||
void cancelReorder();
|
||||
void moveToShift(int index, int shift);
|
||||
void updateShift(int index);
|
||||
|
||||
const style::SubTabs &_st;
|
||||
std::vector<Button> _buttons;
|
||||
rpl::event_stream<QString> _activated;
|
||||
rpl::event_stream<QString> _contextMenuRequests;
|
||||
rpl::event_stream<ReorderUpdate> _reorderUpdates;
|
||||
std::optional<Qt::Orientation> _locked;
|
||||
int _dragx = 0;
|
||||
int _pressx = 0;
|
||||
@@ -106,6 +130,11 @@ private:
|
||||
};
|
||||
std::vector<PinnedInterval> _pinnedIntervals;
|
||||
|
||||
int _reorderIndex = -1;
|
||||
int _reorderStart = 0;
|
||||
int _reorderDesiredIndex = 0;
|
||||
ReorderUpdate::State _reorderState = ReorderUpdate::State::Cancelled;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
Reference in New Issue
Block a user