mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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_day_year" = "{month} {day}, {year}";
|
||||||
"lng_month_year" = "{month} {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_ok" = "OK";
|
||||||
"lng_box_done" = "Done";
|
"lng_box_done" = "Done";
|
||||||
|
|
|
@ -19,6 +19,8 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kDaysInWeek = 7;
|
constexpr auto kDaysInWeek = 7;
|
||||||
constexpr auto kMaxDaysForScroll = kDaysInWeek * 1000;
|
constexpr auto kMaxDaysForScroll = kDaysInWeek * 1000;
|
||||||
|
constexpr auto kTooltipDelay = crl::time(1000);
|
||||||
|
constexpr auto kJumpDelay = 2 * crl::time(1000);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -26,9 +28,9 @@ class CalendarBox::Context {
|
||||||
public:
|
public:
|
||||||
Context(QDate month, QDate highlighted);
|
Context(QDate month, QDate highlighted);
|
||||||
|
|
||||||
void setBeginningButton(bool enabled);
|
void setHasSelection(bool has);
|
||||||
[[nodiscard]] bool hasBeginningButton() const {
|
[[nodiscard]] bool hasSelection() const {
|
||||||
return _beginningButton;
|
return _hasSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMinDate(QDate date);
|
void setMinDate(QDate date);
|
||||||
|
@ -63,9 +65,6 @@ public:
|
||||||
[[nodiscard]] bool isEnabled(int index) const {
|
[[nodiscard]] bool isEnabled(int index) const {
|
||||||
return (index >= _minDayIndex) && (index <= _maxDayIndex);
|
return (index >= _minDayIndex) && (index <= _maxDayIndex);
|
||||||
}
|
}
|
||||||
[[nodiscard]] bool atBeginning() const {
|
|
||||||
return _highlighted == _min;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<QDate> monthValue() const {
|
[[nodiscard]] rpl::producer<QDate> monthValue() const {
|
||||||
return _month.value();
|
return _month.value();
|
||||||
|
@ -80,7 +79,7 @@ private:
|
||||||
static int DaysShiftForMonth(QDate month, QDate min);
|
static int DaysShiftForMonth(QDate month, QDate min);
|
||||||
static int RowsCountForMonth(QDate month, QDate min, QDate max);
|
static int RowsCountForMonth(QDate month, QDate min, QDate max);
|
||||||
|
|
||||||
bool _beginningButton = false;
|
bool _hasSelection = false;
|
||||||
|
|
||||||
rpl::variable<QDate> _month;
|
rpl::variable<QDate> _month;
|
||||||
QDate _min, _max;
|
QDate _min, _max;
|
||||||
|
@ -102,8 +101,8 @@ CalendarBox::Context::Context(QDate month, QDate highlighted)
|
||||||
showMonth(month);
|
showMonth(month);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalendarBox::Context::setBeginningButton(bool enabled) {
|
void CalendarBox::Context::setHasSelection(bool has) {
|
||||||
_beginningButton = enabled;
|
_hasSelection = has;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalendarBox::Context::setMinDate(QDate date) {
|
void CalendarBox::Context::setMinDate(QDate date) {
|
||||||
|
@ -245,7 +244,6 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] int countMaxHeight() const;
|
[[nodiscard]] int countMaxHeight() const;
|
||||||
void setDateChosenCallback(Fn<void(QDate)> callback);
|
void setDateChosenCallback(Fn<void(QDate)> callback);
|
||||||
void selectBeginning();
|
|
||||||
|
|
||||||
~Inner();
|
~Inner();
|
||||||
|
|
||||||
|
@ -479,10 +477,6 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) {
|
||||||
_dateChosenCallback = std::move(callback);
|
_dateChosenCallback = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalendarBox::Inner::selectBeginning() {
|
|
||||||
_dateChosenCallback(_context->dateFromIndex(_context->minDayIndex()));
|
|
||||||
}
|
|
||||||
|
|
||||||
CalendarBox::Inner::~Inner() = default;
|
CalendarBox::Inner::~Inner() = default;
|
||||||
|
|
||||||
class CalendarBox::Title final : public RpWidget {
|
class CalendarBox::Title final : public RpWidget {
|
||||||
|
@ -575,9 +569,10 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args)
|
||||||
, _previous(this, st::calendarPrevious)
|
, _previous(this, st::calendarPrevious)
|
||||||
, _next(this, st::calendarNext)
|
, _next(this, st::calendarNext)
|
||||||
, _callback(std::move(args.callback.value()))
|
, _callback(std::move(args.callback.value()))
|
||||||
, _finalize(std::move(args.finalize)) {
|
, _finalize(std::move(args.finalize))
|
||||||
|
, _jumpTimer([=] { jump(_jumpButton); }) {
|
||||||
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
_context->setBeginningButton(args.hasBeginningButton);
|
_context->setHasSelection(args.allowsSelection);
|
||||||
_context->setMinDate(args.minDate);
|
_context->setMinDate(args.minDate);
|
||||||
_context->setMaxDate(args.maxDate);
|
_context->setMaxDate(args.maxDate);
|
||||||
|
|
||||||
|
@ -587,6 +582,60 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args)
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
processScroll();
|
processScroll();
|
||||||
}, lifetime());
|
}, 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() {
|
void CalendarBox::prepare() {
|
||||||
|
@ -606,9 +655,8 @@ void CalendarBox::prepare() {
|
||||||
if (_finalize) {
|
if (_finalize) {
|
||||||
_finalize(this);
|
_finalize(this);
|
||||||
}
|
}
|
||||||
if (!_context->atBeginning() && _context->hasBeginningButton()) {
|
if (_context->hasSelection()) {
|
||||||
addLeftButton(tr::lng_calendar_beginning(), [=] {
|
addLeftButton(tr::lng_calendar_select_days(), [=] {
|
||||||
_inner->selectBeginning();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,16 +714,40 @@ void CalendarBox::processScroll() {
|
||||||
_watchScroll = true;
|
_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) {
|
void CalendarBox::monthChanged(QDate month) {
|
||||||
setDimensions(_st.width, st::calendarTitleHeight + _st.daysHeight + _inner->countMaxHeight());
|
setDimensions(_st.width, st::calendarTitleHeight + _st.daysHeight + _inner->countMaxHeight());
|
||||||
auto previousEnabled = isPreviousEnabled();
|
|
||||||
_previous->setIconOverride(previousEnabled ? nullptr : &st::calendarPreviousDisabled);
|
_previousEnabled = isPreviousEnabled();
|
||||||
_previous->setRippleColorOverride(previousEnabled ? nullptr : &st::boxBg);
|
_previous->setIconOverride(_previousEnabled ? nullptr : &st::calendarPreviousDisabled);
|
||||||
_previous->setCursor(previousEnabled ? style::cur_pointer : style::cur_default);
|
_previous->setRippleColorOverride(_previousEnabled ? nullptr : &st::boxBg);
|
||||||
auto nextEnabled = isNextEnabled();
|
_previous->setCursor(_previousEnabled ? style::cur_pointer : style::cur_default);
|
||||||
_next->setIconOverride(nextEnabled ? nullptr : &st::calendarNextDisabled);
|
if (!_previousEnabled) {
|
||||||
_next->setRippleColorOverride(nextEnabled ? nullptr : &st::boxBg);
|
_previous->clearState();
|
||||||
_next->setCursor(nextEnabled ? style::cur_pointer : style::cur_default);
|
}
|
||||||
|
_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) {
|
void CalendarBox::resizeEvent(QResizeEvent *e) {
|
||||||
|
@ -700,7 +772,9 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) {
|
||||||
if (e->key() == Qt::Key_Escape) {
|
if (e->key() == Qt::Key_Escape) {
|
||||||
e->ignore();
|
e->ignore();
|
||||||
} else if (e->key() == Qt::Key_Home) {
|
} 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) {
|
} else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) {
|
||||||
goPreviousMonth();
|
goPreviousMonth();
|
||||||
} else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down) {
|
} 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
|
#pragma once
|
||||||
|
|
||||||
#include "ui/layers/box_content.h"
|
#include "ui/layers/box_content.h"
|
||||||
|
#include "ui/widgets/tooltip.h"
|
||||||
#include "base/required.h"
|
#include "base/required.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
namespace style {
|
namespace style {
|
||||||
struct CalendarSizes;
|
struct CalendarSizes;
|
||||||
|
@ -35,10 +37,10 @@ struct CalendarBoxArgs {
|
||||||
const style::CalendarSizes &st = st::defaultCalendarSizes;
|
const style::CalendarSizes &st = st::defaultCalendarSizes;
|
||||||
QDate minDate;
|
QDate minDate;
|
||||||
QDate maxDate;
|
QDate maxDate;
|
||||||
bool hasBeginningButton = false;
|
bool allowsSelection = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CalendarBox final : public BoxContent {
|
class CalendarBox final : public BoxContent, private AbstractTooltipShower {
|
||||||
public:
|
public:
|
||||||
CalendarBox(QWidget*, CalendarBoxArgs &&args);
|
CalendarBox(QWidget*, CalendarBoxArgs &&args);
|
||||||
~CalendarBox();
|
~CalendarBox();
|
||||||
|
@ -60,6 +62,14 @@ private:
|
||||||
void setExactScroll();
|
void setExactScroll();
|
||||||
void processScroll();
|
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;
|
const style::CalendarSizes &_st;
|
||||||
|
|
||||||
class Context;
|
class Context;
|
||||||
|
@ -74,11 +84,17 @@ private:
|
||||||
object_ptr<Title> _title;
|
object_ptr<Title> _title;
|
||||||
object_ptr<IconButton> _previous;
|
object_ptr<IconButton> _previous;
|
||||||
object_ptr<IconButton> _next;
|
object_ptr<IconButton> _next;
|
||||||
|
bool _previousEnabled = false;
|
||||||
|
bool _nextEnabled = false;
|
||||||
|
|
||||||
Fn<void(QDate date)> _callback;
|
Fn<void(QDate date)> _callback;
|
||||||
FnMut<void(not_null<CalendarBox*>)> _finalize;
|
FnMut<void(not_null<CalendarBox*>)> _finalize;
|
||||||
bool _watchScroll = false;
|
bool _watchScroll = false;
|
||||||
|
|
||||||
|
QPointer<IconButton> _tooltipButton;
|
||||||
|
QPointer<IconButton> _jumpButton;
|
||||||
|
base::Timer _jumpTimer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -1147,63 +1147,61 @@ void SessionController::startOrJoinGroupCall(
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
|
void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
|
||||||
|
const auto history = chat.history();
|
||||||
|
if (!history) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto currentPeerDate = [&] {
|
const auto currentPeerDate = [&] {
|
||||||
if (const auto history = chat.history()) {
|
if (history->scrollTopItem) {
|
||||||
if (history->scrollTopItem) {
|
return history->scrollTopItem->dateTime().date();
|
||||||
return history->scrollTopItem->dateTime().date();
|
} else if (history->loadedAtTop()
|
||||||
} else if (history->loadedAtTop()
|
&& !history->isEmpty()
|
||||||
&& !history->isEmpty()
|
&& history->peer->migrateFrom()) {
|
||||||
&& history->peer->migrateFrom()) {
|
if (const auto migrated = history->owner().historyLoaded(history->peer->migrateFrom())) {
|
||||||
if (const auto migrated = history->owner().historyLoaded(history->peer->migrateFrom())) {
|
if (migrated->scrollTopItem) {
|
||||||
if (migrated->scrollTopItem) {
|
// We're up in the migrated history.
|
||||||
// We're up in the migrated history.
|
// So current date is the date of first message here.
|
||||||
// So current date is the date of first message here.
|
return history->blocks.front()->messages.front()->dateTime().date();
|
||||||
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();
|
return QDate();
|
||||||
}();
|
}();
|
||||||
const auto maxPeerDate = [](Dialogs::Key chat) {
|
const auto maxPeerDate = [&] {
|
||||||
if (auto history = chat.history()) {
|
const auto check = history->peer->migrateTo()
|
||||||
if (const auto channel = history->peer->migrateTo()) {
|
? history->owner().historyLoaded(history->peer->migrateTo())
|
||||||
history = channel->owner().historyLoaded(channel);
|
: history;
|
||||||
}
|
if (const auto item = check ? check->lastMessage() : nullptr) {
|
||||||
if (const auto item = history ? history->lastMessage() : nullptr) {
|
return base::unixtime::parse(item->date()).date();
|
||||||
return base::unixtime::parse(item->date()).date();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return QDate::currentDate();
|
return QDate();
|
||||||
};
|
}();
|
||||||
const auto minPeerDate = [](Dialogs::Key chat) {
|
const auto minPeerDate = [&] {
|
||||||
const auto startDate = [] {
|
const auto startDate = [] {
|
||||||
// Telegram was launched in August 2013 :)
|
// Telegram was launched in August 2013 :)
|
||||||
return QDate(2013, 8, 1);
|
return QDate(2013, 8, 1);
|
||||||
};
|
};
|
||||||
if (const auto history = chat.history()) {
|
if (const auto chat = history->peer->migrateFrom()) {
|
||||||
if (const auto chat = history->peer->migrateFrom()) {
|
if (const auto history = chat->owner().historyLoaded(chat)) {
|
||||||
if (const auto history = chat->owner().historyLoaded(chat)) {
|
if (history->loadedAtTop()) {
|
||||||
if (history->loadedAtTop()) {
|
if (!history->isEmpty()) {
|
||||||
if (!history->isEmpty()) {
|
return history->blocks.front()->messages.front()->dateTime().date();
|
||||||
return history->blocks.front()->messages.front()->dateTime().date();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return startDate();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return startDate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (history->loadedAtTop()) {
|
}
|
||||||
if (!history->isEmpty()) {
|
if (history->loadedAtTop()) {
|
||||||
return history->blocks.front()->messages.front()->dateTime().date();
|
if (!history->isEmpty()) {
|
||||||
}
|
return history->blocks.front()->messages.front()->dateTime().date();
|
||||||
return QDate::currentDate();
|
}
|
||||||
}
|
return QDate::currentDate();
|
||||||
}
|
}
|
||||||
return startDate();
|
return startDate();
|
||||||
};
|
}();
|
||||||
const auto highlighted = !requestedDate.isNull()
|
const auto highlighted = !requestedDate.isNull()
|
||||||
? requestedDate
|
? requestedDate
|
||||||
: !currentPeerDate.isNull()
|
: !currentPeerDate.isNull()
|
||||||
|
@ -1215,9 +1213,9 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
|
||||||
.callback = [=](const QDate &date) {
|
.callback = [=](const QDate &date) {
|
||||||
session().api().jumpToDate(chat, date);
|
session().api().jumpToDate(chat, date);
|
||||||
},
|
},
|
||||||
.minDate = minPeerDate(chat),
|
.minDate = minPeerDate,
|
||||||
.maxDate = maxPeerDate(chat),
|
.maxDate = maxPeerDate,
|
||||||
.hasBeginningButton = true,
|
.allowsSelection = history->peer->isUser(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue