time_part_input.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/widgets/fields/time_part_input.h"
  8. #include "base/qt/qt_string_view.h"
  9. #include "ui/ui_utility.h" // WheelDirection
  10. #include <QtCore/QRegularExpression>
  11. namespace Ui {
  12. std::optional<int> TimePart::number() {
  13. static const auto RegExp = QRegularExpression("^\\d+$");
  14. const auto text = getLastText();
  15. auto view = QStringView(text);
  16. while (view.size() > 1 && view.at(0) == '0') {
  17. view = base::StringViewMid(view, 1);
  18. }
  19. return RegExp.match(view).hasMatch()
  20. ? std::make_optional(view.toInt())
  21. : std::nullopt;
  22. }
  23. void TimePart::setMaxValue(int value) {
  24. _maxValue = value;
  25. _maxDigits = 0;
  26. while (value > 0) {
  27. ++_maxDigits;
  28. value /= 10;
  29. }
  30. }
  31. void TimePart::setWheelStep(int value) {
  32. _wheelStep = value;
  33. }
  34. rpl::producer<> TimePart::erasePrevious() const {
  35. return _erasePrevious.events();
  36. }
  37. rpl::producer<> TimePart::jumpToPrevious() const {
  38. return _jumpToPrevious.events();
  39. }
  40. rpl::producer<QChar> TimePart::putNext() const {
  41. return _putNext.events();
  42. }
  43. void TimePart::keyPressEvent(QKeyEvent *e) {
  44. const auto position = cursorPosition();
  45. const auto selection = hasSelectedText();
  46. if (!selection && !position) {
  47. if (e->key() == Qt::Key_Backspace) {
  48. _erasePrevious.fire({});
  49. return;
  50. } else if (e->key() == Qt::Key_Left) {
  51. _jumpToPrevious.fire({});
  52. return;
  53. }
  54. } else if (!selection && position == getLastText().size()) {
  55. if (e->key() == Qt::Key_Right) {
  56. _putNext.fire(QChar(0));
  57. return;
  58. }
  59. }
  60. MaskedInputField::keyPressEvent(e);
  61. }
  62. void TimePart::wheelEvent(QWheelEvent *e) {
  63. const auto direction = WheelDirection(e);
  64. const auto now = number();
  65. if (!now.has_value()) {
  66. return;
  67. }
  68. auto time = *now + (direction * _wheelStep);
  69. const auto max = _maxValue + 1;
  70. if (time < 0) {
  71. time += max;
  72. } else if (time >= max) {
  73. time -= max;
  74. }
  75. setText(QString::number(time));
  76. Ui::MaskedInputField::changed();
  77. }
  78. void TimePart::correctValue(
  79. const QString &was,
  80. int wasCursor,
  81. QString &now,
  82. int &nowCursor) {
  83. const auto oldCursor = nowCursor;
  84. const auto oldLength = now.size();
  85. auto newCursor = (oldCursor > 0) ? -1 : 0;
  86. auto newText = QString();
  87. auto accumulated = 0;
  88. auto limit = 0;
  89. for (; limit != oldLength; ++limit) {
  90. if (now[limit].isDigit()) {
  91. accumulated *= 10;
  92. accumulated += (now[limit].unicode() - '0');
  93. if (accumulated > _maxValue || limit == _maxDigits) {
  94. break;
  95. }
  96. }
  97. }
  98. for (auto i = 0; i != limit;) {
  99. if (now[i].isDigit()) {
  100. newText += now[i];
  101. }
  102. if (++i == oldCursor) {
  103. newCursor = newText.size();
  104. }
  105. }
  106. if (newCursor < 0) {
  107. newCursor = newText.size();
  108. }
  109. if (newText != now) {
  110. now = newText;
  111. setText(now);
  112. startPlaceholderAnimation();
  113. }
  114. if (newCursor != nowCursor) {
  115. nowCursor = newCursor;
  116. setCursorPosition(nowCursor);
  117. }
  118. if (accumulated > _maxValue
  119. || (limit == _maxDigits && oldLength > _maxDigits)) {
  120. if (oldCursor > limit) {
  121. _putNext.fire(QChar('0' + (accumulated % 10)));
  122. } else {
  123. _putNext.fire(QChar(0));
  124. }
  125. }
  126. }
  127. } // namespace Ui