| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- /*
- 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/choose_date_time.h"
- #include "base/unixtime.h"
- #include "base/event_filter.h"
- #include "ui/boxes/calendar_box.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/fields/input_field.h"
- #include "ui/widgets/time_input.h"
- #include "ui/ui_utility.h"
- #include "lang/lang_keys.h"
- #include "styles/style_layers.h"
- #include "styles/style_boxes.h"
- #include <QtWidgets/QTextEdit>
- namespace Ui {
- namespace {
- constexpr auto kMinimalSchedule = TimeId(10);
- QString DayString(const QDate &date) {
- return tr::lng_month_day(
- tr::now,
- lt_month,
- Lang::MonthDay(date.month())(tr::now),
- lt_day,
- QString::number(date.day()));
- }
- QString TimeString(QTime time) {
- return QString("%1:%2"
- ).arg(time.hour()
- ).arg(time.minute(), 2, 10, QLatin1Char('0'));
- }
- } // namespace
- ChooseDateTimeStyleArgs::ChooseDateTimeStyleArgs()
- : labelStyle(&st::boxLabel)
- , dateFieldStyle(&st::scheduleDateField)
- , timeFieldStyle(&st::scheduleTimeField)
- , separatorStyle(&st::scheduleTimeSeparator)
- , atStyle(&st::scheduleAtLabel)
- , calendarStyle(&st::defaultCalendarColors) {
- }
- ChooseDateTimeBoxDescriptor ChooseDateTimeBox(
- not_null<GenericBox*> box,
- ChooseDateTimeBoxArgs &&args) {
- struct State {
- rpl::variable<QDate> date;
- not_null<InputField*> day;
- not_null<TimeInput*> time;
- not_null<FlatLabel*> at;
- };
- box->setTitle(std::move(args.title));
- box->setWidth(st::boxWideWidth);
- const auto content = box->addRow(
- object_ptr<FixedHeightWidget>(box, st::scheduleHeight));
- if (args.description) {
- box->addRow(object_ptr<FlatLabel>(
- box,
- std::move(args.description),
- *args.style.labelStyle));
- }
- const auto parsed = base::unixtime::parse(args.time);
- const auto state = box->lifetime().make_state<State>(State{
- .date = parsed.date(),
- .day = CreateChild<InputField>(
- content,
- *args.style.dateFieldStyle),
- .time = CreateChild<TimeInput>(
- content,
- TimeString(parsed.time()),
- *args.style.timeFieldStyle,
- *args.style.dateFieldStyle,
- *args.style.separatorStyle,
- st::scheduleTimeSeparatorPadding),
- .at = CreateChild<FlatLabel>(
- content,
- tr::lng_schedule_at(),
- *args.style.atStyle),
- });
- state->date.value(
- ) | rpl::start_with_next([=](QDate date) {
- state->day->setText(DayString(date));
- state->time->setFocusFast();
- }, state->day->lifetime());
- const auto min = args.min ? args.min : [] {
- return base::unixtime::now() + kMinimalSchedule;
- };
- const auto max = args.max ? args.max : [] {
- return base::unixtime::serialize(
- QDateTime::currentDateTime().addYears(1)) - 1;
- };
- const auto minDate = [=] {
- return base::unixtime::parse(min()).date();
- };
- const auto maxDate = [=] {
- return base::unixtime::parse(max()).date();
- };
- const auto &dayViewport = state->day->rawTextEdit()->viewport();
- base::install_event_filter(dayViewport, [=](not_null<QEvent*> event) {
- if (event->type() == QEvent::Wheel) {
- const auto e = static_cast<QWheelEvent*>(event.get());
- const auto direction = Ui::WheelDirection(e);
- if (!direction) {
- return base::EventFilterResult::Continue;
- }
- const auto d = state->date.current().addDays(direction);
- state->date = std::clamp(d, minDate(), maxDate());
- return base::EventFilterResult::Cancel;
- }
- return base::EventFilterResult::Continue;
- });
- content->widthValue(
- ) | rpl::start_with_next([=](int width) {
- const auto paddings = width
- - state->at->width()
- - 2 * st::scheduleAtSkip
- - st::scheduleDateWidth
- - st::scheduleTimeWidth;
- const auto left = paddings / 2;
- state->day->resizeToWidth(st::scheduleDateWidth);
- state->day->moveToLeft(left, st::scheduleDateTop, width);
- state->at->moveToLeft(
- left + st::scheduleDateWidth + st::scheduleAtSkip,
- st::scheduleAtTop,
- width);
- state->time->resizeToWidth(st::scheduleTimeWidth);
- state->time->moveToLeft(
- width - left - st::scheduleTimeWidth,
- st::scheduleDateTop,
- width);
- }, content->lifetime());
- const auto calendar
- = content->lifetime().make_state<QPointer<CalendarBox>>();
- const auto calendarStyle = args.style.calendarStyle;
- state->day->focusedChanges(
- ) | rpl::start_with_next([=](bool focused) {
- if (*calendar || !focused) {
- return;
- }
- *calendar = box->getDelegate()->show(
- Box<CalendarBox>(Ui::CalendarBoxArgs{
- .month = state->date.current(),
- .highlighted = state->date.current(),
- .callback = crl::guard(box, [=](QDate chosen) {
- state->date = chosen;
- (*calendar)->closeBox();
- }),
- .minDate = minDate(),
- .maxDate = maxDate(),
- .stColors = *calendarStyle,
- }));
- (*calendar)->boxClosing(
- ) | rpl::start_with_next(crl::guard(state->time, [=] {
- state->time->setFocusFast();
- }), (*calendar)->lifetime());
- }, state->day->lifetime());
- const auto collect = [=] {
- const auto timeValue = state->time->valueCurrent().split(':');
- if (timeValue.size() != 2) {
- return 0;
- }
- const auto time = QTime(timeValue[0].toInt(), timeValue[1].toInt());
- if (!time.isValid()) {
- return 0;
- }
- const auto result = base::unixtime::serialize(
- QDateTime(state->date.current(), time));
- if (result < min() || result > max()) {
- return 0;
- }
- return result;
- };
- const auto save = [=, done = args.done] {
- if (const auto result = collect()) {
- done(result);
- } else {
- state->time->showError();
- }
- };
- state->time->submitRequests(
- ) | rpl::start_with_next(save, state->time->lifetime());
- auto result = ChooseDateTimeBoxDescriptor();
- box->setFocusCallback([=] { state->time->setFocusFast(); });
- result.submit = box->addButton(std::move(args.submit), save);
- result.collect = [=] {
- if (const auto result = collect()) {
- return result;
- }
- state->time->showError();
- return 0;
- };
- result.values = rpl::combine(
- state->date.value(),
- state->time->value()
- ) | rpl::map(collect);
- box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
- return result;
- }
- } // namespace Ui
|