choose_time.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "ui/boxes/choose_time.h"
  8. #include "base/qt_signal_producer.h"
  9. #include "ui/ui_utility.h"
  10. #include "ui/widgets/fields/time_part_input_with_placeholder.h"
  11. #include "ui/wrap/padding_wrap.h"
  12. #include "styles/style_boxes.h"
  13. #include "styles/style_layers.h"
  14. namespace Ui {
  15. ChooseTimeResult ChooseTimeWidget(
  16. not_null<RpWidget*> parent,
  17. TimeId startSeconds,
  18. bool hiddenDaysInput) {
  19. using TimeField = Ui::TimePartWithPlaceholder;
  20. const auto putNext = [](not_null<TimeField*> field, QChar ch) {
  21. field->setCursorPosition(0);
  22. if (ch.unicode()) {
  23. field->setText(ch + field->getLastText());
  24. field->setCursorPosition(1);
  25. }
  26. field->onTextEdited();
  27. field->setFocus();
  28. };
  29. const auto erasePrevious = [](not_null<TimeField*> field) {
  30. const auto text = field->getLastText();
  31. if (!text.isEmpty()) {
  32. field->setCursorPosition(text.size() - 1);
  33. field->setText(text.mid(0, text.size() - 1));
  34. }
  35. field->setFocus();
  36. };
  37. struct State {
  38. not_null<TimeField*> day;
  39. not_null<TimeField*> hour;
  40. not_null<TimeField*> minute;
  41. rpl::variable<int> valueInSeconds = 0;
  42. };
  43. auto content = object_ptr<Ui::FixedHeightWidget>(
  44. parent,
  45. st::scheduleHeight);
  46. const auto startDays = startSeconds / 86400;
  47. startSeconds -= startDays * 86400;
  48. const auto startHours = startSeconds / 3600;
  49. startSeconds -= startHours * 3600;
  50. const auto startMinutes = startSeconds / 60;
  51. const auto state = content->lifetime().make_state<State>(State{
  52. .day = Ui::CreateChild<TimeField>(
  53. content.data(),
  54. st::muteBoxTimeField,
  55. rpl::never<QString>(),
  56. QString::number(startDays)),
  57. .hour = Ui::CreateChild<TimeField>(
  58. content.data(),
  59. st::muteBoxTimeField,
  60. rpl::never<QString>(),
  61. QString::number(startHours)),
  62. .minute = Ui::CreateChild<TimeField>(
  63. content.data(),
  64. st::muteBoxTimeField,
  65. rpl::never<QString>(),
  66. QString::number(startMinutes)),
  67. });
  68. const auto day = Ui::MakeWeak(state->day);
  69. const auto hour = Ui::MakeWeak(state->hour);
  70. const auto minute = Ui::MakeWeak(state->minute);
  71. if (hiddenDaysInput) {
  72. day->setVisible(false);
  73. }
  74. day->setPhrase(tr::lng_days);
  75. day->setMaxValue(31);
  76. day->setWheelStep(1);
  77. day->putNext() | rpl::start_with_next([=](QChar ch) {
  78. putNext(hour, ch);
  79. }, content->lifetime());
  80. hour->setPhrase(tr::lng_hours);
  81. hour->setMaxValue(23);
  82. hour->setWheelStep(1);
  83. hour->putNext() | rpl::start_with_next([=](QChar ch) {
  84. putNext(minute, ch);
  85. }, content->lifetime());
  86. hour->erasePrevious() | rpl::start_with_next([=] {
  87. erasePrevious(day);
  88. }, content->lifetime());
  89. minute->setPhrase(tr::lng_minutes);
  90. minute->setMaxValue(59);
  91. minute->setWheelStep(10);
  92. minute->erasePrevious() | rpl::start_with_next([=] {
  93. erasePrevious(hour);
  94. }, content->lifetime());
  95. content->sizeValue(
  96. ) | rpl::start_with_next([=](const QSize &s) {
  97. const auto inputWidth = s.width() / (hiddenDaysInput ? 2 : 3);
  98. auto rect = QRect(
  99. 0,
  100. (s.height() - day->height()) / 2,
  101. inputWidth,
  102. day->height());
  103. for (const auto &input : { day, hour, minute }) {
  104. if (input->isHidden()) {
  105. continue;
  106. }
  107. input->setGeometry(rect - st::muteBoxTimeFieldPadding);
  108. rect.translate(inputWidth, 0);
  109. }
  110. }, content->lifetime());
  111. rpl::merge(
  112. rpl::single(rpl::empty),
  113. base::qt_signal_producer(day.data(), &MaskedInputField::changed),
  114. base::qt_signal_producer(hour.data(), &MaskedInputField::changed),
  115. base::qt_signal_producer(minute.data(), &MaskedInputField::changed)
  116. ) | rpl::start_with_next([=] {
  117. state->valueInSeconds = 0
  118. + day->getLastText().toUInt() * 3600 * 24
  119. + hour->getLastText().toUInt() * 3600
  120. + minute->getLastText().toUInt() * 60;
  121. }, content->lifetime());
  122. return {
  123. object_ptr<Ui::RpWidget>::fromRaw(content.release()),
  124. state->valueInSeconds.value(),
  125. };
  126. }
  127. } // namespace Ui