Start scrollable CalendarBox.

This commit is contained in:
John Preston 2021-11-11 15:44:12 +04:00
parent f13e28a9c5
commit be7cd51740
11 changed files with 288 additions and 229 deletions

View file

@ -516,14 +516,21 @@ calendarNextDisabled: icon {{ "title_back-flip_horizontal", menuIconFg }};
calendarTitleFont: boxTitleFont; calendarTitleFont: boxTitleFont;
defaultCalendarSizes: CalendarSizes { defaultCalendarSizes: CalendarSizes {
width: boxWideWidth; width: boxWideWidth;
daysHeight: 32px; daysHeight: 40px;
cellSize: size(48px, 40px); cellSize: size(48px, 40px);
cellInner: 34px; cellInner: 34px;
padding: margins(14px, 15px, 14px, 10px); padding: margins(14px, 7px, 14px, 10px);
} }
calendarDaysFont: normalFont; calendarDaysFont: normalFont;
calendarDaysFg: boxTitleAdditionalFg; calendarDaysFg: boxTitleAdditionalFg;
calendarScroll: backgroundScroll; calendarScroll: ScrollArea(defaultSolidScroll) {
deltat: 3px;
deltab: 3px;
round: 1px;
width: 8px;
deltax: 3px;
hiding: 1000;
}
passcodeTextStyle: TextStyle(defaultTextStyle) { passcodeTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 20px; lineHeight: 20px;

View file

@ -704,20 +704,22 @@ void EditRestrictedBox::showRestrictUntil() {
: base::unixtime::parse(getRealUntilValue()).date(); : base::unixtime::parse(getRealUntilValue()).date();
auto month = highlighted; auto month = highlighted;
_restrictUntilBox = Ui::show( _restrictUntilBox = Ui::show(
Box<Ui::CalendarBox>( Box<Ui::CalendarBox>(Ui::CalendarBoxArgs{
month, .month = month,
highlighted, .highlighted = highlighted,
[this](const QDate &date) { .callback = [=](const QDate &date) {
setRestrictUntil( setRestrictUntil(
static_cast<int>(date.startOfDay().toSecsSinceEpoch())); static_cast<int>(date.startOfDay().toSecsSinceEpoch()));
}), },
.finalize = [=](not_null<Ui::CalendarBox*> box) {
box->addLeftButton(
tr::lng_rights_chat_banned_forever(),
[=] { setRestrictUntil(0); });
},
.minDate = tomorrow,
.maxDate = QDate::currentDate().addDays(kMaxRestrictDelayDays),
}),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
_restrictUntilBox->setMaxDate(
QDate::currentDate().addDays(kMaxRestrictDelayDays));
_restrictUntilBox->setMinDate(tomorrow);
_restrictUntilBox->addLeftButton(
tr::lng_rights_chat_banned_forever(),
[=] { setRestrictUntil(0); });
} }
void EditRestrictedBox::setRestrictUntil(TimeId until) { void EditRestrictedBox::setRestrictUntil(TimeId until) {

View file

@ -97,8 +97,8 @@ exportTopBarLabel: FlatLabel(defaultFlatLabel) {
} }
exportCalendarSizes: CalendarSizes(defaultCalendarSizes) { exportCalendarSizes: CalendarSizes(defaultCalendarSizes) {
width: 320px; width: 320px;
daysHeight: 32px; daysHeight: 40px;
cellSize: size(42px, 38px); cellSize: size(42px, 38px);
cellInner: 32px; cellInner: 32px;
padding: margins(14px, 15px, 14px, 10px); padding: margins(14px, 7px, 14px, 10px);
} }

View file

@ -464,12 +464,6 @@ void SettingsWidget::editDateLimit(
const auto month = highlighted; const auto month = highlighted;
const auto shared = std::make_shared<QPointer<Ui::CalendarBox>>(); const auto shared = std::make_shared<QPointer<Ui::CalendarBox>>();
const auto finalize = [=](not_null<Ui::CalendarBox*> box) { const auto finalize = [=](not_null<Ui::CalendarBox*> box) {
box->setMaxDate(max
? base::unixtime::parse(max).date()
: QDate::currentDate());
box->setMinDate(min
? base::unixtime::parse(min).date()
: QDate(2013, 8, 1)); // Telegram was launched in August 2013 :)
box->addLeftButton(std::move(resetLabel), crl::guard(this, [=] { box->addLeftButton(std::move(resetLabel), crl::guard(this, [=] {
done(0); done(0);
if (const auto weak = shared->data()) { if (const auto weak = shared->data()) {
@ -483,12 +477,19 @@ void SettingsWidget::editDateLimit(
weak->closeBox(); weak->closeBox();
} }
}); });
auto box = Box<Ui::CalendarBox>( auto box = Box<Ui::CalendarBox>(Ui::CalendarBoxArgs{
month, .month = month,
highlighted, .highlighted = highlighted,
callback, .callback = callback,
finalize, .finalize = finalize,
st::exportCalendarSizes); .st = st::exportCalendarSizes,
.minDate = (min
? base::unixtime::parse(min).date()
: QDate(2013, 8, 1)), // Telegram was launched in August 2013 :)
.maxDate = (max
? base::unixtime::parse(max).date()
: QDate::currentDate()),
});
*shared = Ui::MakeWeak(box.data()); *shared = Ui::MakeWeak(box.data());
_showBoxCallback(std::move(box)); _showBoxCallback(std::move(box));
} }

View file

@ -8,16 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/calendar_box.h" #include "ui/boxes/calendar_box.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
namespace Ui { namespace Ui {
namespace { namespace {
constexpr auto kDaysInWeek = 7; constexpr auto kDaysInWeek = 7;
constexpr auto kMaxDaysForScroll = kDaysInWeek * 1000;
} // namespace } // namespace
@ -25,49 +26,48 @@ class CalendarBox::Context {
public: public:
Context(QDate month, QDate highlighted); Context(QDate month, QDate highlighted);
void start() {
_month.setForced(_month.value(), true);
}
void setBeginningButton(bool enabled); void setBeginningButton(bool enabled);
bool hasBeginningButton() const { [[nodiscard]] bool hasBeginningButton() const {
return _beginningButton; return _beginningButton;
} }
void setMinDate(QDate date); void setMinDate(QDate date);
void setMaxDate(QDate date); void setMaxDate(QDate date);
int minDayIndex() const { [[nodiscard]] int minDayIndex() const {
return _minDayIndex; return _minDayIndex;
} }
int maxDayIndex() const { [[nodiscard]] int maxDayIndex() const {
return _maxDayIndex; return _maxDayIndex;
} }
void skipMonth(int skip); void skipMonth(int skip);
void showMonth(QDate month); void showMonth(QDate month);
int highlightedIndex() const { [[nodiscard]] int highlightedIndex() const {
return _highlightedIndex; return _highlightedIndex;
} }
int rowsCount() const { [[nodiscard]] int rowsCount() const {
return _rowsCount; return _rowsCount;
} }
int daysShift() const { [[nodiscard]] int rowsCountMax() const {
return 6;
}
[[nodiscard]] int daysShift() const {
return _daysShift; return _daysShift;
} }
int daysCount() const { [[nodiscard]] int daysCount() const {
return _daysCount; return _daysCount;
} }
bool isEnabled(int index) const { [[nodiscard]] bool isEnabled(int index) const {
return (index >= _minDayIndex) && (index <= _maxDayIndex); return (index >= _minDayIndex) && (index <= _maxDayIndex);
} }
bool atBeginning() const { [[nodiscard]] bool atBeginning() const {
return _highlighted == _min; return _highlighted == _min;
} }
const base::Variable<QDate> &month() { [[nodiscard]] rpl::producer<QDate> monthValue() const {
return _month; return _month.value();
} }
QDate dateFromIndex(int index) const; QDate dateFromIndex(int index) const;
@ -76,14 +76,16 @@ public:
private: private:
void applyMonth(const QDate &month, bool forced = false); void applyMonth(const QDate &month, bool forced = false);
static int daysShiftForMonth(QDate month); static int DaysShiftForMonth(QDate month, QDate min);
static int rowsCountForMonth(QDate month); static int RowsCountForMonth(QDate month, QDate min, QDate max);
bool _beginningButton = false; bool _beginningButton = false;
base::Variable<QDate> _month; rpl::variable<QDate> _month;
QDate _min, _max; QDate _min, _max;
QDate _highlighted; QDate _highlighted;
Fn<QString(int)> _dayOfWeek;
Fn<QString(int, int)> _monthOfYear;
int _highlightedIndex = 0; int _highlightedIndex = 0;
int _minDayIndex = 0; int _minDayIndex = 0;
@ -94,7 +96,8 @@ private:
}; };
CalendarBox::Context::Context(QDate month, QDate highlighted) : _highlighted(highlighted) { CalendarBox::Context::Context(QDate month, QDate highlighted)
: _highlighted(highlighted) {
showMonth(month); showMonth(month);
} }
@ -104,12 +107,12 @@ void CalendarBox::Context::setBeginningButton(bool enabled) {
void CalendarBox::Context::setMinDate(QDate date) { void CalendarBox::Context::setMinDate(QDate date) {
_min = date; _min = date;
applyMonth(_month.value(), true); applyMonth(_month.current(), true);
} }
void CalendarBox::Context::setMaxDate(QDate date) { void CalendarBox::Context::setMaxDate(QDate date) {
_max = date; _max = date;
applyMonth(_month.value(), true); applyMonth(_month.current(), true);
} }
void CalendarBox::Context::showMonth(QDate month) { void CalendarBox::Context::showMonth(QDate month) {
@ -121,21 +124,21 @@ void CalendarBox::Context::showMonth(QDate month) {
void CalendarBox::Context::applyMonth(const QDate &month, bool forced) { void CalendarBox::Context::applyMonth(const QDate &month, bool forced) {
_daysCount = month.daysInMonth(); _daysCount = month.daysInMonth();
_daysShift = daysShiftForMonth(month); _daysShift = DaysShiftForMonth(month, _min);
_rowsCount = rowsCountForMonth(month); _rowsCount = RowsCountForMonth(month, _min, _max);
_highlightedIndex = month.daysTo(_highlighted); _highlightedIndex = month.daysTo(_highlighted);
_minDayIndex = _min.isNull() ? INT_MIN : month.daysTo(_min); _minDayIndex = _min.isNull() ? INT_MIN : month.daysTo(_min);
_maxDayIndex = _max.isNull() ? INT_MAX : month.daysTo(_max); _maxDayIndex = _max.isNull() ? INT_MAX : month.daysTo(_max);
if (forced) { if (forced) {
_month.setForced(month, true); _month.force_assign(month);
} else { } else {
_month.set(month, true); _month = month;
} }
} }
void CalendarBox::Context::skipMonth(int skip) { void CalendarBox::Context::skipMonth(int skip) {
auto year = _month.value().year(); auto year = _month.current().year();
auto month = _month.value().month(); auto month = _month.current().month();
month += skip; month += skip;
while (month < 1) { while (month < 1) {
--year; --year;
@ -148,28 +151,58 @@ void CalendarBox::Context::skipMonth(int skip) {
showMonth(QDate(year, month, 1)); showMonth(QDate(year, month, 1));
} }
int CalendarBox::Context::daysShiftForMonth(QDate month) { int CalendarBox::Context::DaysShiftForMonth(QDate month, QDate min) {
Assert(!month.isNull()); Expects(!month.isNull());
constexpr auto kMaxRows = 6; constexpr auto kMaxRows = 6;
auto inMonthIndex = month.day() - 1; const auto inMonthIndex = month.day() - 1;
auto inWeekIndex = month.dayOfWeek() - 1; const auto inWeekIndex = month.dayOfWeek() - 1;
return ((kMaxRows * kDaysInWeek) + inWeekIndex - inMonthIndex) % kDaysInWeek; const auto from = ((kMaxRows * kDaysInWeek) + inWeekIndex - inMonthIndex)
% kDaysInWeek;
if (min.isNull()) {
min = month.addYears(-1);
} else if (min >= month) {
return from;
}
if (min.day() != 1) {
min = QDate(min.year(), min.month(), 1);
}
const auto add = min.daysTo(month) + inWeekIndex + (min.dayOfWeek() - 1);
return from + add;
} }
int CalendarBox::Context::rowsCountForMonth(QDate month) { int CalendarBox::Context::RowsCountForMonth(
Assert(!month.isNull()); QDate month,
auto daysShift = daysShiftForMonth(month); QDate min,
auto daysCount = month.daysInMonth(); QDate max) {
auto cellsCount = daysShift + daysCount; Expects(!month.isNull());
const auto daysShift = DaysShiftForMonth(month, min);
const auto daysCount = month.daysInMonth();
const auto cellsCount = daysShift + daysCount;
auto result = (cellsCount / kDaysInWeek); auto result = (cellsCount / kDaysInWeek);
if (cellsCount % kDaysInWeek) ++result; if (cellsCount % kDaysInWeek) {
return result; ++result;
}
if (max.isNull()) {
max = month.addYears(1);
}
if (max < month.addMonths(1)) {
return result;
}
if (max.day() != 1) {
max = QDate(max.year(), max.month(), 1);
}
max = max.addMonths(1);
max = max.addDays(1 - max.dayOfWeek());
const auto cellsFull = daysShift + (month.day() - 1) + month.daysTo(max);
return cellsFull / kDaysInWeek;
} }
QDate CalendarBox::Context::dateFromIndex(int index) const { QDate CalendarBox::Context::dateFromIndex(int index) const {
constexpr auto kMonthsCount = 12; constexpr auto kMonthsCount = 12;
auto month = _month.value().month(); auto month = _month.current().month();
auto year = _month.value().year(); auto year = _month.current().year();
while (index < 0) { while (index < 0) {
if (!--month) { if (!--month) {
month += kMonthsCount; month += kMonthsCount;
@ -197,14 +230,14 @@ QString CalendarBox::Context::labelFromIndex(int index) const {
return QString::number(day()); return QString::number(day());
} }
class CalendarBox::Inner : public TWidget, private base::Subscriber { class CalendarBox::Inner final : public RpWidget {
public: public:
Inner( Inner(
QWidget *parent, QWidget *parent,
not_null<Context*> context, not_null<Context*> context,
const style::CalendarSizes &st); const style::CalendarSizes &st);
int countHeight(); [[nodiscard]] int countMaxHeight() const;
void setDateChosenCallback(Fn<void(QDate)> callback); void setDateChosenCallback(Fn<void(QDate)> callback);
void selectBeginning(); void selectBeginning();
@ -224,19 +257,23 @@ private:
int rowsLeft() const; int rowsLeft() const;
int rowsTop() const; int rowsTop() const;
void resizeToCurrent(); void resizeToCurrent();
void paintDayNames(Painter &p, QRect clip);
void paintRows(Painter &p, QRect clip); void paintRows(Painter &p, QRect clip);
const style::CalendarSizes &_st; const style::CalendarSizes &_st;
not_null<Context*> _context; const not_null<Context*> _context;
std::map<int, std::unique_ptr<RippleAnimation>> _ripples; std::map<int, std::unique_ptr<RippleAnimation>> _ripples;
Fn<void(QDate)> _dateChosenCallback; Fn<void(QDate)> _dateChosenCallback;
static constexpr auto kEmptySelection = -kDaysInWeek; static constexpr auto kEmptySelection = INT_MIN / 2;
int _selected = kEmptySelection; int _selected = kEmptySelection;
int _pressed = kEmptySelection; int _pressed = kEmptySelection;
bool _pointerCursor = false;
bool _cursorSetWithoutMouseMove = false;
QPoint _lastGlobalPosition;
bool _mouseMoved = false;
}; };
@ -244,13 +281,15 @@ CalendarBox::Inner::Inner(
QWidget *parent, QWidget *parent,
not_null<Context*> context, not_null<Context*> context,
const style::CalendarSizes &st) const style::CalendarSizes &st)
: TWidget(parent) : RpWidget(parent)
, _st(st) , _st(st)
, _context(context) { , _context(context) {
setMouseTracking(true); setMouseTracking(true);
subscribe(context->month(), [this](QDate month) {
context->monthValue(
) | rpl::start_with_next([=](QDate month) {
monthChanged(month); monthChanged(month);
}); }, lifetime());
} }
void CalendarBox::Inner::monthChanged(QDate month) { void CalendarBox::Inner::monthChanged(QDate month) {
@ -262,7 +301,8 @@ void CalendarBox::Inner::monthChanged(QDate month) {
} }
void CalendarBox::Inner::resizeToCurrent() { void CalendarBox::Inner::resizeToCurrent() {
resize(_st.width, countHeight()); const auto height = _context->rowsCount() * _st.cellSize.height();
resize(_st.width, _st.padding.top() + height + _st.padding.bottom());
} }
void CalendarBox::Inner::paintEvent(QPaintEvent *e) { void CalendarBox::Inner::paintEvent(QPaintEvent *e) {
@ -270,33 +310,15 @@ void CalendarBox::Inner::paintEvent(QPaintEvent *e) {
auto clip = e->rect(); auto clip = e->rect();
paintDayNames(p, clip);
paintRows(p, clip); paintRows(p, clip);
} }
void CalendarBox::Inner::paintDayNames(Painter &p, QRect clip) {
p.setFont(st::calendarDaysFont);
p.setPen(st::calendarDaysFg);
auto y = _st.padding.top();
auto x = rowsLeft();
if (!myrtlrect(x, y, _st.cellSize.width() * kDaysInWeek, _st.daysHeight).intersects(clip)) {
return;
}
for (auto i = 0; i != kDaysInWeek; ++i, x += _st.cellSize.width()) {
auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.daysHeight);
if (!rect.intersects(clip)) {
continue;
}
p.drawText(rect, langDayOfWeek(i + 1), style::al_top);
}
}
int CalendarBox::Inner::rowsLeft() const { int CalendarBox::Inner::rowsLeft() const {
return _st.padding.left(); return _st.padding.left();
} }
int CalendarBox::Inner::rowsTop() const { int CalendarBox::Inner::rowsTop() const {
return _st.padding.top() + _st.daysHeight; return _st.padding.top();
} }
void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { void CalendarBox::Inner::paintRows(Painter &p, QRect clip) {
@ -304,14 +326,17 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) {
auto y = rowsTop(); auto y = rowsTop();
auto index = -_context->daysShift(); auto index = -_context->daysShift();
auto highlightedIndex = _context->highlightedIndex(); auto highlightedIndex = _context->highlightedIndex();
for (auto row = 0, rowsCount = _context->rowsCount(), daysCount = _context->daysCount() const auto daysCount = _context->daysCount();
; row != rowsCount const auto rowsCount = _context->rowsCount();
; ++row, y += _st.cellSize.height()) { const auto rowHeight = _st.cellSize.height();
const auto fromRow = std::max(clip.y() - y, 0) / rowHeight;
const auto tillRow = std::min(
(clip.y() + clip.height() + rowHeight - 1) / rowHeight,
rowsCount);
y += fromRow * rowHeight;
index += fromRow * kDaysInWeek;
for (auto row = fromRow; row != tillRow; ++row, y += rowHeight) {
auto x = rowsLeft(); auto x = rowsLeft();
if (!myrtlrect(x, y, _st.cellSize.width() * kDaysInWeek, _st.cellSize.height()).intersects(clip)) {
index += kDaysInWeek;
continue;
}
for (auto col = 0; col != kDaysInWeek; ++col, ++index, x += _st.cellSize.width()) { for (auto col = 0; col != kDaysInWeek; ++col, ++index, x += _st.cellSize.width()) {
auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.cellSize.height()); auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.cellSize.height());
auto grayedOut = (index < 0 || index >= daysCount || !rect.intersects(clip)); auto grayedOut = (index < 0 || index >= daysCount || !rect.intersects(clip));
@ -352,6 +377,10 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) {
} }
void CalendarBox::Inner::mouseMoveEvent(QMouseEvent *e) { void CalendarBox::Inner::mouseMoveEvent(QMouseEvent *e) {
const auto globalPosition = e->globalPos();
_mouseMoved = (_lastGlobalPosition != globalPosition);
_lastGlobalPosition = globalPosition;
const auto size = _st.cellSize; const auto size = _st.cellSize;
const auto point = e->pos(); const auto point = e->pos();
const auto inner = QRect( const auto inner = QRect(
@ -374,9 +403,20 @@ void CalendarBox::Inner::setSelected(int selected) {
selected = kEmptySelection; selected = kEmptySelection;
} }
_selected = selected; _selected = selected;
setCursor((_selected == kEmptySelection) const auto pointer = (_selected != kEmptySelection);
? style::cur_default const auto force = (_mouseMoved && _cursorSetWithoutMouseMove);
: style::cur_pointer); if (_pointerCursor != pointer || force) {
if (force) {
// Workaround some strange bug. When I call setCursor while
// scrolling by touchpad the new cursor is not applied and
// then it is not applied until it is changed.
setCursor(pointer ? style::cur_default : style::cur_pointer);
}
setCursor(pointer ? style::cur_pointer : style::cur_default);
_cursorSetWithoutMouseMove = !_mouseMoved;
_pointerCursor = pointer;
}
_mouseMoved = false;
} }
void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) { void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) {
@ -422,9 +462,8 @@ void CalendarBox::Inner::setPressed(int pressed) {
} }
} }
int CalendarBox::Inner::countHeight() { int CalendarBox::Inner::countMaxHeight() const {
const auto innerHeight = _st.daysHeight const auto innerHeight = _context->rowsCountMax() * _st.cellSize.height();
+ _context->rowsCount() * _st.cellSize.height();
return _st.padding.top() return _st.padding.top()
+ innerHeight + innerHeight
+ _st.padding.bottom(); + _st.padding.bottom();
@ -440,27 +479,41 @@ void CalendarBox::Inner::selectBeginning() {
CalendarBox::Inner::~Inner() = default; CalendarBox::Inner::~Inner() = default;
class CalendarBox::Title : public TWidget, private base::Subscriber { class CalendarBox::Title final : public RpWidget {
public: public:
Title(QWidget *parent, not_null<Context*> context) Title(
: TWidget(parent) QWidget *parent,
, _context(context) { not_null<Context*> context,
subscribe(_context->month(), [this](QDate date) { monthChanged(date); }); const style::CalendarSizes &st);
}
protected: protected:
void paintEvent(QPaintEvent *e); void paintEvent(QPaintEvent *e);
private: private:
void monthChanged(QDate month); void monthChanged(QDate month);
void paintDayNames(Painter &p, QRect clip);
not_null<Context*> _context; const style::CalendarSizes &_st;
const not_null<Context*> _context;
QString _text; QString _text;
int _textWidth = 0; int _textWidth = 0;
}; };
CalendarBox::Title::Title(
QWidget *parent,
not_null<Context*> context,
const style::CalendarSizes &st)
: RpWidget(parent)
, _st(st)
, _context(context) {
_context->monthValue(
) | rpl::start_with_next([=](QDate date) {
monthChanged(date);
}, lifetime());
}
void CalendarBox::Title::monthChanged(QDate month) { void CalendarBox::Title::monthChanged(QDate month) {
_text = langMonthOfYearFull(month.month(), month.year()); _text = langMonthOfYearFull(month.month(), month.year());
_textWidth = st::calendarTitleFont->width(_text); _textWidth = st::calendarTitleFont->width(_text);
@ -470,77 +523,70 @@ void CalendarBox::Title::monthChanged(QDate month) {
void CalendarBox::Title::paintEvent(QPaintEvent *e) { void CalendarBox::Title::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
const auto clip = e->rect();
p.setFont(st::calendarTitleFont); p.setFont(st::calendarTitleFont);
p.setPen(st::boxTitleFg); p.setPen(st::boxTitleFg);
p.drawTextLeft((width() - _textWidth) / 2, (height() - st::calendarTitleFont->height) / 2, width(), _text, _textWidth); p.drawTextLeft((width() - _textWidth) / 2, (st::calendarTitleHeight - st::calendarTitleFont->height) / 2, width(), _text, _textWidth);
paintDayNames(p, clip);
} }
CalendarBox::CalendarBox( void CalendarBox::Title::paintDayNames(Painter &p, QRect clip) {
QWidget*, p.setFont(st::calendarDaysFont);
QDate month, p.setPen(st::calendarDaysFg);
QDate highlighted, auto y = st::calendarTitleHeight + _st.padding.top();
Fn<void(QDate date)> callback, auto x = _st.padding.left();
FnMut<void(not_null<CalendarBox*>)> finalize) if (!myrtlrect(x, y, _st.cellSize.width() * kDaysInWeek, _st.daysHeight).intersects(clip)) {
: CalendarBox( return;
nullptr, }
month, for (auto i = 0; i != kDaysInWeek; ++i, x += _st.cellSize.width()) {
highlighted, auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.daysHeight);
std::move(callback), if (!rect.intersects(clip)) {
std::move(finalize), continue;
st::defaultCalendarSizes) { }
p.drawText(rect, langDayOfWeek(i + 1), style::al_top);
}
} }
CalendarBox::CalendarBox( CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args)
QWidget*, : _st(args.st)
QDate month, , _context(
QDate highlighted, std::make_unique<Context>(args.month.value(), args.highlighted.value()))
Fn<void(QDate date)> callback, , _scroll(std::make_unique<ScrollArea>(this, st::calendarScroll))
FnMut<void(not_null<CalendarBox*>)> finalize, , _inner(
const style::CalendarSizes &st) _scroll->setOwnedWidget(object_ptr<Inner>(this, _context.get(), _st)))
: _st(st) , _title(this, _context.get(), _st)
, _context(std::make_unique<Context>(month, highlighted))
, _inner(this, _context.get(), _st)
, _title(this, _context.get())
, _previous(this, st::calendarPrevious) , _previous(this, st::calendarPrevious)
, _next(this, st::calendarNext) , _next(this, st::calendarNext)
, _callback(std::move(callback)) , _callback(std::move(args.callback.value()))
, _finalize(std::move(finalize)) { , _finalize(std::move(args.finalize)) {
} _title->setAttribute(Qt::WA_TransparentForMouseEvents);
_context->setBeginningButton(args.hasBeginningButton);
void CalendarBox::setMinDate(QDate date) { _context->setMinDate(args.minDate);
_context->setMinDate(date); _context->setMaxDate(args.maxDate);
}
void CalendarBox::setMaxDate(QDate date) {
_context->setMaxDate(date);
}
bool CalendarBox::hasBeginningButton() const {
return _context->hasBeginningButton();
}
void CalendarBox::setBeginningButton(bool enabled) {
_context->setBeginningButton(enabled);
} }
void CalendarBox::prepare() { void CalendarBox::prepare() {
_previous->setClickedCallback([this] { goPreviousMonth(); }); _previous->setClickedCallback([=] { goPreviousMonth(); });
_next->setClickedCallback([this] { goNextMonth(); }); _next->setClickedCallback([=] { goNextMonth(); });
// _inner = setInnerWidget(object_ptr<Inner>(this, _context.get()), st::calendarScroll, st::calendarTitleHeight);
_inner->setDateChosenCallback(std::move(_callback)); _inner->setDateChosenCallback(std::move(_callback));
addButton(tr::lng_close(), [this] { closeBox(); }); addButton(tr::lng_close(), [=] { closeBox(); });
subscribe(_context->month(), [this](QDate month) { monthChanged(month); }); _context->monthValue(
) | rpl::start_with_next([=](QDate month) {
_context->start(); monthChanged(month);
}, lifetime());
if (_finalize) { if (_finalize) {
_finalize(this); _finalize(this);
} }
if (!_context->atBeginning() && hasBeginningButton()) { if (!_context->atBeginning() && _context->hasBeginningButton()) {
addLeftButton(tr::lng_calendar_beginning(), [this] { _inner->selectBeginning(); }); addLeftButton(tr::lng_calendar_beginning(), [=] {
_inner->selectBeginning();
});
} }
} }
@ -565,7 +611,7 @@ void CalendarBox::goNextMonth() {
} }
void CalendarBox::monthChanged(QDate month) { void CalendarBox::monthChanged(QDate month) {
setDimensions(_st.width, st::calendarTitleHeight + _inner->countHeight()); setDimensions(_st.width, st::calendarTitleHeight + _st.daysHeight + _inner->countMaxHeight());
auto previousEnabled = isPreviousEnabled(); auto previousEnabled = isPreviousEnabled();
_previous->setIconOverride(previousEnabled ? nullptr : &st::calendarPreviousDisabled); _previous->setIconOverride(previousEnabled ? nullptr : &st::calendarPreviousDisabled);
_previous->setRippleColorOverride(previousEnabled ? nullptr : &st::boxBg); _previous->setRippleColorOverride(previousEnabled ? nullptr : &st::boxBg);
@ -579,8 +625,9 @@ void CalendarBox::monthChanged(QDate month) {
void CalendarBox::resizeEvent(QResizeEvent *e) { void CalendarBox::resizeEvent(QResizeEvent *e) {
_previous->moveToLeft(0, 0); _previous->moveToLeft(0, 0);
_next->moveToRight(0, 0); _next->moveToRight(0, 0);
_title->setGeometryToLeft(_previous->width(), 0, width() - _previous->width() - _next->width(), st::calendarTitleHeight); const auto title = st::calendarTitleHeight + _st.daysHeight;
_inner->setGeometryToLeft(0, st::calendarTitleHeight, width(), height() - st::calendarTitleHeight); _title->setGeometryToLeft(0, 0, width(), title);
_scroll->setGeometryToLeft(0, title, width(), height() - title);
BoxContent::resizeEvent(e); BoxContent::resizeEvent(e);
} }
@ -589,9 +636,9 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) {
e->ignore(); e->ignore();
} else if (e->key() == Qt::Key_Home) { } else if (e->key() == Qt::Key_Home) {
_inner->selectBeginning(); _inner->selectBeginning();
} else if (e->key() == Qt::Key_Left) { } else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) {
goPreviousMonth(); goPreviousMonth();
} else if (e->key() == Qt::Key_Right) { } else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down) {
goNextMonth(); goNextMonth();
} }
} }

View file

@ -8,38 +8,39 @@ 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 "base/observer.h" #include "base/required.h"
namespace style { namespace style {
struct CalendarSizes; struct CalendarSizes;
} // namespace style } // namespace style
namespace st {
extern const style::CalendarSizes &defaultCalendarSizes;
} // namespace st
namespace Ui { namespace Ui {
class IconButton; class IconButton;
class ScrollArea;
class CalendarBox;
class CalendarBox : public BoxContent, private base::Subscriber { struct CalendarBoxArgs {
template <typename T>
using required = base::required<T>;
required<QDate> month;
required<QDate> highlighted;
required<Fn<void(QDate date)>> callback;
FnMut<void(not_null<CalendarBox*>)> finalize;
const style::CalendarSizes &st = st::defaultCalendarSizes;
QDate minDate;
QDate maxDate;
bool hasBeginningButton = false;
};
class CalendarBox final : public BoxContent {
public: public:
CalendarBox( CalendarBox(QWidget*, CalendarBoxArgs &&args);
QWidget*,
QDate month,
QDate highlighted,
Fn<void(QDate date)> callback,
FnMut<void(not_null<CalendarBox*>)> finalize = nullptr);
CalendarBox(
QWidget*,
QDate month,
QDate highlighted,
Fn<void(QDate date)> callback,
FnMut<void(not_null<CalendarBox*>)> finalize,
const style::CalendarSizes &st);
void setBeginningButton(bool enabled);
bool hasBeginningButton() const;
void setMinDate(QDate date);
void setMaxDate(QDate date);
~CalendarBox(); ~CalendarBox();
protected: protected:
@ -63,8 +64,10 @@ private:
class Context; class Context;
std::unique_ptr<Context> _context; std::unique_ptr<Context> _context;
std::unique_ptr<ScrollArea> _scroll;
class Inner; class Inner;
object_ptr<Inner> _inner; not_null<Inner*> _inner;
class Title; class Title;
object_ptr<Title> _title; object_ptr<Title> _title;

View file

@ -159,19 +159,17 @@ ChooseDateTimeBoxDescriptor ChooseDateTimeBox(
if (*calendar) { if (*calendar) {
return; return;
} }
const auto chosen = [=](QDate chosen) { *calendar = box->getDelegate()->show(
state->date = chosen; Box<CalendarBox>(Ui::CalendarBoxArgs{
(*calendar)->closeBox(); .month = state->date.current(),
}; .highlighted = state->date.current(),
const auto finalize = [=](not_null<CalendarBox*> box) { .callback = crl::guard(box, [=](QDate chosen) {
box->setMinDate(minDate()); state->date = chosen;
box->setMaxDate(maxDate()); (*calendar)->closeBox();
}; }),
*calendar = box->getDelegate()->show(Box<CalendarBox>( .minDate = minDate(),
state->date.current(), .maxDate = maxDate(),
state->date.current(), }));
crl::guard(box, chosen),
finalize));
(*calendar)->boxClosing( (*calendar)->boxClosing(
) | rpl::start_with_next(crl::guard(state->time, [=] { ) | rpl::start_with_next(crl::guard(state->time, [=] {
state->time->setFocusFast(); state->time->setFocusFast();

View file

@ -44,7 +44,7 @@ void SingleChoiceBox(
st::boxPadding.right(), st::boxPadding.right(),
st::boxOptionListSkip)); st::boxOptionListSkip));
} }
const auto callback = args.callback; const auto callback = args.callback.value();
group->setChangedCallback([=](int value) { group->setChangedCallback([=](int value) {
const auto weak = Ui::MakeWeak(box); const auto weak = Ui::MakeWeak(box);
callback(value); callback(value);

View file

@ -22,7 +22,7 @@ struct SingleChoiceBoxArgs {
required<rpl::producer<QString>> title; required<rpl::producer<QString>> title;
const std::vector<QString> &options; const std::vector<QString> &options;
int initialSelection = 0; int initialSelection = 0;
Fn<void(int)> callback; required<Fn<void(int)>> callback;
const style::Checkbox *st = nullptr; const style::Checkbox *st = nullptr;
const style::Radio *radioSt = nullptr; const style::Radio *radioSt = nullptr;
}; };

View file

@ -42,10 +42,13 @@ struct TermsLock;
void ConvertIconToBlack(QImage &image); void ConvertIconToBlack(QImage &image);
struct CounterLayerArgs { struct CounterLayerArgs {
base::required<int> size = 16; template <typename T>
base::required<int> count = 1; using required = base::required<T>;
base::required<style::color> bg;
base::required<style::color> fg; required<int> size = 16;
required<int> count = 1;
required<style::color> bg;
required<style::color> fg;
}; };
[[nodiscard]] QImage GenerateCounterLayer(CounterLayerArgs &&args); [[nodiscard]] QImage GenerateCounterLayer(CounterLayerArgs &&args);

View file

@ -1209,18 +1209,16 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
: !currentPeerDate.isNull() : !currentPeerDate.isNull()
? currentPeerDate ? currentPeerDate
: QDate::currentDate(); : QDate::currentDate();
const auto month = highlighted; show(Box<Ui::CalendarBox>(Ui::CalendarBoxArgs{
auto callback = [=](const QDate &date) { .month = highlighted,
session().api().jumpToDate(chat, date); .highlighted = highlighted,
}; .callback = [=](const QDate &date) {
auto box = Box<Ui::CalendarBox>( session().api().jumpToDate(chat, date);
month, },
highlighted, .minDate = minPeerDate(chat),
std::move(callback)); .maxDate = maxPeerDate(chat),
box->setMinDate(minPeerDate(chat)); .hasBeginningButton = true,
box->setMaxDate(maxPeerDate(chat)); }));
box->setBeginningButton(true);
show(std::move(box));
} }
void SessionController::showPassportForm(const Passport::FormRequest &request) { void SessionController::showPassportForm(const Passport::FormRequest &request) {