125 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // This file is part of Desktop App Toolkit,
 | |
| // a set of libraries for developing nice desktop applications.
 | |
| //
 | |
| // For license and copyright information please follow this link:
 | |
| // https://github.com/desktop-app/legal/blob/master/LEGAL
 | |
| //
 | |
| #include "ui/widgets/fields/time_part_input.h"
 | |
| 
 | |
| #include "base/qt/qt_string_view.h"
 | |
| #include "ui/ui_utility.h" // WheelDirection
 | |
| 
 | |
| #include <QtCore/QRegularExpression>
 | |
| 
 | |
| namespace Ui {
 | |
| 
 | |
| std::optional<int> TimePart::number() {
 | |
| 	const auto text = getLastText();
 | |
| 	auto view = QStringView(text);
 | |
| 	while (view.size() > 1 && view.at(0) == '0') {
 | |
| 		view = base::StringViewMid(view, 1);
 | |
| 	}
 | |
| 	return QRegularExpression("^\\d+$").match(view).hasMatch()
 | |
| 		? std::make_optional(view.toInt())
 | |
| 		: std::nullopt;
 | |
| }
 | |
| 
 | |
| void TimePart::setMaxValue(int value) {
 | |
| 	_maxValue = value;
 | |
| 	_maxDigits = 0;
 | |
| 	while (value > 0) {
 | |
| 		++_maxDigits;
 | |
| 		value /= 10;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TimePart::setWheelStep(int value) {
 | |
| 	_wheelStep = value;
 | |
| }
 | |
| 
 | |
| rpl::producer<> TimePart::erasePrevious() const {
 | |
| 	return _erasePrevious.events();
 | |
| }
 | |
| 
 | |
| rpl::producer<QChar> TimePart::putNext() const {
 | |
| 	return _putNext.events();
 | |
| }
 | |
| 
 | |
| void TimePart::keyPressEvent(QKeyEvent *e) {
 | |
| 	const auto isBackspace = (e->key() == Qt::Key_Backspace);
 | |
| 	const auto isBeginning = (cursorPosition() == 0);
 | |
| 	if (isBackspace && isBeginning && !hasSelectedText()) {
 | |
| 		_erasePrevious.fire({});
 | |
| 	} else {
 | |
| 		MaskedInputField::keyPressEvent(e);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TimePart::wheelEvent(QWheelEvent *e) {
 | |
| 	const auto direction = WheelDirection(e);
 | |
| 	const auto now = number();
 | |
| 	if (!now.has_value()) {
 | |
| 		return;
 | |
| 	}
 | |
| 	auto time = *now + (direction * _wheelStep);
 | |
| 	const auto max = _maxValue + 1;
 | |
| 	if (time < 0) {
 | |
| 		time += max;
 | |
| 	} else if (time >= max) {
 | |
| 		time -= max;
 | |
| 	}
 | |
| 	setText(QString::number(time));
 | |
| 	Ui::MaskedInputField::changed();
 | |
| }
 | |
| 
 | |
| void TimePart::correctValue(
 | |
| 		const QString &was,
 | |
| 		int wasCursor,
 | |
| 		QString &now,
 | |
| 		int &nowCursor) {
 | |
| 	auto newText = QString();
 | |
| 	auto newCursor = -1;
 | |
| 	const auto oldCursor = nowCursor;
 | |
| 	const auto oldLength = now.size();
 | |
| 	auto accumulated = 0;
 | |
| 	auto limit = 0;
 | |
| 	for (; limit != oldLength; ++limit) {
 | |
| 		if (now[limit].isDigit()) {
 | |
| 			accumulated *= 10;
 | |
| 			accumulated += (now[limit].unicode() - '0');
 | |
| 			if (accumulated > _maxValue || limit == _maxDigits) {
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	for (auto i = 0; i != limit;) {
 | |
| 		if (now[i].isDigit()) {
 | |
| 			newText += now[i];
 | |
| 		}
 | |
| 		if (++i == oldCursor) {
 | |
| 			newCursor = newText.size();
 | |
| 		}
 | |
| 	}
 | |
| 	if (newCursor < 0) {
 | |
| 		newCursor = newText.size();
 | |
| 	}
 | |
| 	if (newText != now) {
 | |
| 		now = newText;
 | |
| 		setText(now);
 | |
| 		startPlaceholderAnimation();
 | |
| 	}
 | |
| 	if (newCursor != nowCursor) {
 | |
| 		nowCursor = newCursor;
 | |
| 		setCursorPosition(nowCursor);
 | |
| 	}
 | |
| 	if (accumulated > _maxValue
 | |
| 		|| (limit == _maxDigits && oldLength > _maxDigits)) {
 | |
| 		if (oldCursor > limit) {
 | |
| 			_putNext.fire('0' + (accumulated % 10));
 | |
| 		} else {
 | |
| 			_putNext.fire(0);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| } // namespace Ui
 | 
