| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
- /*
- 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 "media/player/media_player_dropdown.h"
- #include "base/invoke_queued.h"
- #include "base/timer.h"
- #include "lang/lang_keys.h"
- #include "media/player/media_player_button.h"
- #include "ui/cached_round_corners.h"
- #include "ui/widgets/menu/menu.h"
- #include "ui/widgets/menu/menu_action.h"
- #include "ui/widgets/continuous_sliders.h"
- #include "ui/widgets/dropdown_menu.h"
- #include "ui/widgets/shadow.h"
- #include "ui/painter.h"
- #include "ui/ui_utility.h"
- #include "styles/style_media_player.h"
- #include "styles/style_widgets.h"
- namespace Media::Player {
- namespace {
- constexpr auto kSpeedDebounceTimeout = crl::time(1000);
- [[nodiscard]] float64 SpeedToSliderValue(float64 speed) {
- return (speed - kSpeedMin) / (kSpeedMax - kSpeedMin);
- }
- [[nodiscard]] float64 SliderValueToSpeed(float64 value) {
- const auto speed = value * (kSpeedMax - kSpeedMin) + kSpeedMin;
- return base::SafeRound(speed * 10) / 10.;
- }
- constexpr auto kSpeedStickedValues
- = std::array<std::pair<float64, float64>, 7>{{
- { 0.8, 0.05 },
- { 1.0, 0.05 },
- { 1.2, 0.05 },
- { 1.5, 0.05 },
- { 1.7, 0.05 },
- { 2.0, 0.05 },
- { 2.2, 0.05 },
- }};
- class SpeedSliderItem final : public Ui::Menu::ItemBase {
- public:
- SpeedSliderItem(
- not_null<RpWidget*> parent,
- const style::MediaSpeedMenu &st,
- rpl::producer<float64> value);
- not_null<QAction*> action() const override;
- bool isEnabled() const override;
- [[nodiscard]] float64 current() const;
- [[nodiscard]] rpl::producer<float64> changing() const;
- [[nodiscard]] rpl::producer<float64> changed() const;
- [[nodiscard]] rpl::producer<float64> debouncedChanges() const;
- protected:
- int contentHeight() const override;
- private:
- void setExternalValue(float64 speed);
- void setSliderValue(float64 speed);
- const base::unique_qptr<Ui::MediaSlider> _slider;
- const not_null<QAction*> _dummyAction;
- const style::MediaSpeedMenu &_st;
- Ui::Text::String _text;
- int _height = 0;
- rpl::event_stream<float64> _changing;
- rpl::event_stream<float64> _changed;
- rpl::event_stream<float64> _debounced;
- base::Timer _debounceTimer;
- rpl::variable<float64> _last = 0.;
- };
- SpeedSliderItem::SpeedSliderItem(
- not_null<RpWidget*> parent,
- const style::MediaSpeedMenu &st,
- rpl::producer<float64> value)
- : Ui::Menu::ItemBase(parent, st.dropdown.menu)
- , _slider(base::make_unique_q<Ui::MediaSlider>(this, st.slider))
- , _dummyAction(new QAction(parent))
- , _st(st)
- , _height(st.sliderPadding.top()
- + st.dropdown.menu.itemStyle.font->height
- + st.sliderPadding.bottom())
- , _debounceTimer([=] { _debounced.fire(current()); }) {
- initResizeHook(parent->sizeValue());
- enableMouseSelecting();
- enableMouseSelecting(_slider.get());
- setPointerCursor(false);
- setMinWidth(st.sliderPadding.left()
- + st.sliderWidth
- + st.sliderPadding.right());
- _slider->setAlwaysDisplayMarker(true);
- sizeValue(
- ) | rpl::start_with_next([=](const QSize &size) {
- const auto geometry = QRect(QPoint(), size);
- const auto padding = _st.sliderPadding;
- const auto inner = geometry - padding;
- _slider->setGeometry(
- padding.left(),
- inner.y(),
- (geometry.width() - padding.left() - padding.right()),
- inner.height());
- }, lifetime());
- paintRequest(
- ) | rpl::start_with_next([=](const QRect &clip) {
- auto p = Painter(this);
- p.fillRect(clip, _st.dropdown.menu.itemBg);
- const auto left = (_st.sliderPadding.left() - _text.maxWidth()) / 2;
- const auto top = _st.dropdown.menu.itemPadding.top();
- p.setPen(_st.dropdown.menu.itemFg);
- _text.drawLeftElided(p, left, top, _text.maxWidth(), width());
- }, lifetime());
- _slider->setChangeProgressCallback([=](float64 value) {
- const auto speed = SliderValueToSpeed(value);
- if (!EqualSpeeds(current(), speed)) {
- _last = speed;
- _changing.fire_copy(speed);
- _debounceTimer.callOnce(kSpeedDebounceTimeout);
- }
- });
- _slider->setChangeFinishedCallback([=](float64 value) {
- const auto speed = SliderValueToSpeed(value);
- _last = speed;
- _changed.fire_copy(speed);
- _debounced.fire_copy(speed);
- _debounceTimer.cancel();
- });
- std::move(
- value
- ) | rpl::start_with_next([=](float64 external) {
- setExternalValue(external);
- }, lifetime());
- _last.value(
- ) | rpl::start_with_next([=](float64 value) {
- const auto text = QString::number(value, 'f', 1) + 'x';
- if (_text.toString() != text) {
- _text.setText(_st.sliderStyle, text);
- update();
- }
- }, lifetime());
- _slider->setAdjustCallback([=](float64 value) {
- const auto speed = SliderValueToSpeed(value);
- for (const auto &snap : kSpeedStickedValues) {
- if (speed > (snap.first - snap.second)
- && speed < (snap.first + snap.second)) {
- return SpeedToSliderValue(snap.first);
- }
- }
- return value;
- });
- }
- void FillSpeedMenu(
- not_null<Ui::Menu::Menu*> menu,
- const style::MediaSpeedMenu &st,
- rpl::producer<float64> value,
- Fn<void(float64)> callback,
- bool onlySlider) {
- auto slider = base::make_unique_q<SpeedSliderItem>(
- menu,
- st,
- rpl::duplicate(value));
- slider->debouncedChanges(
- ) | rpl::start_with_next(callback, slider->lifetime());
- struct State {
- rpl::variable<float64> realtime;
- };
- const auto state = slider->lifetime().make_state<State>();
- state->realtime = rpl::single(
- slider->current()
- ) | rpl::then(rpl::merge(
- slider->changing(),
- slider->changed()
- ));
- menu->addAction(std::move(slider));
- if (onlySlider) {
- return;
- }
- menu->addSeparator(&st.dropdown.menu.separator);
- struct SpeedPoint {
- float64 speed = 0.;
- tr::phrase<> text;
- const style::icon &icon;
- const style::icon &iconActive;
- };
- const auto points = std::vector<SpeedPoint>{
- {
- 0.5,
- tr::lng_voice_speed_slow,
- st.slow,
- st.slowActive },
- {
- 1.0,
- tr::lng_voice_speed_normal,
- st.normal,
- st.normalActive },
- {
- 1.2,
- tr::lng_voice_speed_medium,
- st.medium,
- st.mediumActive },
- {
- 1.5,
- tr::lng_voice_speed_fast,
- st.fast,
- st.fastActive },
- {
- 1.7,
- tr::lng_voice_speed_very_fast,
- st.veryFast,
- st.veryFastActive },
- {
- 2.0,
- tr::lng_voice_speed_super_fast,
- st.superFast,
- st.superFastActive },
- };
- for (const auto &point : points) {
- const auto speed = point.speed;
- const auto text = point.text(tr::now);
- const auto icon = &point.icon;
- const auto iconActive = &point.iconActive;
- auto action = base::make_unique_q<Ui::Menu::Action>(
- menu,
- st.dropdown.menu,
- Ui::Menu::CreateAction(menu, text, [=] { callback(speed); }),
- &point.icon,
- &point.icon);
- const auto raw = action.get();
- const auto check = Ui::CreateChild<Ui::RpWidget>(raw);
- check->resize(st.activeCheck.size());
- check->paintRequest(
- ) | rpl::start_with_next([check, icon = &st.activeCheck] {
- auto p = QPainter(check);
- icon->paint(p, 0, 0, check->width());
- }, check->lifetime());
- raw->sizeValue(
- ) | rpl::start_with_next([=, skip = st.activeCheckSkip](QSize size) {
- check->moveToRight(
- skip,
- (size.height() - check->height()) / 2,
- size.width());
- }, check->lifetime());
- check->setAttribute(Qt::WA_TransparentForMouseEvents);
- state->realtime.value(
- ) | rpl::start_with_next([=](float64 now) {
- const auto chosen = EqualSpeeds(speed, now);
- const auto overriden = chosen ? iconActive : icon;
- raw->setIcon(overriden, overriden);
- raw->action()->setEnabled(!chosen);
- check->setVisible(chosen);
- }, raw->lifetime());
- menu->addAction(std::move(action));
- }
- }
- void SpeedSliderItem::setExternalValue(float64 speed) {
- if (!_slider->isChanging()) {
- setSliderValue(speed);
- }
- }
- void SpeedSliderItem::setSliderValue(float64 speed) {
- const auto value = SpeedToSliderValue(speed);
- _slider->setValue(value);
- _last = speed;
- _changed.fire_copy(speed);
- }
- not_null<QAction*> SpeedSliderItem::action() const {
- return _dummyAction;
- }
- bool SpeedSliderItem::isEnabled() const {
- return false;
- }
- int SpeedSliderItem::contentHeight() const {
- return _height;
- }
- float64 SpeedSliderItem::current() const {
- return _last.current();
- }
- rpl::producer<float64> SpeedSliderItem::changing() const {
- return _changing.events();
- }
- rpl::producer<float64> SpeedSliderItem::changed() const {
- return _changed.events();
- }
- rpl::producer<float64> SpeedSliderItem::debouncedChanges() const {
- return _debounced.events();
- }
- } // namespace
- Dropdown::Dropdown(QWidget *parent)
- : RpWidget(parent)
- , _hideTimer([=] { startHide(); })
- , _showTimer([=] { startShow(); }) {
- hide();
- macWindowDeactivateEvents(
- ) | rpl::filter([=] {
- return !isHidden();
- }) | rpl::start_with_next([=] {
- leaveEvent(nullptr);
- }, lifetime());
- hide();
- auto margin = getMargin();
- resize(margin.left() + st::mediaPlayerVolumeSize.width() + margin.right(), margin.top() + st::mediaPlayerVolumeSize.height() + margin.bottom());
- }
- QMargins Dropdown::getMargin() const {
- const auto top1 = st::mediaPlayerHeight
- + st::lineWidth
- - st::mediaPlayerPlayTop
- - st::mediaPlayerVolumeToggle.height;
- const auto top2 = st::mediaPlayerPlayback.fullWidth;
- const auto top = std::max(top1, top2);
- return QMargins(st::mediaPlayerVolumeMargin, top, st::mediaPlayerVolumeMargin, st::mediaPlayerVolumeMargin);
- }
- bool Dropdown::overlaps(const QRect &globalRect) {
- if (isHidden() || _a_appearance.animating()) return false;
- return rect().marginsRemoved(getMargin()).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
- }
- void Dropdown::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- if (!_cache.isNull()) {
- bool animating = _a_appearance.animating();
- if (animating) {
- p.setOpacity(_a_appearance.value(_hiding ? 0. : 1.));
- } else if (_hiding || isHidden()) {
- hidingFinished();
- return;
- }
- p.drawPixmap(0, 0, _cache);
- if (!animating) {
- showChildren();
- _cache = QPixmap();
- }
- return;
- }
- // draw shadow
- auto shadowedRect = rect().marginsRemoved(getMargin());
- auto shadowedSides = RectPart::Left | RectPart::Right | RectPart::Bottom;
- Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides);
- const auto &corners = Ui::CachedCornerPixmaps(Ui::MenuCorners);
- const auto fill = Ui::CornersPixmaps{
- .p = { QPixmap(), QPixmap(), corners.p[2], corners.p[3] },
- };
- Ui::FillRoundRect(
- p,
- shadowedRect.x(),
- 0,
- shadowedRect.width(),
- shadowedRect.y() + shadowedRect.height(),
- st::menuBg,
- fill);
- }
- void Dropdown::enterEventHook(QEnterEvent *e) {
- _hideTimer.cancel();
- if (_a_appearance.animating()) {
- startShow();
- } else {
- _showTimer.callOnce(0);
- }
- return RpWidget::enterEventHook(e);
- }
- void Dropdown::leaveEventHook(QEvent *e) {
- _showTimer.cancel();
- if (_a_appearance.animating()) {
- startHide();
- } else {
- _hideTimer.callOnce(300);
- }
- return RpWidget::leaveEventHook(e);
- }
- void Dropdown::otherEnter() {
- _hideTimer.cancel();
- if (_a_appearance.animating()) {
- startShow();
- } else {
- _showTimer.callOnce(0);
- }
- }
- void Dropdown::otherLeave() {
- _showTimer.cancel();
- if (_a_appearance.animating()) {
- startHide();
- } else {
- _hideTimer.callOnce(0);
- }
- }
- void Dropdown::startShow() {
- if (isHidden()) {
- show();
- } else if (!_hiding) {
- return;
- }
- _hiding = false;
- startAnimation();
- }
- void Dropdown::startHide() {
- if (_hiding) {
- return;
- }
- _hiding = true;
- startAnimation();
- }
- void Dropdown::startAnimation() {
- if (_cache.isNull()) {
- showChildren();
- _cache = Ui::GrabWidget(this);
- }
- hideChildren();
- _a_appearance.start(
- [=] { appearanceCallback(); },
- _hiding ? 1. : 0.,
- _hiding ? 0. : 1.,
- st::defaultInnerDropdown.duration);
- }
- void Dropdown::appearanceCallback() {
- if (!_a_appearance.animating() && _hiding) {
- _hiding = false;
- hidingFinished();
- } else {
- update();
- }
- }
- void Dropdown::hidingFinished() {
- hide();
- _cache = QPixmap();
- }
- bool Dropdown::eventFilter(QObject *obj, QEvent *e) {
- if (e->type() == QEvent::Enter) {
- otherEnter();
- } else if (e->type() == QEvent::Leave) {
- otherLeave();
- }
- return false;
- }
- WithDropdownController::WithDropdownController(
- not_null<Ui::AbstractButton*> button,
- not_null<QWidget*> menuParent,
- const style::DropdownMenu &menuSt,
- Qt::Alignment menuAlign,
- Fn<void(bool)> menuOverCallback)
- : _button(button)
- , _menuParent(menuParent)
- , _menuSt(menuSt)
- , _menuAlign(menuAlign)
- , _menuOverCallback(std::move(menuOverCallback)) {
- button->events(
- ) | rpl::filter([=](not_null<QEvent*> e) {
- return (e->type() == QEvent::Enter)
- || (e->type() == QEvent::Leave);
- }) | rpl::start_with_next([=](not_null<QEvent*> e) {
- _overButton = (e->type() == QEvent::Enter);
- if (_overButton) {
- InvokeQueued(button, [=] {
- if (_overButton) {
- showMenu();
- }
- });
- }
- }, button->lifetime());
- }
- not_null<Ui::AbstractButton*> WithDropdownController::button() const {
- return _button;
- }
- Ui::DropdownMenu *WithDropdownController::menu() const {
- return _menu.get();
- }
- void WithDropdownController::updateDropdownGeometry() {
- if (!_menu) {
- return;
- }
- const auto bwidth = _button->width();
- const auto bheight = _button->height();
- const auto mwidth = _menu->width();
- const auto mheight = _menu->height();
- const auto padding = _menuSt.wrap.padding;
- const auto x = st::mediaPlayerMenuPosition.x();
- const auto y = st::mediaPlayerMenuPosition.y();
- const auto position = _menu->parentWidget()->mapFromGlobal(
- _button->mapToGlobal(QPoint())
- ) + [&] {
- switch (_menuAlign) {
- case style::al_topleft: return QPoint(
- -padding.left() - x,
- bheight - padding.top() + y);
- case style::al_topright: return QPoint(
- bwidth - mwidth + padding.right() + x,
- bheight - padding.top() + y);
- case style::al_bottomright: return QPoint(
- bwidth - mwidth + padding.right() + x,
- -mheight + padding.bottom() - y);
- case style::al_bottomleft: return QPoint(
- -padding.left() - x,
- -mheight + padding.bottom() - y);
- }
- Unexpected("Menu align value.");
- }();
- _menu->move(position);
- }
- rpl::producer<bool> WithDropdownController::menuToggledValue() const {
- return _menuToggled.value();
- }
- void WithDropdownController::hideTemporarily() {
- if (_menu && !_menu->isHidden()) {
- _temporarilyHidden = true;
- _menu->hide();
- }
- }
- void WithDropdownController::showBack() {
- if (_temporarilyHidden) {
- _temporarilyHidden = false;
- if (_menu && _menu->isHidden()) {
- _menu->show();
- }
- }
- }
- void WithDropdownController::showMenu() {
- if (_menu) {
- return;
- }
- _menu.emplace(_menuParent, _menuSt);
- const auto raw = _menu.get();
- _menu->events(
- ) | rpl::start_with_next([this](not_null<QEvent*> e) {
- const auto type = e->type();
- if (type == QEvent::Enter) {
- _menuOverCallback(true);
- } else if (type == QEvent::Leave) {
- _menuOverCallback(false);
- }
- }, _menu->lifetime());
- _menu->setHiddenCallback([=]{
- if (_menu.get() == raw) {
- _menuToggled = false;
- }
- Ui::PostponeCall(raw, [this] {
- _menu = nullptr;
- _menuToggled = false;
- });
- });
- _menu->setShowStartCallback([=] {
- _menuToggled = true;
- });
- _menu->setHideStartCallback([=] {
- _menuToggled = false;
- });
- _button->installEventFilter(raw);
- fillMenu(raw);
- updateDropdownGeometry();
- const auto origin = [&] {
- using Origin = Ui::PanelAnimation::Origin;
- switch (_menuAlign) {
- case style::al_topleft: return Origin::TopLeft;
- case style::al_topright: return Origin::TopRight;
- case style::al_bottomright: return Origin::BottomRight;
- case style::al_bottomleft: return Origin::BottomLeft;
- }
- Unexpected("Menu align value.");
- }();
- _menu->showAnimated(origin);
- _menuToggled = true;
- }
- OrderController::OrderController(
- not_null<Ui::IconButton*> button,
- not_null<QWidget*> menuParent,
- Fn<void(bool)> menuOverCallback,
- rpl::producer<OrderMode> value,
- Fn<void(OrderMode)> change)
- : WithDropdownController(
- button,
- menuParent,
- st::mediaPlayerMenu,
- style::al_topright,
- std::move(menuOverCallback))
- , _button(button)
- , _appOrder(std::move(value))
- , _change(std::move(change)) {
- button->setClickedCallback([=] {
- showMenu();
- });
- _appOrder.value(
- ) | rpl::start_with_next([=] {
- updateIcon();
- }, button->lifetime());
- }
- void OrderController::fillMenu(not_null<Ui::DropdownMenu*> menu) {
- const auto addOrderAction = [&](OrderMode mode) {
- struct Fields {
- QString label;
- const style::icon &icon;
- const style::icon &activeIcon;
- };
- const auto active = (_appOrder.current() == mode);
- const auto callback = [change = _change, mode, active] {
- change(active ? OrderMode::Default : mode);
- };
- const auto fields = [&]() -> Fields {
- switch (mode) {
- case OrderMode::Reverse: return {
- .label = tr::lng_audio_player_reverse(tr::now),
- .icon = st::mediaPlayerOrderIconReverse,
- .activeIcon = st::mediaPlayerOrderIconReverseActive,
- };
- case OrderMode::Shuffle: return {
- .label = tr::lng_audio_player_shuffle(tr::now),
- .icon = st::mediaPlayerOrderIconShuffle,
- .activeIcon = st::mediaPlayerOrderIconShuffleActive,
- };
- }
- Unexpected("Order mode in addOrderAction.");
- }();
- menu->addAction(base::make_unique_q<Ui::Menu::Action>(
- menu,
- (active
- ? st::mediaPlayerOrderMenuActive
- : st::mediaPlayerOrderMenu),
- Ui::Menu::CreateAction(menu, fields.label, callback),
- &(active ? fields.activeIcon : fields.icon),
- &(active ? fields.activeIcon : fields.icon)));
- };
- addOrderAction(OrderMode::Reverse);
- addOrderAction(OrderMode::Shuffle);
- }
- void OrderController::updateIcon() {
- switch (_appOrder.current()) {
- case OrderMode::Default:
- _button->setIconOverride(
- &st::mediaPlayerReverseDisabledIcon,
- &st::mediaPlayerReverseDisabledIconOver);
- _button->setRippleColorOverride(
- &st::mediaPlayerRepeatDisabledRippleBg);
- break;
- case OrderMode::Reverse:
- _button->setIconOverride(&st::mediaPlayerReverseIcon);
- _button->setRippleColorOverride(nullptr);
- break;
- case OrderMode::Shuffle:
- _button->setIconOverride(&st::mediaPlayerShuffleIcon);
- _button->setRippleColorOverride(nullptr);
- break;
- }
- }
- SpeedController::SpeedController(
- not_null<Ui::AbstractButton*> button,
- const style::MediaSpeedButton &st,
- not_null<QWidget*> menuParent,
- Fn<void(bool)> menuOverCallback,
- Fn<float64(bool lastNonDefault)> value,
- Fn<void(float64)> change,
- std::vector<int> qualities,
- Fn<VideoQuality()> quality,
- Fn<void(int)> changeQuality)
- : WithDropdownController(
- button,
- menuParent,
- st.menu.dropdown,
- st.menuAlign,
- std::move(menuOverCallback))
- , _st(st)
- , _lookup(std::move(value))
- , _change(std::move(change))
- , _qualities(std::move(qualities))
- , _lookupQuality(std::move(quality))
- , _changeQuality(std::move(changeQuality)) {
- Expects(_qualities.empty() || (_lookupQuality && _changeQuality));
- button->setClickedCallback([=] {
- if (_lookup && !_lookupQuality && !_changeQuality) {
- toggleDefault();
- save();
- if (const auto current = menu()) {
- current->otherEnter();
- }
- } else {
- showMenu();
- }
- });
- if (const auto lookup = _lookup) {
- setSpeed(lookup(false));
- _speed = lookup(true);
- }
- }
- rpl::producer<> SpeedController::saved() const {
- return _saved.events();
- }
- rpl::producer<float64> SpeedController::realtimeValue() const {
- return _speedChanged.events_starting_with(speed());
- }
- float64 SpeedController::speed() const {
- return _isDefault ? 1. : _speed;
- }
- bool SpeedController::isDefault() const {
- return _isDefault;
- }
- float64 SpeedController::lastNonDefaultSpeed() const {
- return _speed;
- }
- void SpeedController::toggleDefault() {
- _isDefault = !_isDefault;
- _speedChanged.fire(speed());
- }
- void SpeedController::setSpeed(float64 newSpeed) {
- if (!(_isDefault = EqualSpeeds(newSpeed, 1.))) {
- _speed = newSpeed;
- }
- _speedChanged.fire(speed());
- }
- void SpeedController::save() {
- if (const auto change = _change) {
- change(speed());
- }
- _saved.fire({});
- }
- void SpeedController::setQuality(VideoQuality quality) {
- _quality = quality;
- _changeQuality(quality.manual ? quality.height : 0);
- }
- void SpeedController::fillMenu(not_null<Ui::DropdownMenu*> menu) {
- if (_lookup) {
- FillSpeedMenu(
- menu->menu(),
- _st.menu,
- _speedChanged.events_starting_with(speed()),
- [=](float64 speed) { setSpeed(speed); save(); },
- !_qualities.empty());
- }
- if (_qualities.empty()) {
- return;
- }
- _quality = _lookupQuality();
- const auto raw = menu->menu();
- const auto &st = _st.menu;
- if (_lookup) {
- raw->addSeparator(&st.dropdown.menu.separator);
- }
- const auto add = [&](int quality) {
- const auto automatic = tr::lng_mediaview_quality_auto(tr::now);
- const auto text = quality ? u"%1p"_q.arg(quality) : automatic;
- auto action = base::make_unique_q<Ui::Menu::Action>(
- raw,
- st.qualityMenu,
- Ui::Menu::CreateAction(
- raw,
- text,
- [=] { _changeQuality(quality); }),
- nullptr,
- nullptr);
- const auto raw = action.get();
- const auto check = Ui::CreateChild<Ui::RpWidget>(raw);
- check->resize(st.activeCheck.size());
- check->paintRequest(
- ) | rpl::start_with_next([check, icon = &st.activeCheck] {
- auto p = QPainter(check);
- icon->paint(p, 0, 0, check->width());
- }, check->lifetime());
- raw->sizeValue(
- ) | rpl::start_with_next([=, skip = st.activeCheckSkip](QSize size) {
- check->moveToRight(
- skip,
- (size.height() - check->height()) / 2,
- size.width());
- }, check->lifetime());
- check->setAttribute(Qt::WA_TransparentForMouseEvents);
- _quality.value(
- ) | rpl::start_with_next([=](VideoQuality now) {
- const auto chosen = now.manual
- ? (now.height == quality)
- : !quality;
- raw->action()->setEnabled(!chosen);
- if (!quality) {
- raw->action()->setText(automatic
- + (now.manual ? QString() : u"\t%1p"_q.arg(now.height)));
- }
- check->setVisible(chosen);
- }, raw->lifetime());
- menu->addAction(std::move(action));
- };
- add(0);
- for (const auto quality : _qualities) {
- add(quality);
- }
- }
- } // namespace Media::Player
|