AyuGramDesktop/Telegram/SourceFiles/ui/boxes/edit_birthday_box.cpp
2024-04-01 22:44:21 +04:00

232 lines
6.5 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/boxes/edit_birthday_box.h"
#include "base/event_filter.h"
#include "data/data_birthday.h"
#include "lang/lang_keys.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/vertical_drum_picker.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include <QtCore/QDate>
namespace Ui {
class GenericBox;
void EditBirthdayBox(
not_null<Ui::GenericBox*> box,
Data::Birthday current,
Fn<void(Data::Birthday)> save) {
box->setWidth(st::boxWideWidth);
const auto content = box->addRow(object_ptr<Ui::FixedHeightWidget>(
box,
st::settingsWorkingHoursPicker));
const auto font = st::boxTextFont;
const auto itemHeight = st::settingsWorkingHoursPickerItemHeight;
const auto picker = [=](
int count,
int startIndex,
Fn<void(QPainter &p, QRectF rect, int index)> 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();
};
return Ui::CreateChild<Ui::VerticalDrumPicker>(
content,
std::move(paintCallback),
count,
itemHeight,
startIndex);
};
const auto nowDate = QDate::currentDate();
const auto nowYear = nowDate.year();
const auto nowMonth = nowDate.month();
const auto nowDay = nowDate.day();
const auto now = Data::Birthday(nowDay, nowMonth, nowYear);
const auto max = current.year() ? std::max(now, current) : now;
const auto maxYear = max.year();
const auto minYear = Data::Birthday::kYearMin;
const auto yearsCount = (maxYear - minYear + 2); // Last - not set.
const auto yearsStartIndex = current.year()
? (current.year() - minYear)
: (yearsCount - 1);
const auto yearsPaint = [=](QPainter &p, QRectF rect, int index) {
p.drawText(
rect,
(index < yearsCount - 1
? QString::number(minYear + index)
: QString::fromUtf8("\xe2\x80\x94")),
style::al_center);
};
const auto years = picker(yearsCount, yearsStartIndex, yearsPaint);
struct State {
rpl::variable<Ui::VerticalDrumPicker*> months;
rpl::variable<Ui::VerticalDrumPicker*> days;
};
const auto state = content->lifetime().make_state<State>();
// years->value() is valid only after size is set.
rpl::combine(
content->sizeValue(),
state->months.value(),
state->days.value()
) | rpl::start_with_next([=](
QSize s,
Ui::VerticalDrumPicker *months,
Ui::VerticalDrumPicker *days) {
const auto half = s.width() / 2;
years->setGeometry(half * 3 / 2, 0, half / 2, s.height());
if (months) {
months->setGeometry(half / 2, 0, half, s.height());
}
if (days) {
days->setGeometry(0, 0, half / 2, s.height());
}
}, content->lifetime());
Ui::SendPendingMoveResizeEvents(years);
years->value() | rpl::start_with_next([=](int yearsIndex) {
const auto year = (yearsIndex == yearsCount - 1)
? 0
: minYear + yearsIndex;
const auto monthsCount = (year == maxYear)
? max.month()
: 12;
const auto monthsStartIndex = std::clamp(
(state->months.current()
? state->months.current()->index()
: current.month()
? (current.month() - 1)
: (now.month() - 1)),
0,
monthsCount - 1);
const auto monthsPaint = [=](QPainter &p, QRectF rect, int index) {
p.drawText(
rect,
Lang::Month(index + 1)(tr::now),
style::al_center);
};
const auto updated = picker(
monthsCount,
monthsStartIndex,
monthsPaint);
delete state->months.current();
state->months = updated;
state->months.current()->show();
}, years->lifetime());
Ui::SendPendingMoveResizeEvents(state->months.current());
state->months.value() | rpl::map([=](Ui::VerticalDrumPicker *picker) {
return picker ? picker->value() : rpl::single(current.month()
? (current.month() - 1)
: (now.month() - 1));
}) | rpl::flatten_latest() | rpl::start_with_next([=](int monthIndex) {
const auto month = monthIndex + 1;
const auto yearsIndex = years->index();
const auto year = (yearsIndex == yearsCount - 1)
? 0
: minYear + yearsIndex;
const auto daysCount = (year == maxYear && month == max.month())
? max.day()
: (month == 2)
? ((!year || ((year % 4) && (!(year % 100) || (year % 400))))
? 29
: 28)
: ((month == 4) || (month == 6) || (month == 9) || (month == 11))
? 30
: 31;
const auto daysStartIndex = std::clamp(
(state->days.current()
? state->days.current()->index()
: current.day()
? (current.day() - 1)
: (now.day() - 1)),
0,
daysCount - 1);
const auto daysPaint = [=](QPainter &p, QRectF rect, int index) {
p.drawText(rect, QString::number(index + 1), style::al_center);
};
const auto updated = picker(
daysCount,
daysStartIndex,
daysPaint);
delete state->days.current();
state->days = updated;
state->days.current()->show();
}, years->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());
base::install_event_filter(box, [=](not_null<QEvent*> e) {
if (e->type() == QEvent::KeyPress) {
years->handleKeyEvent(static_cast<QKeyEvent*>(e.get()));
}
return base::EventFilterResult::Continue;
});
box->addButton(tr::lng_settings_save(), [=] {
const auto result = Data::Birthday(
state->days.current()->index() + 1,
state->months.current()->index() + 1,
((years->index() == yearsCount - 1)
? 0
: minYear + years->index()));
box->closeBox();
save(result);
});
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
if (current) {
box->addLeftButton(tr::lng_settings_birthday_reset(), [=] {
box->closeBox();
save(Data::Birthday());
});
}
}
} // namespace Ui