Display signal bars in calls.

This commit is contained in:
John Preston 2018-05-27 11:24:47 +03:00
parent 1e4cf4c466
commit 46976c4e03
7 changed files with 223 additions and 12 deletions

View File

@ -10,6 +10,14 @@ using "basic.style";
using "ui/widgets/widgets.style";
using "window/window.style";
CallSignalBars {
width: pixels;
radius: pixels;
skip: pixels;
color: color;
inactiveOpacity: double;
}
callWidth: 300px;
callHeight: 470px;
callShadow: Shadow {
@ -189,3 +197,16 @@ callDebugLabel: FlatLabel(defaultFlatLabel) {
margin: callDebugPadding;
}
callPanelDuration: 150;
callPanelSignalBars: CallSignalBars {
width: 3px;
radius: 1px;
skip: 1px;
color: callNameFg;
inactiveOpacity: 0.5;
}
callBarSignalBars: CallSignalBars(callPanelSignalBars) {
color: callBarFg;
}
callSignalMargin: 8px;
callSignalPadding: 4px;

View File

@ -558,6 +558,12 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
const auto call = static_cast<Call*>(controller->implData);
call->handleControllerStateChange(controller, state);
};
callbacks.signalBarCountChanged = [](
tgvoip::VoIPController *controller,
int count) {
const auto call = static_cast<Call*>(controller->implData);
call->handleControllerBarCountChange(controller, count);
};
_controller.create();
if (_mute) {
@ -584,9 +590,12 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
_controller->Connect();
}
void Call::handleControllerStateChange(tgvoip::VoIPController *controller, int state) {
void Call::handleControllerStateChange(
tgvoip::VoIPController *controller,
int state) {
// NB! Can be called from an arbitrary thread!
// Expects(controller == _controller.get()); This can be called from ~VoIPController()!
// This can be called from ~VoIPController()!
// Expects(controller == _controller.get());
Expects(controller->implData == static_cast<void*>(this));
switch (state) {
@ -615,6 +624,26 @@ void Call::handleControllerStateChange(tgvoip::VoIPController *controller, int s
}
}
void Call::handleControllerBarCountChange(
tgvoip::VoIPController *controller,
int count) {
// NB! Can be called from an arbitrary thread!
// This can be called from ~VoIPController()!
// Expects(controller == _controller.get());
Expects(controller->implData == static_cast<void*>(this));
InvokeQueued(this, [=] {
setSignalBarCount(count);
});
}
void Call::setSignalBarCount(int count) {
if (_signalBarCount != count) {
_signalBarCount = count;
_signalBarCountChanged.notify(count);
}
}
template <typename T>
bool Call::checkCallCommonFields(const T &call) {
auto checkFailed = [this] {
@ -708,6 +737,9 @@ void Call::setState(State state) {
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
Expects(type != FinishType::None);
setSignalBarCount(kSignalBarFinished);
auto finalState = (type == FinishType::Ended) ? State::Ended : State::Failed;
auto hangupState = (type == FinishType::Ended) ? State::HangingUp : State::FailedHangingUp;
if (_state == State::Requesting) {
@ -742,11 +774,15 @@ void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
}
void Call::setStateQueued(State state) {
InvokeQueued(this, [this, state] { setState(state); });
InvokeQueued(this, [=] {
setState(state);
});
}
void Call::setFailedQueued(int error) {
InvokeQueued(this, [this, error] { handleControllerError(error); });
InvokeQueued(this, [=] {
handleControllerError(error);
});
}
void Call::handleRequestError(const RPCError &error) {
@ -778,6 +814,7 @@ void Call::destroyController() {
_controller.reset();
DEBUG_LOG(("Call Info: Call controller destroyed."));
}
setSignalBarCount(kSignalBarFinished);
}
Call::~Call() {

View File

@ -93,6 +93,13 @@ public:
return _stateChanged;
}
static constexpr auto kSignalBarStarting = -1;
static constexpr auto kSignalBarFinished = -2;
static constexpr auto kSignalBarCount = 4;
base::Observable<int> &signalBarCountChanged() {
return _signalBarCountChanged;
}
void setMute(bool mute);
bool isMute() const {
return _mute;
@ -146,7 +153,12 @@ private:
void startWaitingTrack();
void generateModExpFirst(base::const_byte_span randomSeed);
void handleControllerStateChange(tgvoip::VoIPController *controller, int state);
void handleControllerStateChange(
tgvoip::VoIPController *controller,
int state);
void handleControllerBarCountChange(
tgvoip::VoIPController *controller,
int count);
void createAndStartController(const MTPDphoneCall &call);
template <typename T>
@ -159,6 +171,7 @@ private:
void setState(State state);
void setStateQueued(State state);
void setFailedQueued(int error);
void setSignalBarCount(int count);
void destroyController();
not_null<Delegate*> _delegate;
@ -168,6 +181,8 @@ private:
FinishType _finishAfterRequestingCall = FinishType::None;
bool _answerAfterDhConfigReceived = false;
base::Observable<State> _stateChanged;
int _signalBarCount = kSignalBarStarting;
base::Observable<int> _signalBarCountChanged;
TimeMs _startTime = 0;
base::DelayedCallTimer _finishByTimeoutTimer;
base::Timer _discardByTimeoutTimer;

View File

@ -67,6 +67,66 @@ private:
};
SignalBars::SignalBars(
QWidget *parent,
not_null<Call*> call,
const style::CallSignalBars &st,
base::lambda<void()> displayedChangedCallback)
: RpWidget(parent)
, _st(st)
, _displayedChangedCallback(std::move(displayedChangedCallback)) {
resize(
_st.width + (_st.width + _st.skip) * (Call::kSignalBarCount - 1),
_st.width * Call::kSignalBarCount);
subscribe(call->signalBarCountChanged(), [=](int count) {
changed(count);
});
}
bool SignalBars::isDisplayed() const {
return (_count >= 0);
}
void SignalBars::paintEvent(QPaintEvent *e) {
if (!isDisplayed()) {
return;
}
Painter p(this);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.setBrush(_st.color);
for (auto i = 0; i < Call::kSignalBarCount; ++i) {
p.setOpacity((i < _count) ? 1. : _st.inactiveOpacity);
const auto barHeight = (i + 1) * _st.width;
const auto barLeft = i * (_st.width + _st.skip);
const auto barTop = height() - barHeight;
p.drawRoundedRect(
barLeft,
barTop,
_st.width,
barHeight,
_st.radius,
_st.radius);
}
p.setOpacity(1.);
}
void SignalBars::changed(int count) {
if (_count == Call::kSignalBarFinished) {
return;
}
if (_count != count) {
const auto wasDisplayed = isDisplayed();
_count = count;
if (isDisplayed() != wasDisplayed && _displayedChangedCallback) {
_displayedChangedCallback();
}
update();
}
}
Panel::Button::Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo) : Ui::RippleButton(parent, stFrom.button.ripple)
, _stFrom(&stFrom)
, _stTo(stTo) {
@ -238,7 +298,8 @@ Panel::Panel(not_null<Call*> call)
, _cancel(this, object_ptr<Button>(this, st::callCancel))
, _mute(this, st::callMuteToggle)
, _name(this, st::callName)
, _status(this, st::callStatus) {
, _status(this, st::callStatus)
, _signalBars(this, call, st::callPanelSignalBars) {
_decline->setDuration(st::callPanelDuration);
_cancel->setDuration(st::callPanelDuration);
@ -337,10 +398,18 @@ void Panel::initControls() {
void Panel::reinitControls() {
Expects(_call != nullptr);
unsubscribe(_stateChangedSubscription);
_stateChangedSubscription = subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); });
unsubscribe(base::take(_stateChangedSubscription));
_stateChangedSubscription = subscribe(
_call->stateChanged(),
[=](State state) { stateChanged(state); });
stateChanged(_call->state());
_signalBars.create(
this,
_call,
st::callPanelSignalBars,
[=] { rtlupdate(signalBarsRect()); });
_name->setText(App::peerName(_call->user()));
updateStatusText(_call->state());
}
@ -585,6 +654,12 @@ void Panel::updateControlsGeometry() {
updateHangupGeometry();
_mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop);
const auto skip = st::callSignalMargin + st::callSignalPadding;
const auto delta = (_signalBars->width() - _signalBars->height());
_signalBars->moveToLeft(
_padding.left() + skip,
_padding.top() + skip + delta / 2);
}
void Panel::updateHangupGeometry() {
@ -637,6 +712,10 @@ void Panel::paintEvent(QPaintEvent *e) {
p.fillRect(0, _contentTop, width(), height() - _contentTop, brush);
}
if (_signalBars->isDisplayed()) {
paintSignalBarsBg(p);
}
if (!_fingerprint.empty()) {
App::roundRect(p, _fingerprintArea, st::callFingerprintBg, ImageRoundRadius::Small);
@ -651,6 +730,23 @@ void Panel::paintEvent(QPaintEvent *e) {
}
}
QRect Panel::signalBarsRect() const {
const auto size = 2 * st::callSignalPadding + _signalBars->width();
return QRect(
_padding.left() + st::callSignalMargin,
_padding.top() + st::callSignalMargin,
size,
size);
}
void Panel::paintSignalBarsBg(Painter &p) {
App::roundRect(
p,
signalBarsRect(),
st::callFingerprintBg,
ImageRoundRadius::Small);
}
void Panel::closeEvent(QCloseEvent *e) {
if (_call) {
_call->hangup();

View File

@ -20,8 +20,34 @@ template <typename Widget>
class FadeWrap;
} // namespace Ui
namespace style {
struct CallSignalBars;
} // namespace style
namespace Calls {
class SignalBars : public Ui::RpWidget, private base::Subscriber {
public:
SignalBars(
QWidget *parent,
not_null<Call*> call,
const style::CallSignalBars &st,
base::lambda<void()> displayedChangedCallback = nullptr);
bool isDisplayed() const;
protected:
void paintEvent(QPaintEvent *e) override;
private:
void changed(int count);
const style::CallSignalBars &_st;
int _count = Call::kSignalBarStarting;
base::lambda<void()> _displayedChangedCallback;
};
class Panel
: public Ui::RpWidget
, private base::Subscriber
@ -67,6 +93,8 @@ private:
void refreshUserPhoto();
bool isGoodUserPhoto(PhotoData *photo);
void createUserpicCache(ImagePtr image);
QRect signalBarsRect() const;
void paintSignalBarsBg(Painter &p);
void updateControlsGeometry();
void updateHangupGeometry();
@ -102,6 +130,7 @@ private:
object_ptr<Ui::IconButton> _mute;
object_ptr<Ui::FlatLabel> _name;
object_ptr<Ui::FlatLabel> _status;
object_ptr<SignalBars> _signalBars;
std::vector<EmojiPtr> _fingerprint;
QRect _fingerprintArea;

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "calls/calls_call.h"
#include "calls/calls_instance.h"
#include "calls/calls_panel.h"
#include "styles/style_boxes.h"
#include "observer_peer.h"
#include "boxes/abstract_box.h"
@ -75,6 +76,7 @@ TopBar::TopBar(
: RpWidget(parent)
, _call(call)
, _durationLabel(this, st::callBarLabel)
, _signalBars(this, _call.get(), st::callBarSignalBars)
, _fullInfoLabel(this, st::callBarInfoLabel)
, _shortInfoLabel(this, st::callBarInfoLabel)
, _hangupLabel(this, st::callBarLabel, lang(lng_call_bar_hangup).toUpper())
@ -169,14 +171,23 @@ void TopBar::resizeEvent(QResizeEvent *e) {
void TopBar::updateControlsGeometry() {
auto left = 0;
_mute->moveToLeft(left, 0); left += _mute->width();
_durationLabel->moveToLeft(left, st::callBarLabelTop); left += _durationLabel->width() + st::callBarSkip;
_mute->moveToLeft(left, 0);
left += _mute->width();
_durationLabel->moveToLeft(left, st::callBarLabelTop);
left += _durationLabel->width() + st::callBarSkip;
_signalBars->moveToLeft(left, (height() - _signalBars->height()) / 2);
left += _signalBars->width() + st::callBarSkip;
auto right = st::callBarRightSkip;
_hangupLabel->moveToRight(right, st::callBarLabelTop); right += _hangupLabel->width();
_hangupLabel->moveToRight(right, st::callBarLabelTop);
right += _hangupLabel->width();
right += st::callBarHangup.width;
_hangup->setGeometryToRight(0, 0, right, height());
_info->setGeometryToLeft(_mute->width(), 0, width() - _mute->width() - _hangup->width(), height());
_info->setGeometryToLeft(
_mute->width(),
0,
width() - _mute->width() - _hangup->width(),
height());
auto fullWidth = _fullInfoLabel->naturalWidth();
auto showFull = (left + fullWidth + right <= width());

View File

@ -21,6 +21,7 @@ class FlatLabel;
namespace Calls {
class Call;
class SignalBars;
class TopBar : public Ui::RpWidget, private base::Subscriber {
public:
@ -45,6 +46,7 @@ private:
bool _muted = false;
object_ptr<Ui::LabelSimple> _durationLabel;
object_ptr<SignalBars> _signalBars;
object_ptr<Ui::FlatLabel> _fullInfoLabel;
object_ptr<Ui::FlatLabel> _shortInfoLabel;
object_ptr<Ui::LabelSimple> _hangupLabel;