Added month and year picker to calendar box by clicking on title.

This commit is contained in:
23rd
2025-10-07 11:11:50 +03:00
parent 7587bbd4bc
commit 70d8cede9b
+201 -9
View File
@@ -9,14 +9,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/vertical_drum_picker.h"
#include "ui/effects/ripple_animation.h"
#include "ui/chat/chat_style.h"
#include "ui/ui_utility.h"
#include "ui/painter.h"
#include "ui/cached_round_corners.h"
#include "ui/layers/generic_box.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
#include <QtCore/QLocale>
@@ -32,6 +36,172 @@ constexpr auto kJumpDelay = 2 * crl::time(1000);
return (kDaysInWeek + date.dayOfWeek() - firstDayOfWeek) % kDaysInWeek;
}
void FillMonthYearPicker(
not_null<GenericBox*> box,
QDate current,
QDate minDate,
QDate maxDate,
const style::CalendarSizes &st,
Fn<void(QDate)> done) {
box->setWidth(st::boxWideWidth);
const auto content = box->addRow(
object_ptr<FixedHeightWidget>(box, st::settingsWorkingHoursPicker));
const auto font = st::boxTextFont;
const auto itemHeight = st::settingsWorkingHoursPickerItemHeight;
const auto picker = [=](
int count,
int startIndex,
Fn<void(QPainter&, QRectF, int)> paint) {
auto paintCallback = [=](
QPainter &p,
int index,
float64 y,
float64 distanceFromCenter,
int outerWidth) {
const auto r = QRectF(0, y, outerWidth, itemHeight);
const auto progress = std::abs(distanceFromCenter);
const auto revProgress = 1. - progress;
p.save();
p.translate(r.center());
constexpr auto kMinYScale = 0.2;
const auto yScale = kMinYScale
+ (1. - kMinYScale) * anim::easeOutCubic(1., revProgress);
p.scale(1., yScale);
p.translate(-r.center());
p.setOpacity(revProgress);
p.setFont(font);
p.setPen(st::defaultFlatLabel.textFg);
paint(p, r, index);
p.restore();
};
const auto result = CreateChild<VerticalDrumPicker>(
content,
std::move(paintCallback),
count,
itemHeight,
startIndex);
result->show();
return result;
};
const auto effectiveMaxDate = maxDate.isValid()
? maxDate
: QDate::currentDate().addDays(365);
const auto minYear = minDate.isValid() ? minDate.year() : 2013;
const auto maxYear = effectiveMaxDate.year();
const auto yearsCount = maxYear - minYear + 1;
const auto yearsStartIndex = current.year() - minYear;
const auto yearsPaint = [=](QPainter &p, QRectF rect, int index) {
p.drawText(rect, QString::number(minYear + index), style::al_center);
};
const auto years = picker(yearsCount, yearsStartIndex, yearsPaint);
const auto monthsRange = [=](int year) {
auto minMonth = 1;
auto maxMonth = 12;
if (minDate.isValid() && minDate.year() == year) {
minMonth = minDate.month();
}
if (maxDate.isValid() && maxDate.year() == year) {
maxMonth = maxDate.month();
}
return std::make_pair(minMonth, maxMonth);
};
struct State {
base::unique_qptr<VerticalDrumPicker> months;
int currentMinMonth = 0;
int currentMaxMonth = 0;
};
const auto state = box->lifetime().make_state<State>();
const auto [minMonth, maxMonth] = monthsRange(current.year());
const auto monthsCount = maxMonth - minMonth + 1;
const auto monthsStartIndex = current.month() - minMonth;
const auto monthsPaint = [=, minMonth = minMonth](
QPainter &p,
QRectF rect,
int index) {
p.drawText(
rect,
Lang::Month(minMonth + index)(tr::now),
style::al_center);
};
state->months = base::unique_qptr<VerticalDrumPicker>(
picker(monthsCount, monthsStartIndex, monthsPaint));
state->currentMinMonth = minMonth;
state->currentMaxMonth = maxMonth;
years->value(
) | rpl::skip(1) | rpl::start_with_next([=](int yearIndex) {
const auto year = minYear + yearIndex;
const auto [newMinMonth, newMaxMonth] = monthsRange(year);
if (newMinMonth != state->currentMinMonth
|| newMaxMonth != state->currentMaxMonth) {
const auto newMonthsCount = newMaxMonth - newMinMonth + 1;
const auto oldMonth = state->currentMinMonth
+ state->months->index();
const auto clampedMonth = std::clamp(
oldMonth - newMinMonth,
0,
newMonthsCount - 1);
const auto newMonthsPaint = [=, minMonth = newMinMonth](
QPainter &p,
QRectF rect,
int index) {
p.drawText(
rect,
Lang::Month(minMonth + index)(tr::now),
style::al_center);
};
state->months = base::unique_qptr<VerticalDrumPicker>(
picker(newMonthsCount, clampedMonth, newMonthsPaint));
state->currentMinMonth = newMinMonth;
state->currentMaxMonth = newMaxMonth;
const auto s = content->size();
if (s.isValid()) {
const auto half = s.width() / 2;
state->months->setGeometry(0, 0, half, s.height());
}
}
}, box->lifetime());
content->sizeValue(
) | rpl::start_with_next([=](QSize s) {
const auto half = s.width() / 2;
state->months->setGeometry(0, 0, half, s.height());
years->setGeometry(half, 0, half, s.height());
}, content->lifetime());
content->paintRequest(
) | rpl::start_with_next([=](const QRect &r) {
auto p = QPainter(content);
p.fillRect(r, Qt::transparent);
const auto lineRect = QRect(
0,
content->height() / 2,
content->width(),
st::defaultInputField.borderActive);
p.fillRect(
lineRect.translated(0, itemHeight / 2),
st::activeLineFg);
p.fillRect(
lineRect.translated(0, -itemHeight / 2),
st::activeLineFg);
}, content->lifetime());
box->addButton(tr::lng_gift_menu_show(), [=] {
const auto year = minYear + years->index();
const auto [minMonth, maxMonth] = monthsRange(year);
const auto month = minMonth + state->months->index();
done(QDate(year, month, 1));
box->closeBox();
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
} // namespace
class CalendarBox::Context {
@@ -755,7 +925,7 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) {
CalendarBox::Inner::~Inner() = default;
class CalendarBox::Title final : public RpWidget {
class CalendarBox::Title final : public AbstractButton {
public:
Title(
QWidget *parent,
@@ -786,7 +956,7 @@ CalendarBox::Title::Title(
not_null<Context*> context,
const style::CalendarSizes &st,
const style::CalendarColors &styleColors)
: RpWidget(parent)
: AbstractButton(parent)
, _st(st)
, _styleColors(styleColors)
, _context(context) {
@@ -804,13 +974,18 @@ CalendarBox::Title::Title(
) | rpl::start_with_next([=] {
if (!_context->selectionMode()) {
setTextFromMonth(_context->month());
} else if (!_context->selectedMin()) {
setText(tr::lng_calendar_select_days(tr::now));
setAttribute(Qt::WA_TransparentForMouseEvents, false);
} else {
setText(tr::lng_calendar_days(
tr::now,
lt_count,
(1 + *_context->selectedMax() - *_context->selectedMin())));
setAttribute(Qt::WA_TransparentForMouseEvents, true);
if (!_context->selectedMin()) {
setText(tr::lng_calendar_select_days(tr::now));
} else {
setText(tr::lng_calendar_days(
tr::now,
lt_count,
(1 + *_context->selectedMax()
- *_context->selectedMin())));
}
}
}, lifetime());
}
@@ -879,11 +1054,28 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args)
, _finalize(std::move(args.finalize))
, _jumpTimer([=] { jump(_jumpButton); })
, _selectionChanged(std::move(args.selectionChanged)) {
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
_context->setAllowsSelection(args.allowsSelection);
_context->setMinDate(args.minDate);
_context->setMaxDate(args.maxDate);
_title->setClickedCallback([=,
minDate = args.minDate,
maxDate = args.maxDate] {
BoxContent::uiShow()->show(Box([=](not_null<GenericBox*> box) {
FillMonthYearPicker(
box,
_context->month(),
minDate,
maxDate,
_st,
[=](QDate date) {
_watchScroll = false;
_context->showMonth(date);
setExactScroll();
});
}), LayerOption::KeepOther);
});
_scroll->scrolls(
) | rpl::filter([=] {
return _watchScroll;