Replaced submenu for playback speed with slider.
This commit is contained in:
parent
2d50c61703
commit
813470ff25
|
@ -1721,7 +1721,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_mediaview_downloads" = "Downloads";
|
"lng_mediaview_downloads" = "Downloads";
|
||||||
"lng_mediaview_video_loading" = "Loading - {percent}";
|
"lng_mediaview_video_loading" = "Loading - {percent}";
|
||||||
"lng_mediaview_playback_speed" = "Playback speed";
|
"lng_mediaview_playback_speed" = "Playback speed";
|
||||||
"lng_mediaview_playback_speed_normal" = "Normal";
|
|
||||||
"lng_mediaview_rotate_video" = "Rotate video";
|
"lng_mediaview_rotate_video" = "Rotate video";
|
||||||
|
|
||||||
"lng_theme_preview_title" = "Theme Preview";
|
"lng_theme_preview_title" = "Theme Preview";
|
||||||
|
|
|
@ -507,10 +507,15 @@ public:
|
||||||
[[nodiscard]] static bool ThirdColumnByDefault();
|
[[nodiscard]] static bool ThirdColumnByDefault();
|
||||||
[[nodiscard]] float64 DefaultDialogsWidthRatio();
|
[[nodiscard]] float64 DefaultDialogsWidthRatio();
|
||||||
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
|
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
|
||||||
return int(std::round(std::clamp(speed * 4., 2., 8.))) - 2;
|
return int(std::round(std::clamp(speed, 0.5, 2.0) * 100));
|
||||||
}
|
}
|
||||||
[[nodiscard]] static float64 DeserializePlaybackSpeed(qint32 speed) {
|
[[nodiscard]] static float64 DeserializePlaybackSpeed(qint32 speed) {
|
||||||
return (std::clamp(speed, 0, 6) + 2) / 4.;
|
if (speed < 10) {
|
||||||
|
// The old values in settings.
|
||||||
|
return (std::clamp(speed, 0, 6) + 2) / 4.;
|
||||||
|
} else {
|
||||||
|
return std::clamp(speed, 50, 200) / 100.;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetOnLastLogout();
|
void resetOnLastLogout();
|
||||||
|
|
|
@ -301,3 +301,5 @@ pipCloseIcon: icon {{ "player_pip_close", mediaviewPlaybackIconFg }};
|
||||||
pipCloseIconOver: icon {{ "player_pip_close", mediaviewPlaybackIconFgOver }};
|
pipCloseIconOver: icon {{ "player_pip_close", mediaviewPlaybackIconFgOver }};
|
||||||
pipEnlargeIcon: icon {{ "player_pip_enlarge", mediaviewPlaybackIconFg }};
|
pipEnlargeIcon: icon {{ "player_pip_enlarge", mediaviewPlaybackIconFg }};
|
||||||
pipEnlargeIconOver: icon {{ "player_pip_enlarge", mediaviewPlaybackIconFgOver }};
|
pipEnlargeIconOver: icon {{ "player_pip_enlarge", mediaviewPlaybackIconFgOver }};
|
||||||
|
|
||||||
|
speedSliderDividerSize: size(2px, 8px);
|
||||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/continuous_sliders.h"
|
#include "ui/widgets/continuous_sliders.h"
|
||||||
#include "ui/effects/fade_animation.h"
|
#include "ui/effects/fade_animation.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/menu/menu_item_base.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
|
@ -21,6 +22,166 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Media {
|
namespace Media {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kMinSpeed = 50;
|
||||||
|
constexpr auto kMaxSpeed = 200;
|
||||||
|
|
||||||
|
constexpr float64 SpeedShiftToValue(float64 value) {
|
||||||
|
const auto valueAsSpeedF = value * 100.;
|
||||||
|
const auto valueAsSpeed = int(valueAsSpeedF + 0.5); // constexpr round.
|
||||||
|
return float64(valueAsSpeed) / (kMaxSpeed - kMinSpeed);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr float64 SpeedToValue(float64 value) {
|
||||||
|
const auto valueAsSpeedF = value * 100.;
|
||||||
|
const auto valueAsSpeed = int(valueAsSpeedF + 0.5); // constexpr round.
|
||||||
|
return float64(valueAsSpeed - kMinSpeed) / (kMaxSpeed - kMinSpeed);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto kSpeedStickedValues =
|
||||||
|
std::array<std::pair<float64, float64>, 5>{{
|
||||||
|
{ SpeedToValue(0.75), SpeedShiftToValue(0.03) },
|
||||||
|
{ SpeedToValue(1.00), SpeedShiftToValue(0.05) },
|
||||||
|
{ SpeedToValue(1.25), SpeedShiftToValue(0.03) },
|
||||||
|
{ SpeedToValue(1.50), SpeedShiftToValue(0.03) },
|
||||||
|
{ SpeedToValue(1.75), SpeedShiftToValue(0.03) },
|
||||||
|
}};
|
||||||
|
|
||||||
|
class MenuSpeedItem final : public Ui::Menu::ItemBase {
|
||||||
|
public:
|
||||||
|
MenuSpeedItem(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
float64 startSpeed);
|
||||||
|
|
||||||
|
not_null<QAction*> action() const override;
|
||||||
|
bool isEnabled() const override;
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<float64> changeSpeedRequests() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int contentHeight() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QRect _itemRect;
|
||||||
|
QRect _textRect;
|
||||||
|
|
||||||
|
const style::MediaSlider &_sliderSt;
|
||||||
|
const base::unique_qptr<Ui::MediaSlider> _slider;
|
||||||
|
const not_null<QAction*> _dummyAction;
|
||||||
|
const int _height;
|
||||||
|
|
||||||
|
rpl::event_stream<float64> _changeSpeedRequests;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
MenuSpeedItem::MenuSpeedItem(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
float64 startSpeed)
|
||||||
|
: Ui::Menu::ItemBase(parent, st)
|
||||||
|
, _sliderSt(st::mediaviewPlayback)
|
||||||
|
, _slider(base::make_unique_q<Ui::MediaSlider>(
|
||||||
|
this,
|
||||||
|
_sliderSt))
|
||||||
|
, _dummyAction(new QAction(parent))
|
||||||
|
, _height(st.itemPadding.top() * 2
|
||||||
|
+ st.itemStyle.font->height
|
||||||
|
+ _sliderSt.seekSize.height()
|
||||||
|
+ st.itemPadding.bottom() * 2) {
|
||||||
|
|
||||||
|
initResizeHook(parent->sizeValue());
|
||||||
|
enableMouseSelecting();
|
||||||
|
enableMouseSelecting(_slider.get());
|
||||||
|
|
||||||
|
const auto computeSpeed = [=](float64 value) {
|
||||||
|
return anim::interpolate(kMinSpeed, kMaxSpeed, value) / 100.;
|
||||||
|
};
|
||||||
|
const auto speedString = [=](float64 value) {
|
||||||
|
return u"%1: %2x"_q
|
||||||
|
.arg(tr::lng_mediaview_playback_speed(tr::now))
|
||||||
|
.arg(computeSpeed(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
_slider->setAlwaysDisplayMarker(true);
|
||||||
|
_slider->setValue((std::round(startSpeed * 100.) - kMinSpeed)
|
||||||
|
/ (kMaxSpeed - kMinSpeed));
|
||||||
|
|
||||||
|
_slider->addDivider(
|
||||||
|
kSpeedStickedValues[1].first,
|
||||||
|
st::speedSliderDividerSize);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto goodWidth = st.itemPadding.left()
|
||||||
|
+ st.itemPadding.right()
|
||||||
|
+ st.itemStyle.font->width(speedString(0.9));
|
||||||
|
setMinWidth(std::clamp(goodWidth, st.widthMin, st.widthMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeValue(
|
||||||
|
) | rpl::start_with_next([=](const QSize &size) {
|
||||||
|
const auto geometry = QRect(QPoint(), size);
|
||||||
|
_itemRect = geometry - st.itemPadding;
|
||||||
|
|
||||||
|
const auto height = _itemRect.height();
|
||||||
|
_textRect = _itemRect
|
||||||
|
- style::margins(0, 0, 0, height - st.itemStyle.font->height);
|
||||||
|
|
||||||
|
const auto sliderGeometry = _itemRect
|
||||||
|
- style::margins(0, height - _sliderSt.seekSize.height(), 0, 0);
|
||||||
|
_slider->setGeometry(sliderGeometry);
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
paintRequest(
|
||||||
|
) | rpl::start_with_next([=](const QRect &clip) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
const auto selected = isSelected();
|
||||||
|
p.fillRect(clip, selected ? st.itemBgOver : st.itemBg);
|
||||||
|
|
||||||
|
const auto value = _slider->value();
|
||||||
|
|
||||||
|
p.setFont(st.itemStyle.font);
|
||||||
|
p.drawText(_textRect, speedString(value), style::al_left);
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
_slider->setChangeProgressCallback([=](float64 value) {
|
||||||
|
update(_textRect);
|
||||||
|
});
|
||||||
|
|
||||||
|
_slider->setChangeFinishedCallback([=](float64 value) {
|
||||||
|
_changeSpeedRequests.fire_copy(computeSpeed(value));
|
||||||
|
});
|
||||||
|
|
||||||
|
_slider->setAdjustCallback([=](float64 value) {
|
||||||
|
for (const auto &snap : kSpeedStickedValues) {
|
||||||
|
if (value > (snap.first - snap.second)
|
||||||
|
&& value < (snap.first + snap.second)) {
|
||||||
|
return snap.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<QAction*> MenuSpeedItem::action() const {
|
||||||
|
return _dummyAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuSpeedItem::isEnabled() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MenuSpeedItem::contentHeight() const {
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<float64> MenuSpeedItem::changeSpeedRequests() const {
|
||||||
|
return _changeSpeedRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
PlaybackControls::PlaybackControls(
|
PlaybackControls::PlaybackControls(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
@ -37,7 +198,7 @@ PlaybackControls::PlaybackControls(
|
||||||
, _pictureInPicture(this, st::mediaviewPipButton)
|
, _pictureInPicture(this, st::mediaviewPipButton)
|
||||||
, _playedAlready(this, st::mediaviewPlayProgressLabel)
|
, _playedAlready(this, st::mediaviewPlayProgressLabel)
|
||||||
, _toPlayLeft(this, st::mediaviewPlayProgressLabel)
|
, _toPlayLeft(this, st::mediaviewPlayProgressLabel)
|
||||||
, _speedMenuStyle(st::mediaviewControlsPopupMenu)
|
, _menuStyle(st::mediaviewControlsPopupMenu)
|
||||||
, _fadeAnimation(std::make_unique<Ui::FadeAnimation>(this)) {
|
, _fadeAnimation(std::make_unique<Ui::FadeAnimation>(this)) {
|
||||||
_fadeAnimation->show();
|
_fadeAnimation->show();
|
||||||
_fadeAnimation->setFinishedCallback([=] {
|
_fadeAnimation->setFinishedCallback([=] {
|
||||||
|
@ -175,61 +336,32 @@ void PlaybackControls::fadeUpdated(float64 opacity) {
|
||||||
_volumeController->setFadeOpacity(opacity);
|
_volumeController->setFadeOpacity(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaybackControls::validateSpeedMenuStyle() {
|
|
||||||
auto &st = _speedMenuStyle.menu;
|
|
||||||
const auto &check = st::mediaviewMenuCheck;
|
|
||||||
const auto normal = tr::lng_mediaview_playback_speed_normal(tr::now);
|
|
||||||
const auto itemHeight = st.itemPadding.top()
|
|
||||||
+ st.itemStyle.font->height
|
|
||||||
+ st.itemPadding.bottom();
|
|
||||||
const auto itemWidth = st.itemPadding.left()
|
|
||||||
+ st.itemStyle.font->width(normal)
|
|
||||||
+ st.itemPadding.right();
|
|
||||||
if (itemWidth + st.itemPadding.right() + check.width() > st.widthMin) {
|
|
||||||
st.widthMin = itemWidth + st.itemPadding.right() + check.width();
|
|
||||||
}
|
|
||||||
const auto realWidth = std::clamp(itemWidth, st.widthMin, st.widthMax);
|
|
||||||
st.itemIconPosition = QPoint(
|
|
||||||
realWidth - st.itemPadding.right() - check.width(),
|
|
||||||
(itemHeight - check.height()) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaybackControls::showMenu() {
|
void PlaybackControls::showMenu() {
|
||||||
if (_menu) {
|
if (_menu) {
|
||||||
_menu = nullptr;
|
_menu = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
validateSpeedMenuStyle();
|
_menu.emplace(this, _menuStyle);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto speedItem = base::make_unique_q<MenuSpeedItem>(
|
||||||
|
_menu,
|
||||||
|
_menuStyle.menu,
|
||||||
|
_delegate->playbackControlsCurrentSpeed());
|
||||||
|
speedItem->changeSpeedRequests(
|
||||||
|
) | rpl::start_with_next([=](float64 speed) {
|
||||||
|
updatePlaybackSpeed(speed);
|
||||||
|
}, speedItem->lifetime());
|
||||||
|
_menu->addAction(std::move(speedItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
_menu->addSeparator();
|
||||||
|
|
||||||
auto submenu = std::make_unique<Ui::PopupMenu>(
|
|
||||||
this,
|
|
||||||
_speedMenuStyle);
|
|
||||||
const auto addSpeed = [&](float64 speed, QString text = QString()) {
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
text = QString::number(speed);
|
|
||||||
}
|
|
||||||
const auto checked = (speed == _delegate->playbackControlsCurrentSpeed());
|
|
||||||
const auto action = submenu->addAction(
|
|
||||||
text,
|
|
||||||
[=] { updatePlaybackSpeed(speed); },
|
|
||||||
checked ? &st::mediaviewMenuCheck : nullptr);
|
|
||||||
};
|
|
||||||
addSpeed(0.5);
|
|
||||||
addSpeed(0.75);
|
|
||||||
addSpeed(1., tr::lng_mediaview_playback_speed_normal(tr::now));
|
|
||||||
addSpeed(1.25);
|
|
||||||
addSpeed(1.5);
|
|
||||||
addSpeed(1.75);
|
|
||||||
addSpeed(2.);
|
|
||||||
_menu.emplace(this, st::mediaviewControlsPopupMenu);
|
|
||||||
_menu->addAction(tr::lng_mediaview_rotate_video(tr::now), [=] {
|
_menu->addAction(tr::lng_mediaview_rotate_video(tr::now), [=] {
|
||||||
_delegate->playbackControlsRotate();
|
_delegate->playbackControlsRotate();
|
||||||
});
|
});
|
||||||
_menu->addSeparator();
|
|
||||||
_menu->addAction(
|
|
||||||
tr::lng_mediaview_playback_speed(tr::now),
|
|
||||||
std::move(submenu));
|
|
||||||
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::BottomLeft);
|
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::BottomLeft);
|
||||||
_menu->popup(mapToGlobal(_menuToggle->geometry().topLeft()));
|
_menu->popup(mapToGlobal(_menuToggle->geometry().topLeft()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ private:
|
||||||
void updatePlayPauseResumeState(const Player::TrackState &state);
|
void updatePlayPauseResumeState(const Player::TrackState &state);
|
||||||
void updateTimeTexts(const Player::TrackState &state);
|
void updateTimeTexts(const Player::TrackState &state);
|
||||||
void refreshTimeTexts();
|
void refreshTimeTexts();
|
||||||
void validateSpeedMenuStyle();
|
|
||||||
void showMenu();
|
void showMenu();
|
||||||
|
|
||||||
not_null<Delegate*> _delegate;
|
not_null<Delegate*> _delegate;
|
||||||
|
@ -112,7 +111,7 @@ private:
|
||||||
object_ptr<Ui::LabelSimple> _toPlayLeft;
|
object_ptr<Ui::LabelSimple> _toPlayLeft;
|
||||||
object_ptr<Ui::LabelSimple> _downloadProgress = { nullptr };
|
object_ptr<Ui::LabelSimple> _downloadProgress = { nullptr };
|
||||||
|
|
||||||
style::PopupMenu _speedMenuStyle;
|
const style::PopupMenu &_menuStyle;
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
std::unique_ptr<Ui::FadeAnimation> _fadeAnimation;
|
std::unique_ptr<Ui::FadeAnimation> _fadeAnimation;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue