diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index 39c48f7ad..056aa224e 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -571,7 +571,7 @@ bool AttachWebView::botHandleLocalUri(QString uri, bool keepOpen) { Core::App().domain().activate(&bot->session().account()); } const auto window = !bot->session().windows().empty() - ? bot->session().windows().front() + ? bot->session().windows().front().get() : nullptr; const auto variant = QVariant::fromValue(ClickHandlerContext{ .attachBotWebviewUrl = shownUrl, diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp index 1d06c99ce..db30dd847 100644 --- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp +++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp @@ -86,6 +86,34 @@ private: : wrap(time == kDay ? 0 : time); } +[[nodiscard]] QString FormatTimeHour(TimeId time) { + const auto wrap = [](TimeId value) { + return QString::number(value / 3600).rightJustified(2, u'0'); + }; + if (time < kDay) { + return wrap(time); + } + const auto wrapped = wrap(time - kDay); + const auto result = tr::lng_hours_next_day(tr::now, lt_time, wrapped); + const auto i = result.indexOf(wrapped); + return (i >= 0) ? (result.left(i) + wrapped) : result; +} + +[[nodiscard]] QString FormatTimeMinute(TimeId time) { + const auto wrap = [](TimeId value) { + return QString::number(value / 60).rightJustified(2, u'0'); + }; + if (time < kDay) { + return wrap(time); + } + const auto wrapped = wrap(time - kDay); + const auto result = tr::lng_hours_next_day(tr::now, lt_time, wrapped); + const auto i = result.indexOf(wrapped); + return (i >= 0) + ? (wrapped + result.right(result.size() - i - wrapped.size())) + : result; +} + [[nodiscard]] QString JoinIntervals(const Data::WorkingIntervals &data) { auto result = QStringList(); result.reserve(data.list.size()); @@ -105,49 +133,97 @@ void EditTimeBox( Fn save) { Expects(low <= high); - const auto values = (high - low + 60) / 60; - const auto startIndex = (value - low) / 60; - const auto content = box->addRow(object_ptr( box, st::settingsWorkingHoursPicker)); const auto font = st::boxTextFont; const auto itemHeight = st::settingsWorkingHoursPickerItemHeight; - 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); - p.drawText(r, FormatDayTime(low + index * 60, true), style::al_center); - p.restore(); + const auto picker = [=]( + int count, + int startIndex, + Fn 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( + content, + std::move(paintCallback), + count, + itemHeight, + startIndex); }; - const auto picker = Ui::CreateChild( - content, - std::move(paintCallback), - values, - itemHeight, - startIndex); + const auto hoursCount = (high - low + 3600) / 3600; + const auto hoursStartIndex = (value - low) / 3600; + const auto hoursPaint = [=](QPainter &p, QRectF rect, int index) { + p.drawText( + rect, + FormatTimeHour(((low / 3600) + index) * 3600), + style::al_right); + }; + const auto hours = picker(hoursCount, hoursStartIndex, hoursPaint); + const auto minutes = content->lifetime().make_state< + rpl::variable + >(nullptr); + const auto minutesStart = content->lifetime().make_state(); + hours->value() | rpl::start_with_next([=](int hoursIndex) { + const auto start = std::max(low, (hoursIndex + (low / 3600)) * 3600); + const auto end = std::min(high, ((start / 3600) * 60 + 59) * 60); + const auto minutesCount = (end - start + 60) / 60; + const auto minutesStartIndex = minutes->current() + ? std::clamp( + ((((*minutesStart) / 60 + minutes->current()->index()) % 60) + - ((start / 60) % 60)), + 0, + (minutesCount - 1)) + : std::clamp((value - start) / 60, 0, minutesCount - 1); + *minutesStart = start; - content->sizeValue( - ) | rpl::start_with_next([=](const QSize &s) { - picker->resize(s.width(), s.height()); - picker->moveToLeft((s.width() - picker->width()) / 2, 0); + const auto minutesPaint = [=](QPainter &p, QRectF rect, int index) { + p.drawText( + rect, + FormatTimeMinute((((start / 60) + index) % 60) * 60), + style::al_left); + }; + const auto updated = picker( + minutesCount, + minutesStartIndex, + minutesPaint); + delete minutes->current(); + *minutes = updated; + minutes->current()->show(); + }, hours->lifetime()); + + const auto separator = u":"_q; + const auto separatorWidth = st::boxTextFont->width(separator); + + rpl::combine( + content->sizeValue(), + minutes->value() + ) | rpl::start_with_next([=](QSize s, Ui::VerticalDrumPicker *minutes) { + const auto half = (s.width() - separatorWidth) / 2; + hours->setGeometry(0, 0, half, s.height()); + minutes->setGeometry(half + separatorWidth, 0, half, s.height()); }, content->lifetime()); content->paintRequest( @@ -163,28 +239,22 @@ void EditTimeBox( st::defaultInputField.borderActive); p.fillRect(lineRect.translated(0, itemHeight / 2), st::activeLineFg); p.fillRect(lineRect.translated(0, -itemHeight / 2), st::activeLineFg); + p.drawText(QRectF(content->rect()), separator, style::al_center); }, content->lifetime()); - base::install_event_filter(content, [=](not_null e) { - if ((e->type() == QEvent::MouseButtonPress) - || (e->type() == QEvent::MouseButtonRelease) - || (e->type() == QEvent::MouseMove)) { - picker->handleMouseEvent(static_cast(e.get())); - } else if (e->type() == QEvent::Wheel) { - picker->handleWheelEvent(static_cast(e.get())); - } - return base::EventFilterResult::Continue; - }); base::install_event_filter(box, [=](not_null e) { if (e->type() == QEvent::KeyPress) { - picker->handleKeyEvent(static_cast(e.get())); + hours->handleKeyEvent(static_cast(e.get())); } return base::EventFilterResult::Continue; }); box->addButton(tr::lng_settings_save(), [=] { const auto weak = Ui::MakeWeak(box); - save(std::clamp(low + picker->index() * 60, low, high)); + save(std::clamp( + ((*minutesStart) / 60 + minutes->current()->index()) * 60, + low, + high)); if (const auto strong = weak.data()) { strong->closeBox(); } diff --git a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp index 58fd123ea..73f2ecf8c 100644 --- a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp +++ b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp @@ -92,6 +92,8 @@ VerticalDrumPicker::VerticalDrumPicker( _loopData.minIndex = -_itemsVisible.centerOffset; _loopData.maxIndex = _itemsCount - 1 - _itemsVisible.centerOffset; } + + _changes.fire({}); }, lifetime()); paintRequest( @@ -144,7 +146,9 @@ void VerticalDrumPicker::increaseShift(float64 by) { index++; index = normalizedIndex(index); } - if (!_loopData.looped && (index <= _loopData.minIndex)) { + if (_loopData.minIndex == _loopData.maxIndex) { + _shift = 0.; + } else if (!_loopData.looped && (index <= _loopData.minIndex)) { _shift = std::min(0., shift); _index = _loopData.minIndex; } else if (!_loopData.looped && (index >= _loopData.maxIndex)) { @@ -154,6 +158,7 @@ void VerticalDrumPicker::increaseShift(float64 by) { _shift = shift; _index = index; } + _changes.fire({}); update(); } @@ -270,4 +275,14 @@ int VerticalDrumPicker::index() const { return normalizedIndex(_index + _itemsVisible.centerOffset); } +rpl::producer VerticalDrumPicker::changes() const { + return _changes.events() | rpl::map([=] { return index(); }); +} + +rpl::producer VerticalDrumPicker::value() const { + return rpl::single(index()) + | rpl::then(changes()) + | rpl::distinct_until_changed(); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h index 4140d3397..63c0cbc2a 100644 --- a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h +++ b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h @@ -52,6 +52,8 @@ public: bool looped = false); [[nodiscard]] int index() const; + [[nodiscard]] rpl::producer changes() const; + [[nodiscard]] rpl::producer value() const; void handleWheelEvent(not_null e); void handleMouseEvent(not_null e); @@ -84,6 +86,7 @@ private: int _index = 0; float64 _shift = 0.; + rpl::event_stream<> _changes; struct { const bool looped;