| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- /*
- 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 "calls/group/calls_volume_item.h"
- #include "calls/group/calls_group_common.h"
- #include "ui/effects/animation_value.h"
- #include "ui/effects/cross_line.h"
- #include "ui/widgets/continuous_sliders.h"
- #include "ui/rect.h"
- #include "styles/style_calls.h"
- #include "ui/paint/arcs.h"
- namespace Calls {
- namespace {
- constexpr auto kMaxVolumePercent = 200;
- const auto kSpeakerThreshold = std::vector<float>{
- 10.0f / kMaxVolumePercent,
- 50.0f / kMaxVolumePercent,
- 150.0f / kMaxVolumePercent };
- constexpr auto kVolumeStickedValues
- = std::array<std::pair<float64, float64>, 7>{{
- { 25. / kMaxVolumePercent, 2. / kMaxVolumePercent },
- { 50. / kMaxVolumePercent, 2. / kMaxVolumePercent },
- { 75. / kMaxVolumePercent, 2. / kMaxVolumePercent },
- { 100. / kMaxVolumePercent, 10. / kMaxVolumePercent },
- { 125. / kMaxVolumePercent, 2. / kMaxVolumePercent },
- { 150. / kMaxVolumePercent, 2. / kMaxVolumePercent },
- { 175. / kMaxVolumePercent, 2. / kMaxVolumePercent },
- }};
- } // namespace
- MenuVolumeItem::MenuVolumeItem(
- not_null<RpWidget*> parent,
- const style::Menu &st,
- const style::MediaSlider &stSlider,
- rpl::producer<Group::ParticipantState> participantState,
- int startVolume,
- int maxVolume,
- bool muted,
- const QMargins &padding)
- : Ui::Menu::ItemBase(parent, st)
- , _maxVolume(maxVolume)
- , _cloudMuted(muted)
- , _localMuted(muted)
- , _slider(base::make_unique_q<Ui::MediaSlider>(this, stSlider))
- , _dummyAction(new QAction(parent))
- , _st(st)
- , _stCross(st::groupCallMuteCrossLine)
- , _padding(padding)
- , _crossLineMute(std::make_unique<Ui::CrossLineAnimation>(_stCross, true))
- , _arcs(std::make_unique<Ui::Paint::ArcsAnimation>(
- st::groupCallSpeakerArcsAnimation,
- kSpeakerThreshold,
- _localMuted ? 0. : (startVolume / float(maxVolume)),
- Ui::Paint::ArcsAnimation::Direction::Right)) {
- initResizeHook(parent->sizeValue());
- enableMouseSelecting();
- enableMouseSelecting(_slider.get());
- _slider->setAlwaysDisplayMarker(true);
- sizeValue(
- ) | rpl::start_with_next([=](const QSize &size) {
- const auto geometry = QRect(QPoint(), size);
- _itemRect = geometry - _padding;
- _speakerRect = QRect(_itemRect.topLeft(), _stCross.icon.size());
- _arcPosition = _speakerRect.center()
- + QPoint(0, st::groupCallMenuSpeakerArcsSkip);
- _slider->setGeometry(
- st::groupCallMenuVolumeMargin.left(),
- _speakerRect.y(),
- (geometry.width()
- - st::groupCallMenuVolumeMargin.left()
- - st::groupCallMenuVolumeMargin.right()),
- _speakerRect.height());
- }, lifetime());
- setCloudVolume(startVolume);
- paintRequest(
- ) | rpl::start_with_next([=](const QRect &clip) {
- auto p = QPainter(this);
- const auto volume = _localMuted
- ? 0
- : base::SafeRound(_slider->value() * kMaxVolumePercent);
- const auto muteProgress
- = _crossLineAnimation.value((!volume) ? 1. : 0.);
- const auto selected = isSelected();
- p.fillRect(clip, selected ? st.itemBgOver : st.itemBg);
- const auto mutePen = anim::color(
- unmuteColor(),
- muteColor(),
- muteProgress);
- _crossLineMute->paint(
- p,
- _speakerRect.topLeft(),
- muteProgress,
- (muteProgress > 0) ? std::make_optional(mutePen) : std::nullopt);
- {
- p.translate(_arcPosition);
- _arcs->paint(p);
- }
- }, lifetime());
- _slider->setChangeProgressCallback([=](float64 value) {
- const auto newMuted = (value == 0);
- if (_localMuted != newMuted) {
- _localMuted = newMuted;
- _toggleMuteLocallyRequests.fire_copy(newMuted);
- _crossLineAnimation.start(
- [=] { update(_speakerRect); },
- _localMuted ? 0. : 1.,
- _localMuted ? 1. : 0.,
- st::callPanelDuration);
- }
- if (value > 0) {
- _changeVolumeLocallyRequests.fire(value * _maxVolume);
- }
- _arcs->setValue(value);
- updateSliderColor(value);
- });
- const auto returnVolume = [=] {
- _changeVolumeLocallyRequests.fire_copy(_cloudVolume);
- };
- _slider->setChangeFinishedCallback([=](float64 value) {
- const auto newVolume = base::SafeRound(value * _maxVolume);
- const auto muted = (value == 0);
- if (!_cloudMuted && muted) {
- returnVolume();
- _localMuted = true;
- _toggleMuteRequests.fire(true);
- }
- if (_cloudMuted && muted) {
- returnVolume();
- }
- if (_cloudMuted && !muted) {
- _waitingForUpdateVolume = true;
- _localMuted = false;
- _toggleMuteRequests.fire(false);
- }
- if (!_cloudMuted && !muted) {
- _changeVolumeRequests.fire_copy(newVolume);
- }
- updateSliderColor(value);
- });
- std::move(
- participantState
- ) | rpl::start_with_next([=](const Group::ParticipantState &state) {
- const auto newMuted = state.mutedByMe;
- const auto newVolume = state.volume.value_or(0);
- _cloudMuted = _localMuted = newMuted;
- if (!newVolume) {
- return;
- }
- if (_waitingForUpdateVolume) {
- const auto localVolume
- = base::SafeRound(_slider->value() * _maxVolume);
- if ((localVolume != newVolume)
- && (_cloudVolume == newVolume)) {
- _changeVolumeRequests.fire(int(localVolume));
- }
- } else {
- setCloudVolume(newVolume);
- }
- _waitingForUpdateVolume = false;
- }, lifetime());
- _slider->setAdjustCallback([=](float64 value) {
- for (const auto &snap : kVolumeStickedValues) {
- if (value > (snap.first - snap.second)
- && value < (snap.first + snap.second)) {
- return snap.first;
- }
- }
- return value;
- });
- initArcsAnimation();
- }
- void MenuVolumeItem::initArcsAnimation() {
- const auto lastTime = lifetime().make_state<int>(0);
- _arcsAnimation.init([=](crl::time now) {
- _arcs->update(now);
- update(_speakerRect);
- });
- _arcs->startUpdateRequests(
- ) | rpl::start_with_next([=] {
- if (!_arcsAnimation.animating()) {
- *lastTime = crl::now();
- _arcsAnimation.start();
- }
- }, lifetime());
- _arcs->stopUpdateRequests(
- ) | rpl::start_with_next([=] {
- _arcsAnimation.stop();
- }, lifetime());
- }
- QColor MenuVolumeItem::unmuteColor() const {
- return (isSelected()
- ? _st.itemFgOver
- : isEnabled()
- ? _st.itemFg
- : _st.itemFgDisabled)->c;
- }
- QColor MenuVolumeItem::muteColor() const {
- return (isSelected()
- ? st::attentionButtonFgOver
- : st::attentionButtonFg)->c;
- }
- void MenuVolumeItem::setCloudVolume(int volume) {
- if (_cloudVolume == volume) {
- return;
- }
- _cloudVolume = volume;
- if (!_slider->isChanging()) {
- setSliderVolume(_cloudMuted ? 0. : volume);
- }
- }
- void MenuVolumeItem::setSliderVolume(int volume) {
- const auto value = float64(volume) / _maxVolume;
- _slider->setValue(value);
- updateSliderColor(value);
- }
- void MenuVolumeItem::updateSliderColor(float64 value) {
- value = std::clamp(value, 0., 1.);
- const auto color = [](int rgb) {
- return QColor(
- int((rgb & 0xFF0000) >> 16),
- int((rgb & 0x00FF00) >> 8),
- int(rgb & 0x0000FF));
- };
- const auto colors = std::array<QColor, 4>{ {
- color(0xF66464),
- color(0xD0B738),
- color(0x24CD80),
- color(0x3BBCEC),
- } };
- _slider->setActiveFgOverride((value < 0.25)
- ? anim::color(colors[0], colors[1], value / 0.25)
- : (value < 0.5)
- ? anim::color(colors[1], colors[2], (value - 0.25) / 0.25)
- : anim::color(colors[2], colors[3], (value - 0.5) / 0.5));
- }
- not_null<QAction*> MenuVolumeItem::action() const {
- return _dummyAction;
- }
- bool MenuVolumeItem::isEnabled() const {
- return true;
- }
- int MenuVolumeItem::contentHeight() const {
- return rect::m::sum::v(_padding) + _stCross.icon.height();
- }
- rpl::producer<bool> MenuVolumeItem::toggleMuteRequests() const {
- return _toggleMuteRequests.events();
- }
- rpl::producer<bool> MenuVolumeItem::toggleMuteLocallyRequests() const {
- return _toggleMuteLocallyRequests.events();
- }
- rpl::producer<int> MenuVolumeItem::changeVolumeRequests() const {
- return _changeVolumeRequests.events();
- }
- rpl::producer<int> MenuVolumeItem::changeVolumeLocallyRequests() const {
- return _changeVolumeLocallyRequests.events();
- }
- } // namespace Calls
|