mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Implement jump-to-start / end by long press in CalendarBox.
This commit is contained in:
parent
15dc6064ef
commit
4414369fc8
4 changed files with 165 additions and 75 deletions
|
@ -78,7 +78,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_month_day_year" = "{month} {day}, {year}";
|
||||
"lng_month_year" = "{month} {year}";
|
||||
|
||||
"lng_calendar_beginning" = "Beginning";
|
||||
"lng_calendar_select_days" = "Select days";
|
||||
"lng_calendar_start_tip" = "Press and hold to jump to the start.";
|
||||
"lng_calendar_end_tip" = "Press and hold to jump to the end.";
|
||||
|
||||
"lng_box_ok" = "OK";
|
||||
"lng_box_done" = "Done";
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace {
|
|||
|
||||
constexpr auto kDaysInWeek = 7;
|
||||
constexpr auto kMaxDaysForScroll = kDaysInWeek * 1000;
|
||||
constexpr auto kTooltipDelay = crl::time(1000);
|
||||
constexpr auto kJumpDelay = 2 * crl::time(1000);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -26,9 +28,9 @@ class CalendarBox::Context {
|
|||
public:
|
||||
Context(QDate month, QDate highlighted);
|
||||
|
||||
void setBeginningButton(bool enabled);
|
||||
[[nodiscard]] bool hasBeginningButton() const {
|
||||
return _beginningButton;
|
||||
void setHasSelection(bool has);
|
||||
[[nodiscard]] bool hasSelection() const {
|
||||
return _hasSelection;
|
||||
}
|
||||
|
||||
void setMinDate(QDate date);
|
||||
|
@ -63,9 +65,6 @@ public:
|
|||
[[nodiscard]] bool isEnabled(int index) const {
|
||||
return (index >= _minDayIndex) && (index <= _maxDayIndex);
|
||||
}
|
||||
[[nodiscard]] bool atBeginning() const {
|
||||
return _highlighted == _min;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<QDate> monthValue() const {
|
||||
return _month.value();
|
||||
|
@ -80,7 +79,7 @@ private:
|
|||
static int DaysShiftForMonth(QDate month, QDate min);
|
||||
static int RowsCountForMonth(QDate month, QDate min, QDate max);
|
||||
|
||||
bool _beginningButton = false;
|
||||
bool _hasSelection = false;
|
||||
|
||||
rpl::variable<QDate> _month;
|
||||
QDate _min, _max;
|
||||
|
@ -102,8 +101,8 @@ CalendarBox::Context::Context(QDate month, QDate highlighted)
|
|||
showMonth(month);
|
||||
}
|
||||
|
||||
void CalendarBox::Context::setBeginningButton(bool enabled) {
|
||||
_beginningButton = enabled;
|
||||
void CalendarBox::Context::setHasSelection(bool has) {
|
||||
_hasSelection = has;
|
||||
}
|
||||
|
||||
void CalendarBox::Context::setMinDate(QDate date) {
|
||||
|
@ -245,7 +244,6 @@ public:
|
|||
|
||||
[[nodiscard]] int countMaxHeight() const;
|
||||
void setDateChosenCallback(Fn<void(QDate)> callback);
|
||||
void selectBeginning();
|
||||
|
||||
~Inner();
|
||||
|
||||
|
@ -479,10 +477,6 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) {
|
|||
_dateChosenCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void CalendarBox::Inner::selectBeginning() {
|
||||
_dateChosenCallback(_context->dateFromIndex(_context->minDayIndex()));
|
||||
}
|
||||
|
||||
CalendarBox::Inner::~Inner() = default;
|
||||
|
||||
class CalendarBox::Title final : public RpWidget {
|
||||
|
@ -575,9 +569,10 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args)
|
|||
, _previous(this, st::calendarPrevious)
|
||||
, _next(this, st::calendarNext)
|
||||
, _callback(std::move(args.callback.value()))
|
||||
, _finalize(std::move(args.finalize)) {
|
||||
, _finalize(std::move(args.finalize))
|
||||
, _jumpTimer([=] { jump(_jumpButton); }) {
|
||||
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_context->setBeginningButton(args.hasBeginningButton);
|
||||
_context->setHasSelection(args.allowsSelection);
|
||||
_context->setMinDate(args.minDate);
|
||||
_context->setMaxDate(args.maxDate);
|
||||
|
||||
|
@ -587,6 +582,60 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args)
|
|||
}) | rpl::start_with_next([=] {
|
||||
processScroll();
|
||||
}, lifetime());
|
||||
|
||||
const auto setupJumps = [&](
|
||||
not_null<IconButton*> button,
|
||||
not_null<bool*> enabled) {
|
||||
button->events(
|
||||
) | rpl::filter([=] {
|
||||
return *enabled;
|
||||
}) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
const auto type = e->type();
|
||||
if (type == QEvent::MouseMove
|
||||
&& !(static_cast<QMouseEvent*>(e.get())->buttons()
|
||||
& Qt::LeftButton)) {
|
||||
showJumpTooltip(button);
|
||||
} else if (type == QEvent::Leave) {
|
||||
Ui::Tooltip::Hide();
|
||||
} else if (type == QEvent::MouseButtonPress
|
||||
&& (static_cast<QMouseEvent*>(e.get())->button()
|
||||
== Qt::LeftButton)) {
|
||||
jumpAfterDelay(button);
|
||||
} else if (type == QEvent::MouseButtonRelease
|
||||
&& (static_cast<QMouseEvent*>(e.get())->button()
|
||||
== Qt::LeftButton)) {
|
||||
_jumpTimer.cancel();
|
||||
}
|
||||
}, lifetime());
|
||||
};
|
||||
setupJumps(_previous.data(), &_previousEnabled);
|
||||
setupJumps(_next.data(), &_nextEnabled);
|
||||
}
|
||||
|
||||
void CalendarBox::showJumpTooltip(not_null<IconButton*> button) {
|
||||
_tooltipButton = button;
|
||||
Ui::Tooltip::Show(kTooltipDelay, this);
|
||||
}
|
||||
|
||||
void CalendarBox::jumpAfterDelay(not_null<IconButton*> button) {
|
||||
_jumpButton = button;
|
||||
_jumpTimer.callOnce(kJumpDelay);
|
||||
Ui::Tooltip::Hide();
|
||||
}
|
||||
|
||||
void CalendarBox::jump(QPointer<IconButton> button) {
|
||||
const auto jumpToIndex = [&](int index) {
|
||||
_watchScroll = false;
|
||||
_context->showMonth(_context->dateFromIndex(index));
|
||||
setExactScroll();
|
||||
};
|
||||
if (_jumpButton == _previous.data() && _previousEnabled) {
|
||||
jumpToIndex(_context->minDayIndex());
|
||||
} else if (_jumpButton == _next.data() && _nextEnabled) {
|
||||
jumpToIndex(_context->maxDayIndex());
|
||||
}
|
||||
_jumpButton = nullptr;
|
||||
_jumpTimer.cancel();
|
||||
}
|
||||
|
||||
void CalendarBox::prepare() {
|
||||
|
@ -606,9 +655,8 @@ void CalendarBox::prepare() {
|
|||
if (_finalize) {
|
||||
_finalize(this);
|
||||
}
|
||||
if (!_context->atBeginning() && _context->hasBeginningButton()) {
|
||||
addLeftButton(tr::lng_calendar_beginning(), [=] {
|
||||
_inner->selectBeginning();
|
||||
if (_context->hasSelection()) {
|
||||
addLeftButton(tr::lng_calendar_select_days(), [=] {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -666,16 +714,40 @@ void CalendarBox::processScroll() {
|
|||
_watchScroll = true;
|
||||
}
|
||||
|
||||
QString CalendarBox::tooltipText() const {
|
||||
if (_tooltipButton == _previous.data()) {
|
||||
return tr::lng_calendar_start_tip(tr::now);
|
||||
} else if (_tooltipButton == _next.data()) {
|
||||
return tr::lng_calendar_end_tip(tr::now);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QPoint CalendarBox::tooltipPos() const {
|
||||
return QCursor::pos();
|
||||
}
|
||||
|
||||
bool CalendarBox::tooltipWindowActive() const {
|
||||
return window()->isActiveWindow();
|
||||
}
|
||||
|
||||
void CalendarBox::monthChanged(QDate month) {
|
||||
setDimensions(_st.width, st::calendarTitleHeight + _st.daysHeight + _inner->countMaxHeight());
|
||||
auto previousEnabled = isPreviousEnabled();
|
||||
_previous->setIconOverride(previousEnabled ? nullptr : &st::calendarPreviousDisabled);
|
||||
_previous->setRippleColorOverride(previousEnabled ? nullptr : &st::boxBg);
|
||||
_previous->setCursor(previousEnabled ? style::cur_pointer : style::cur_default);
|
||||
auto nextEnabled = isNextEnabled();
|
||||
_next->setIconOverride(nextEnabled ? nullptr : &st::calendarNextDisabled);
|
||||
_next->setRippleColorOverride(nextEnabled ? nullptr : &st::boxBg);
|
||||
_next->setCursor(nextEnabled ? style::cur_pointer : style::cur_default);
|
||||
|
||||
_previousEnabled = isPreviousEnabled();
|
||||
_previous->setIconOverride(_previousEnabled ? nullptr : &st::calendarPreviousDisabled);
|
||||
_previous->setRippleColorOverride(_previousEnabled ? nullptr : &st::boxBg);
|
||||
_previous->setCursor(_previousEnabled ? style::cur_pointer : style::cur_default);
|
||||
if (!_previousEnabled) {
|
||||
_previous->clearState();
|
||||
}
|
||||
_nextEnabled = isNextEnabled();
|
||||
_next->setIconOverride(_nextEnabled ? nullptr : &st::calendarNextDisabled);
|
||||
_next->setRippleColorOverride(_nextEnabled ? nullptr : &st::boxBg);
|
||||
_next->setCursor(_nextEnabled ? style::cur_pointer : style::cur_default);
|
||||
if (!_nextEnabled) {
|
||||
_next->clearState();
|
||||
}
|
||||
}
|
||||
|
||||
void CalendarBox::resizeEvent(QResizeEvent *e) {
|
||||
|
@ -700,7 +772,9 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) {
|
|||
if (e->key() == Qt::Key_Escape) {
|
||||
e->ignore();
|
||||
} else if (e->key() == Qt::Key_Home) {
|
||||
_inner->selectBeginning();
|
||||
jump(_previous.data());
|
||||
} else if (e->key() == Qt::Key_End) {
|
||||
jump(_next.data());
|
||||
} else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) {
|
||||
goPreviousMonth();
|
||||
} else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down) {
|
||||
|
|
|
@ -8,7 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "base/required.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace style {
|
||||
struct CalendarSizes;
|
||||
|
@ -35,10 +37,10 @@ struct CalendarBoxArgs {
|
|||
const style::CalendarSizes &st = st::defaultCalendarSizes;
|
||||
QDate minDate;
|
||||
QDate maxDate;
|
||||
bool hasBeginningButton = false;
|
||||
bool allowsSelection = false;
|
||||
};
|
||||
|
||||
class CalendarBox final : public BoxContent {
|
||||
class CalendarBox final : public BoxContent, private AbstractTooltipShower {
|
||||
public:
|
||||
CalendarBox(QWidget*, CalendarBoxArgs &&args);
|
||||
~CalendarBox();
|
||||
|
@ -60,6 +62,14 @@ private:
|
|||
void setExactScroll();
|
||||
void processScroll();
|
||||
|
||||
void showJumpTooltip(not_null<IconButton*> button);
|
||||
void jumpAfterDelay(not_null<IconButton*> button);
|
||||
void jump(QPointer<IconButton> button);
|
||||
|
||||
QString tooltipText() const override;
|
||||
QPoint tooltipPos() const override;
|
||||
bool tooltipWindowActive() const override;
|
||||
|
||||
const style::CalendarSizes &_st;
|
||||
|
||||
class Context;
|
||||
|
@ -74,11 +84,17 @@ private:
|
|||
object_ptr<Title> _title;
|
||||
object_ptr<IconButton> _previous;
|
||||
object_ptr<IconButton> _next;
|
||||
bool _previousEnabled = false;
|
||||
bool _nextEnabled = false;
|
||||
|
||||
Fn<void(QDate date)> _callback;
|
||||
FnMut<void(not_null<CalendarBox*>)> _finalize;
|
||||
bool _watchScroll = false;
|
||||
|
||||
QPointer<IconButton> _tooltipButton;
|
||||
QPointer<IconButton> _jumpButton;
|
||||
base::Timer _jumpTimer;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -1147,63 +1147,61 @@ void SessionController::startOrJoinGroupCall(
|
|||
}
|
||||
|
||||
void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
|
||||
const auto history = chat.history();
|
||||
if (!history) {
|
||||
return;
|
||||
}
|
||||
const auto currentPeerDate = [&] {
|
||||
if (const auto history = chat.history()) {
|
||||
if (history->scrollTopItem) {
|
||||
return history->scrollTopItem->dateTime().date();
|
||||
} else if (history->loadedAtTop()
|
||||
&& !history->isEmpty()
|
||||
&& history->peer->migrateFrom()) {
|
||||
if (const auto migrated = history->owner().historyLoaded(history->peer->migrateFrom())) {
|
||||
if (migrated->scrollTopItem) {
|
||||
// We're up in the migrated history.
|
||||
// So current date is the date of first message here.
|
||||
return history->blocks.front()->messages.front()->dateTime().date();
|
||||
}
|
||||
if (history->scrollTopItem) {
|
||||
return history->scrollTopItem->dateTime().date();
|
||||
} else if (history->loadedAtTop()
|
||||
&& !history->isEmpty()
|
||||
&& history->peer->migrateFrom()) {
|
||||
if (const auto migrated = history->owner().historyLoaded(history->peer->migrateFrom())) {
|
||||
if (migrated->scrollTopItem) {
|
||||
// We're up in the migrated history.
|
||||
// So current date is the date of first message here.
|
||||
return history->blocks.front()->messages.front()->dateTime().date();
|
||||
}
|
||||
} else if (const auto item = history->lastMessage()) {
|
||||
return base::unixtime::parse(item->date()).date();
|
||||
}
|
||||
} else if (const auto item = history->lastMessage()) {
|
||||
return base::unixtime::parse(item->date()).date();
|
||||
}
|
||||
return QDate();
|
||||
}();
|
||||
const auto maxPeerDate = [](Dialogs::Key chat) {
|
||||
if (auto history = chat.history()) {
|
||||
if (const auto channel = history->peer->migrateTo()) {
|
||||
history = channel->owner().historyLoaded(channel);
|
||||
}
|
||||
if (const auto item = history ? history->lastMessage() : nullptr) {
|
||||
return base::unixtime::parse(item->date()).date();
|
||||
}
|
||||
const auto maxPeerDate = [&] {
|
||||
const auto check = history->peer->migrateTo()
|
||||
? history->owner().historyLoaded(history->peer->migrateTo())
|
||||
: history;
|
||||
if (const auto item = check ? check->lastMessage() : nullptr) {
|
||||
return base::unixtime::parse(item->date()).date();
|
||||
}
|
||||
return QDate::currentDate();
|
||||
};
|
||||
const auto minPeerDate = [](Dialogs::Key chat) {
|
||||
return QDate();
|
||||
}();
|
||||
const auto minPeerDate = [&] {
|
||||
const auto startDate = [] {
|
||||
// Telegram was launched in August 2013 :)
|
||||
return QDate(2013, 8, 1);
|
||||
};
|
||||
if (const auto history = chat.history()) {
|
||||
if (const auto chat = history->peer->migrateFrom()) {
|
||||
if (const auto history = chat->owner().historyLoaded(chat)) {
|
||||
if (history->loadedAtTop()) {
|
||||
if (!history->isEmpty()) {
|
||||
return history->blocks.front()->messages.front()->dateTime().date();
|
||||
}
|
||||
} else {
|
||||
return startDate();
|
||||
if (const auto chat = history->peer->migrateFrom()) {
|
||||
if (const auto history = chat->owner().historyLoaded(chat)) {
|
||||
if (history->loadedAtTop()) {
|
||||
if (!history->isEmpty()) {
|
||||
return history->blocks.front()->messages.front()->dateTime().date();
|
||||
}
|
||||
} else {
|
||||
return startDate();
|
||||
}
|
||||
}
|
||||
if (history->loadedAtTop()) {
|
||||
if (!history->isEmpty()) {
|
||||
return history->blocks.front()->messages.front()->dateTime().date();
|
||||
}
|
||||
return QDate::currentDate();
|
||||
}
|
||||
}
|
||||
if (history->loadedAtTop()) {
|
||||
if (!history->isEmpty()) {
|
||||
return history->blocks.front()->messages.front()->dateTime().date();
|
||||
}
|
||||
return QDate::currentDate();
|
||||
}
|
||||
return startDate();
|
||||
};
|
||||
}();
|
||||
const auto highlighted = !requestedDate.isNull()
|
||||
? requestedDate
|
||||
: !currentPeerDate.isNull()
|
||||
|
@ -1215,9 +1213,9 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
|
|||
.callback = [=](const QDate &date) {
|
||||
session().api().jumpToDate(chat, date);
|
||||
},
|
||||
.minDate = minPeerDate(chat),
|
||||
.maxDate = maxPeerDate(chat),
|
||||
.hasBeginningButton = true,
|
||||
.minDate = minPeerDate,
|
||||
.maxDate = maxPeerDate,
|
||||
.allowsSelection = history->peer->isUser(),
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue