Use new button types from lib_ui.
This commit is contained in:
parent
bc01a364d0
commit
7cfb39ea5d
Binary file not shown.
After Width: | Height: | Size: 345 B |
Binary file not shown.
After Width: | Height: | Size: 591 B |
Binary file not shown.
After Width: | Height: | Size: 932 B |
|
@ -1817,6 +1817,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_group_call_settings" = "Settings";
|
"lng_group_call_settings" = "Settings";
|
||||||
"lng_group_call_unmute" = "Unmute";
|
"lng_group_call_unmute" = "Unmute";
|
||||||
"lng_group_call_you_are_live" = "You are Live";
|
"lng_group_call_you_are_live" = "You are Live";
|
||||||
|
"lng_group_call_force_muted" = "You are muted";
|
||||||
"lng_group_call_connecting" = "Connecting...";
|
"lng_group_call_connecting" = "Connecting...";
|
||||||
"lng_group_call_leave" = "Leave";
|
"lng_group_call_leave" = "Leave";
|
||||||
"lng_group_call_leave_title" = "Leave voice chat";
|
"lng_group_call_leave_title" = "Leave voice chat";
|
||||||
|
|
|
@ -120,12 +120,14 @@ callAnswer: CallButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bg: callAnswerBg;
|
bg: callAnswerBg;
|
||||||
|
bgSize: 44px;
|
||||||
|
bgPosition: point(12px, 12px);
|
||||||
angle: 135.;
|
angle: 135.;
|
||||||
outerRadius: 12px;
|
outerRadius: 12px;
|
||||||
outerBg: callAnswerBgOuter;
|
outerBg: callAnswerBgOuter;
|
||||||
label: callButtonLabel;
|
label: callButtonLabel;
|
||||||
}
|
}
|
||||||
callHangup: CallButton {
|
callHangup: CallButton(callAnswer) {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
icon: icon {{ "calls/call_discard", callIconFg }};
|
icon: icon {{ "calls/call_discard", callIconFg }};
|
||||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
@ -136,7 +138,7 @@ callHangup: CallButton {
|
||||||
outerBg: callHangupBg;
|
outerBg: callHangupBg;
|
||||||
label: callButtonLabel;
|
label: callButtonLabel;
|
||||||
}
|
}
|
||||||
callCancel: CallButton {
|
callCancel: CallButton(callAnswer) {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
icon: icon {{ "calls/call_cancel", callIconFgActive }};
|
icon: icon {{ "calls/call_cancel", callIconFgActive }};
|
||||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
@ -147,7 +149,7 @@ callCancel: CallButton {
|
||||||
outerBg: callIconBgActive;
|
outerBg: callIconBgActive;
|
||||||
label: callButtonLabel;
|
label: callButtonLabel;
|
||||||
}
|
}
|
||||||
callMicrophoneMute: CallButton {
|
callMicrophoneMute: CallButton(callAnswer) {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
icon: icon {{ "calls/call_record_active", callIconFg }};
|
icon: icon {{ "calls/call_record_active", callIconFg }};
|
||||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
@ -478,3 +480,19 @@ groupCallMutedButton: IconButton(groupCallInactiveButton) {
|
||||||
iconOver: icon {{ "calls/group_calls_muted", groupCallMemberMutedIcon }};
|
iconOver: icon {{ "calls/group_calls_muted", groupCallMemberMutedIcon }};
|
||||||
}
|
}
|
||||||
groupCallMemberButtonSkip: 10px;
|
groupCallMemberButtonSkip: 10px;
|
||||||
|
|
||||||
|
groupCallSettings: CallButton(callMicrophoneMute) {
|
||||||
|
button: IconButton(callButton) {
|
||||||
|
iconPosition: point(-1px, 22px);
|
||||||
|
icon: icon {{ "calls/call_settings", callIconFg }};
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: callMuteRipple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bg: callIconBg;
|
||||||
|
outerBg: callMuteRipple;
|
||||||
|
label: callButtonLabel;
|
||||||
|
}
|
||||||
|
groupCallButtonSkip: 43px;
|
||||||
|
groupCallButtonBottomSkip: 134px;
|
||||||
|
groupCallMuteBottomSkip: 149px;
|
||||||
|
|
|
@ -10,7 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "calls/calls_group_members.h"
|
#include "calls/calls_group_members.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/window.h"
|
#include "ui/widgets/window.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/widgets/call_button.h"
|
||||||
|
#include "ui/widgets/call_mute_button.h"
|
||||||
#include "ui/layers/layer_manager.h"
|
#include "ui/layers/layer_manager.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
@ -29,191 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
|
|
||||||
class GroupPanel::Button final : public Ui::RippleButton {
|
|
||||||
public:
|
|
||||||
Button(
|
|
||||||
QWidget *parent,
|
|
||||||
const style::CallButton &stFrom,
|
|
||||||
const style::CallButton *stTo = nullptr);
|
|
||||||
|
|
||||||
void setProgress(float64 progress);
|
|
||||||
void setText(rpl::producer<QString> text);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *e) override;
|
|
||||||
|
|
||||||
void onStateChanged(State was, StateChangeSource source) override;
|
|
||||||
|
|
||||||
QImage prepareRippleMask() const override;
|
|
||||||
QPoint prepareRippleStartPosition() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPoint iconPosition(not_null<const style::CallButton*> st) const;
|
|
||||||
void mixIconMasks();
|
|
||||||
|
|
||||||
not_null<const style::CallButton*> _stFrom;
|
|
||||||
const style::CallButton *_stTo = nullptr;
|
|
||||||
float64 _progress = 0.;
|
|
||||||
|
|
||||||
object_ptr<Ui::FlatLabel> _label = { nullptr };
|
|
||||||
|
|
||||||
QImage _bgMask, _bg;
|
|
||||||
QPixmap _bgFrom, _bgTo;
|
|
||||||
QImage _iconMixedMask, _iconFrom, _iconTo, _iconMixed;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
GroupPanel::Button::Button(
|
|
||||||
QWidget *parent,
|
|
||||||
const style::CallButton &stFrom,
|
|
||||||
const style::CallButton *stTo)
|
|
||||||
: Ui::RippleButton(parent, stFrom.button.ripple)
|
|
||||||
, _stFrom(&stFrom)
|
|
||||||
, _stTo(stTo) {
|
|
||||||
resize(_stFrom->button.width, _stFrom->button.height);
|
|
||||||
|
|
||||||
_bgMask = prepareRippleMask();
|
|
||||||
_bgFrom = App::pixmapFromImageInPlace(style::colorizeImage(_bgMask, _stFrom->bg));
|
|
||||||
if (_stTo) {
|
|
||||||
Assert(_stFrom->button.width == _stTo->button.width);
|
|
||||||
Assert(_stFrom->button.height == _stTo->button.height);
|
|
||||||
Assert(_stFrom->button.rippleAreaPosition == _stTo->button.rippleAreaPosition);
|
|
||||||
Assert(_stFrom->button.rippleAreaSize == _stTo->button.rippleAreaSize);
|
|
||||||
|
|
||||||
_bg = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_bg.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_bgTo = App::pixmapFromImageInPlace(style::colorizeImage(_bgMask, _stTo->bg));
|
|
||||||
_iconMixedMask = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconMixedMask.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_iconFrom = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconFrom.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_iconFrom.fill(Qt::black);
|
|
||||||
{
|
|
||||||
Painter p(&_iconFrom);
|
|
||||||
p.drawImage((_stFrom->button.rippleAreaSize - _stFrom->button.icon.width()) / 2, (_stFrom->button.rippleAreaSize - _stFrom->button.icon.height()) / 2, _stFrom->button.icon.instance(Qt::white));
|
|
||||||
}
|
|
||||||
_iconTo = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconTo.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_iconTo.fill(Qt::black);
|
|
||||||
{
|
|
||||||
Painter p(&_iconTo);
|
|
||||||
p.drawImage((_stTo->button.rippleAreaSize - _stTo->button.icon.width()) / 2, (_stTo->button.rippleAreaSize - _stTo->button.icon.height()) / 2, _stTo->button.icon.instance(Qt::white));
|
|
||||||
}
|
|
||||||
_iconMixed = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconMixed.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupPanel::Button::setText(rpl::producer<QString> text) {
|
|
||||||
_label.create(this, std::move(text), _stFrom->label);
|
|
||||||
_label->show();
|
|
||||||
rpl::combine(
|
|
||||||
sizeValue(),
|
|
||||||
_label->sizeValue()
|
|
||||||
) | rpl::start_with_next([=](QSize my, QSize label) {
|
|
||||||
_label->moveToLeft(
|
|
||||||
(my.width() - label.width()) / 2,
|
|
||||||
my.height() - label.height(),
|
|
||||||
my.width());
|
|
||||||
}, _label->lifetime());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupPanel::Button::setProgress(float64 progress) {
|
|
||||||
_progress = progress;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupPanel::Button::paintEvent(QPaintEvent *e) {
|
|
||||||
Painter p(this);
|
|
||||||
|
|
||||||
auto bgPosition = myrtlpoint(_stFrom->button.rippleAreaPosition);
|
|
||||||
auto paintFrom = (_progress == 0.) || !_stTo;
|
|
||||||
auto paintTo = !paintFrom && (_progress == 1.);
|
|
||||||
|
|
||||||
if (paintFrom) {
|
|
||||||
p.drawPixmap(bgPosition, _bgFrom);
|
|
||||||
} else if (paintTo) {
|
|
||||||
p.drawPixmap(bgPosition, _bgTo);
|
|
||||||
} else {
|
|
||||||
style::colorizeImage(_bgMask, anim::color(_stFrom->bg, _stTo->bg, _progress), &_bg);
|
|
||||||
p.drawImage(bgPosition, _bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rippleColorInterpolated = QColor();
|
|
||||||
auto rippleColorOverride = &rippleColorInterpolated;
|
|
||||||
if (paintFrom) {
|
|
||||||
rippleColorOverride = nullptr;
|
|
||||||
} else if (paintTo) {
|
|
||||||
rippleColorOverride = &_stTo->button.ripple.color->c;
|
|
||||||
} else {
|
|
||||||
rippleColorInterpolated = anim::color(_stFrom->button.ripple.color, _stTo->button.ripple.color, _progress);
|
|
||||||
}
|
|
||||||
paintRipple(p, _stFrom->button.rippleAreaPosition.x(), _stFrom->button.rippleAreaPosition.y(), rippleColorOverride);
|
|
||||||
|
|
||||||
auto positionFrom = iconPosition(_stFrom);
|
|
||||||
if (paintFrom) {
|
|
||||||
const auto icon = &_stFrom->button.icon;
|
|
||||||
icon->paint(p, positionFrom, width());
|
|
||||||
} else {
|
|
||||||
auto positionTo = iconPosition(_stTo);
|
|
||||||
if (paintTo) {
|
|
||||||
_stTo->button.icon.paint(p, positionTo, width());
|
|
||||||
} else {
|
|
||||||
mixIconMasks();
|
|
||||||
style::colorizeImage(_iconMixedMask, st::callIconFg->c, &_iconMixed);
|
|
||||||
p.drawImage(myrtlpoint(_stFrom->button.rippleAreaPosition), _iconMixed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint GroupPanel::Button::iconPosition(not_null<const style::CallButton*> st) const {
|
|
||||||
auto result = st->button.iconPosition;
|
|
||||||
if (result.x() < 0) {
|
|
||||||
result.setX((width() - st->button.icon.width()) / 2);
|
|
||||||
}
|
|
||||||
if (result.y() < 0) {
|
|
||||||
result.setY((height() - st->button.icon.height()) / 2);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupPanel::Button::mixIconMasks() {
|
|
||||||
_iconMixedMask.fill(Qt::black);
|
|
||||||
|
|
||||||
Painter p(&_iconMixedMask);
|
|
||||||
PainterHighQualityEnabler hq(p);
|
|
||||||
auto paintIconMask = [this, &p](const QImage &mask, float64 angle) {
|
|
||||||
auto skipFrom = _stFrom->button.rippleAreaSize / 2;
|
|
||||||
p.translate(skipFrom, skipFrom);
|
|
||||||
p.rotate(angle);
|
|
||||||
p.translate(-skipFrom, -skipFrom);
|
|
||||||
p.drawImage(0, 0, mask);
|
|
||||||
};
|
|
||||||
p.save();
|
|
||||||
paintIconMask(_iconFrom, (_stFrom->angle - _stTo->angle) * _progress);
|
|
||||||
p.restore();
|
|
||||||
p.setOpacity(_progress);
|
|
||||||
paintIconMask(_iconTo, (_stTo->angle - _stFrom->angle) * (1. - _progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupPanel::Button::onStateChanged(State was, StateChangeSource source) {
|
|
||||||
RippleButton::onStateChanged(was, source);
|
|
||||||
|
|
||||||
auto over = isOver();
|
|
||||||
auto wasOver = static_cast<bool>(was & StateFlag::Over);
|
|
||||||
if (over != wasOver) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint GroupPanel::Button::prepareRippleStartPosition() const {
|
|
||||||
return mapFromGlobal(QCursor::pos()) - _stFrom->button.rippleAreaPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage GroupPanel::Button::prepareRippleMask() const {
|
|
||||||
return Ui::RippleAnimation::ellipseMask(QSize(_stFrom->button.rippleAreaSize, _stFrom->button.rippleAreaSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
||||||
: _call(call)
|
: _call(call)
|
||||||
, _channel(call->channel())
|
, _channel(call->channel())
|
||||||
|
@ -225,9 +41,14 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
||||||
st::callTitle))
|
st::callTitle))
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
, _members(widget(), call)
|
, _members(widget(), call)
|
||||||
, _settings(widget(), st::callCancel)
|
, _settings(widget(), st::groupCallSettings)
|
||||||
, _hangup(widget(), st::callHangup)
|
, _mute(std::make_unique<Ui::CallMuteButton>(
|
||||||
, _mute(widget(), st::callMicrophoneMute, &st::callMicrophoneUnmute) {
|
widget(),
|
||||||
|
Ui::CallMuteButtonState{
|
||||||
|
.text = tr::lng_group_call_connecting(tr::now),
|
||||||
|
.type = Ui::CallMuteButtonType::Connecting,
|
||||||
|
}))
|
||||||
|
, _hangup(widget(), st::callHangup) {
|
||||||
initWindow();
|
initWindow();
|
||||||
initWidget();
|
initWidget();
|
||||||
initControls();
|
initControls();
|
||||||
|
@ -297,22 +118,17 @@ void GroupPanel::initWidget() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupPanel::initControls() {
|
void GroupPanel::initControls() {
|
||||||
_call->mutedValue(
|
_mute->clicks(
|
||||||
) | rpl::start_with_next([=](MuteState state) {
|
) | rpl::filter([=](Qt::MouseButton button) {
|
||||||
if (state == MuteState::ForceMuted) {
|
return (button == Qt::LeftButton);
|
||||||
_mute->clearState();
|
}) | rpl::start_with_next([=] {
|
||||||
}
|
|
||||||
_mute->setAttribute(
|
|
||||||
Qt::WA_TransparentForMouseEvents,
|
|
||||||
(state == MuteState::ForceMuted));
|
|
||||||
}, _mute->lifetime());
|
|
||||||
_mute->setClickedCallback([=] {
|
|
||||||
if (_call && _call->muted() != MuteState::ForceMuted) {
|
if (_call && _call->muted() != MuteState::ForceMuted) {
|
||||||
_call->setMuted((_call->muted() == MuteState::Active)
|
_call->setMuted((_call->muted() == MuteState::Active)
|
||||||
? MuteState::Muted
|
? MuteState::Muted
|
||||||
: MuteState::Active);
|
: MuteState::Active);
|
||||||
}
|
}
|
||||||
});
|
}, _mute->lifetime());
|
||||||
|
|
||||||
_hangup->setClickedCallback([=] {
|
_hangup->setClickedCallback([=] {
|
||||||
_layerBg->showBox(Box<ConfirmBox>(
|
_layerBg->showBox(Box<ConfirmBox>(
|
||||||
tr::lng_group_call_leave_sure(tr::now),
|
tr::lng_group_call_leave_sure(tr::now),
|
||||||
|
@ -321,6 +137,15 @@ void GroupPanel::initControls() {
|
||||||
});
|
});
|
||||||
_settings->setClickedCallback([=] {
|
_settings->setClickedCallback([=] {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_settings->setText(tr::lng_menu_settings());
|
||||||
|
_hangup->setText(tr::lng_box_leave());
|
||||||
|
|
||||||
|
_members->desiredHeightValue(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
updateControlsGeometry();
|
||||||
|
}, _members->lifetime());
|
||||||
|
|
||||||
initWithCall(_call);
|
initWithCall(_call);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,33 +158,44 @@ void GroupPanel::initWithCall(GroupCall *call) {
|
||||||
|
|
||||||
_channel = _call->channel();
|
_channel = _call->channel();
|
||||||
|
|
||||||
_settings->setText(tr::lng_menu_settings());
|
call->levelUpdates(
|
||||||
_hangup->setText(tr::lng_box_leave());
|
) | rpl::filter([=](const LevelUpdate &update) {
|
||||||
|
return update.self;
|
||||||
_call->mutedValue(
|
}) | rpl::start_with_next([=](const LevelUpdate &update) {
|
||||||
) | rpl::start_with_next([=](MuteState state) {
|
_mute->setLevel(update.value);
|
||||||
_mute->setProgress((state != MuteState::Active) ? 1. : 0.);
|
|
||||||
_mute->setText((state != MuteState::Active)
|
|
||||||
? tr::lng_call_unmute_audio()
|
|
||||||
: tr::lng_call_mute_audio());
|
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
|
|
||||||
_call->stateValue(
|
|
||||||
) | rpl::start_with_next([=](State state) {
|
|
||||||
stateChanged(state);
|
|
||||||
}, _callLifetime);
|
|
||||||
|
|
||||||
_members->desiredHeightValue(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
updateControlsGeometry();
|
|
||||||
}, _members->lifetime());
|
|
||||||
|
|
||||||
_members->toggleMuteRequests(
|
_members->toggleMuteRequests(
|
||||||
) | rpl::start_with_next([=](GroupMembers::MuteRequest request) {
|
) | rpl::start_with_next([=](GroupMembers::MuteRequest request) {
|
||||||
if (_call) {
|
if (_call) {
|
||||||
_call->toggleMute(request.user, request.mute);
|
_call->toggleMute(request.user, request.mute);
|
||||||
}
|
}
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
|
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
rpl::combine(
|
||||||
|
_call->mutedValue(),
|
||||||
|
_call->stateValue() | rpl::map(
|
||||||
|
_1 == State::Creating || _1 == State::Joining
|
||||||
|
)
|
||||||
|
) | rpl::start_with_next([=](MuteState mute, bool connecting) {
|
||||||
|
_mute->setState(Ui::CallMuteButtonState{
|
||||||
|
.text = (connecting
|
||||||
|
? tr::lng_group_call_connecting(tr::now)
|
||||||
|
: mute == MuteState::ForceMuted
|
||||||
|
? tr::lng_group_call_force_muted(tr::now)
|
||||||
|
: mute != MuteState::Active
|
||||||
|
? tr::lng_call_unmute_audio(tr::now)
|
||||||
|
: tr::lng_call_mute_audio(tr::now)),
|
||||||
|
.type = (connecting
|
||||||
|
? Ui::CallMuteButtonType::Connecting
|
||||||
|
: mute == MuteState::ForceMuted
|
||||||
|
? Ui::CallMuteButtonType::ForceMuted
|
||||||
|
: mute == MuteState::Muted
|
||||||
|
? Ui::CallMuteButtonType::Muted
|
||||||
|
: Ui::CallMuteButtonType::Active),
|
||||||
|
});
|
||||||
|
}, _callLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupPanel::initLayout() {
|
void GroupPanel::initLayout() {
|
||||||
|
@ -424,10 +260,10 @@ void GroupPanel::updateControlsGeometry() {
|
||||||
membersWidthAvailable,
|
membersWidthAvailable,
|
||||||
membersWidthMin,
|
membersWidthMin,
|
||||||
st::groupCallMembersWidthMax);
|
st::groupCallMembersWidthMax);
|
||||||
const auto muteTop = widget()->height() - 2 * _mute->height();
|
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
|
||||||
const auto buttonsTop = muteTop;
|
const auto buttonsTop = widget()->height() - st::groupCallButtonBottomSkip;
|
||||||
const auto membersTop = computeMembersListTop();
|
const auto membersTop = computeMembersListTop();
|
||||||
const auto availableHeight = buttonsTop
|
const auto availableHeight = muteTop
|
||||||
- membersTop
|
- membersTop
|
||||||
- st::groupCallMembersMargin.bottom();
|
- st::groupCallMembersMargin.bottom();
|
||||||
_members->setGeometry(
|
_members->setGeometry(
|
||||||
|
@ -435,9 +271,13 @@ void GroupPanel::updateControlsGeometry() {
|
||||||
membersTop,
|
membersTop,
|
||||||
membersWidth,
|
membersWidth,
|
||||||
std::min(desiredHeight, availableHeight));
|
std::min(desiredHeight, availableHeight));
|
||||||
_mute->move((widget()->width() - _mute->width()) / 2, muteTop);
|
const auto muteSize = _mute->innerSize().width();
|
||||||
_settings->moveToLeft(_settings->width(), buttonsTop);
|
const auto fullWidth = muteSize
|
||||||
_hangup->moveToRight(_settings->width(), buttonsTop);
|
+ 2 * _settings->width()
|
||||||
|
+ 2 * st::groupCallButtonSkip;
|
||||||
|
_mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop });
|
||||||
|
_settings->moveToLeft((widget()->width() - fullWidth) / 2, buttonsTop);
|
||||||
|
_hangup->moveToRight((widget()->width() - fullWidth) / 2, buttonsTop);
|
||||||
refreshTitle();
|
refreshTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,14 +335,4 @@ not_null<Ui::RpWidget*> GroupPanel::widget() const {
|
||||||
return _window->body();
|
return _window->body();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupPanel::stateChanged(State state) {
|
|
||||||
Expects(_call != nullptr);
|
|
||||||
|
|
||||||
if ((state != State::HangingUp)
|
|
||||||
&& (state != State::Ended)
|
|
||||||
&& (state != State::FailedHangingUp)
|
|
||||||
&& (state != State::Failed)) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Calls
|
} // namespace Calls
|
||||||
|
|
|
@ -22,6 +22,8 @@ class CloudImageView;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
class CallButton;
|
||||||
|
class CallMuteButton;
|
||||||
class IconButton;
|
class IconButton;
|
||||||
class FlatLabel;
|
class FlatLabel;
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
|
@ -57,7 +59,6 @@ public:
|
||||||
void closeBeforeDestroy();
|
void closeBeforeDestroy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Button;
|
|
||||||
using State = GroupCall::State;
|
using State = GroupCall::State;
|
||||||
|
|
||||||
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
|
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
|
||||||
|
@ -74,7 +75,6 @@ private:
|
||||||
bool handleClose();
|
bool handleClose();
|
||||||
|
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void stateChanged(State state);
|
|
||||||
void showControls();
|
void showControls();
|
||||||
|
|
||||||
[[nodiscard]] int computeMembersListTop() const;
|
[[nodiscard]] int computeMembersListTop() const;
|
||||||
|
@ -96,9 +96,9 @@ private:
|
||||||
object_ptr<Ui::FlatLabel> _title = { nullptr };
|
object_ptr<Ui::FlatLabel> _title = { nullptr };
|
||||||
object_ptr<GroupMembers> _members;
|
object_ptr<GroupMembers> _members;
|
||||||
|
|
||||||
object_ptr<Button> _settings;
|
object_ptr<Ui::CallButton> _settings;
|
||||||
object_ptr<Button> _hangup;
|
std::unique_ptr<Ui::CallMuteButton> _mute;
|
||||||
object_ptr<Button> _mute;
|
object_ptr<Ui::CallButton> _hangup;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "calls/calls_signal_bars.h"
|
#include "calls/calls_signal_bars.h"
|
||||||
#include "calls/calls_userpic.h"
|
#include "calls/calls_userpic.h"
|
||||||
#include "calls/calls_video_bubble.h"
|
#include "calls/calls_video_bubble.h"
|
||||||
|
#include "ui/widgets/call_button.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/window.h"
|
#include "ui/widgets/window.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
|
@ -170,226 +170,6 @@ void Panel::Incoming::fillBottomShadow(QPainter &p) {
|
||||||
factor * fill.height()));
|
factor * fill.height()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Panel::Button final : public Ui::RippleButton {
|
|
||||||
public:
|
|
||||||
Button(
|
|
||||||
QWidget *parent,
|
|
||||||
const style::CallButton &stFrom,
|
|
||||||
const style::CallButton *stTo = nullptr);
|
|
||||||
|
|
||||||
void setProgress(float64 progress);
|
|
||||||
void setOuterValue(float64 value);
|
|
||||||
void setText(rpl::producer<QString> text);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *e) override;
|
|
||||||
|
|
||||||
void onStateChanged(State was, StateChangeSource source) override;
|
|
||||||
|
|
||||||
QImage prepareRippleMask() const override;
|
|
||||||
QPoint prepareRippleStartPosition() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPoint iconPosition(not_null<const style::CallButton*> st) const;
|
|
||||||
void mixIconMasks();
|
|
||||||
|
|
||||||
not_null<const style::CallButton*> _stFrom;
|
|
||||||
const style::CallButton *_stTo = nullptr;
|
|
||||||
float64 _progress = 0.;
|
|
||||||
|
|
||||||
object_ptr<Ui::FlatLabel> _label = { nullptr };
|
|
||||||
|
|
||||||
QImage _bgMask, _bg;
|
|
||||||
QPixmap _bgFrom, _bgTo;
|
|
||||||
QImage _iconMixedMask, _iconFrom, _iconTo, _iconMixed;
|
|
||||||
|
|
||||||
float64 _outerValue = 0.;
|
|
||||||
Ui::Animations::Simple _outerAnimation;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Panel::Button::Button(
|
|
||||||
QWidget *parent,
|
|
||||||
const style::CallButton &stFrom,
|
|
||||||
const style::CallButton *stTo)
|
|
||||||
: Ui::RippleButton(parent, stFrom.button.ripple)
|
|
||||||
, _stFrom(&stFrom)
|
|
||||||
, _stTo(stTo) {
|
|
||||||
resize(_stFrom->button.width, _stFrom->button.height);
|
|
||||||
|
|
||||||
_bgMask = prepareRippleMask();
|
|
||||||
_bgFrom = App::pixmapFromImageInPlace(style::colorizeImage(_bgMask, _stFrom->bg));
|
|
||||||
if (_stTo) {
|
|
||||||
Assert(_stFrom->button.width == _stTo->button.width);
|
|
||||||
Assert(_stFrom->button.height == _stTo->button.height);
|
|
||||||
Assert(_stFrom->button.rippleAreaPosition == _stTo->button.rippleAreaPosition);
|
|
||||||
Assert(_stFrom->button.rippleAreaSize == _stTo->button.rippleAreaSize);
|
|
||||||
|
|
||||||
_bg = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_bg.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_bgTo = App::pixmapFromImageInPlace(style::colorizeImage(_bgMask, _stTo->bg));
|
|
||||||
_iconMixedMask = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconMixedMask.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_iconFrom = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconFrom.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_iconFrom.fill(Qt::black);
|
|
||||||
{
|
|
||||||
Painter p(&_iconFrom);
|
|
||||||
p.drawImage((_stFrom->button.rippleAreaSize - _stFrom->button.icon.width()) / 2, (_stFrom->button.rippleAreaSize - _stFrom->button.icon.height()) / 2, _stFrom->button.icon.instance(Qt::white));
|
|
||||||
}
|
|
||||||
_iconTo = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconTo.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
_iconTo.fill(Qt::black);
|
|
||||||
{
|
|
||||||
Painter p(&_iconTo);
|
|
||||||
p.drawImage((_stTo->button.rippleAreaSize - _stTo->button.icon.width()) / 2, (_stTo->button.rippleAreaSize - _stTo->button.icon.height()) / 2, _stTo->button.icon.instance(Qt::white));
|
|
||||||
}
|
|
||||||
_iconMixed = QImage(_bgMask.size(), QImage::Format_ARGB32_Premultiplied);
|
|
||||||
_iconMixed.setDevicePixelRatio(cRetinaFactor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::Button::setOuterValue(float64 value) {
|
|
||||||
if (_outerValue != value) {
|
|
||||||
_outerAnimation.start([this] {
|
|
||||||
if (_progress == 0. || _progress == 1.) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}, _outerValue, value, Call::kSoundSampleMs);
|
|
||||||
_outerValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::Button::setText(rpl::producer<QString> text) {
|
|
||||||
_label.create(this, std::move(text), _stFrom->label);
|
|
||||||
_label->show();
|
|
||||||
rpl::combine(
|
|
||||||
sizeValue(),
|
|
||||||
_label->sizeValue()
|
|
||||||
) | rpl::start_with_next([=](QSize my, QSize label) {
|
|
||||||
_label->moveToLeft(
|
|
||||||
(my.width() - label.width()) / 2,
|
|
||||||
my.height() - label.height(),
|
|
||||||
my.width());
|
|
||||||
}, _label->lifetime());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::Button::setProgress(float64 progress) {
|
|
||||||
_progress = progress;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::Button::paintEvent(QPaintEvent *e) {
|
|
||||||
Painter p(this);
|
|
||||||
|
|
||||||
auto bgPosition = myrtlpoint(_stFrom->button.rippleAreaPosition);
|
|
||||||
auto paintFrom = (_progress == 0.) || !_stTo;
|
|
||||||
auto paintTo = !paintFrom && (_progress == 1.);
|
|
||||||
|
|
||||||
auto outerValue = _outerAnimation.value(_outerValue);
|
|
||||||
if (outerValue > 0.) {
|
|
||||||
auto outerRadius = paintFrom ? _stFrom->outerRadius : paintTo ? _stTo->outerRadius : (_stFrom->outerRadius * (1. - _progress) + _stTo->outerRadius * _progress);
|
|
||||||
auto outerPixels = outerValue * outerRadius;
|
|
||||||
auto outerRect = QRectF(myrtlrect(bgPosition.x(), bgPosition.y(), _stFrom->button.rippleAreaSize, _stFrom->button.rippleAreaSize));
|
|
||||||
outerRect = outerRect.marginsAdded(QMarginsF(outerPixels, outerPixels, outerPixels, outerPixels));
|
|
||||||
|
|
||||||
PainterHighQualityEnabler hq(p);
|
|
||||||
if (paintFrom) {
|
|
||||||
p.setBrush(_stFrom->outerBg);
|
|
||||||
} else if (paintTo) {
|
|
||||||
p.setBrush(_stTo->outerBg);
|
|
||||||
} else {
|
|
||||||
p.setBrush(anim::brush(_stFrom->outerBg, _stTo->outerBg, _progress));
|
|
||||||
}
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
p.drawEllipse(outerRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paintFrom) {
|
|
||||||
p.drawPixmap(bgPosition, _bgFrom);
|
|
||||||
} else if (paintTo) {
|
|
||||||
p.drawPixmap(bgPosition, _bgTo);
|
|
||||||
} else {
|
|
||||||
style::colorizeImage(_bgMask, anim::color(_stFrom->bg, _stTo->bg, _progress), &_bg);
|
|
||||||
p.drawImage(bgPosition, _bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rippleColorInterpolated = QColor();
|
|
||||||
auto rippleColorOverride = &rippleColorInterpolated;
|
|
||||||
if (paintFrom) {
|
|
||||||
rippleColorOverride = nullptr;
|
|
||||||
} else if (paintTo) {
|
|
||||||
rippleColorOverride = &_stTo->button.ripple.color->c;
|
|
||||||
} else {
|
|
||||||
rippleColorInterpolated = anim::color(_stFrom->button.ripple.color, _stTo->button.ripple.color, _progress);
|
|
||||||
}
|
|
||||||
paintRipple(p, _stFrom->button.rippleAreaPosition.x(), _stFrom->button.rippleAreaPosition.y(), rippleColorOverride);
|
|
||||||
|
|
||||||
auto positionFrom = iconPosition(_stFrom);
|
|
||||||
if (paintFrom) {
|
|
||||||
const auto icon = &_stFrom->button.icon;
|
|
||||||
icon->paint(p, positionFrom, width());
|
|
||||||
} else {
|
|
||||||
auto positionTo = iconPosition(_stTo);
|
|
||||||
if (paintTo) {
|
|
||||||
_stTo->button.icon.paint(p, positionTo, width());
|
|
||||||
} else {
|
|
||||||
mixIconMasks();
|
|
||||||
style::colorizeImage(_iconMixedMask, st::callIconFg->c, &_iconMixed);
|
|
||||||
p.drawImage(myrtlpoint(_stFrom->button.rippleAreaPosition), _iconMixed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint Panel::Button::iconPosition(not_null<const style::CallButton*> st) const {
|
|
||||||
auto result = st->button.iconPosition;
|
|
||||||
if (result.x() < 0) {
|
|
||||||
result.setX((width() - st->button.icon.width()) / 2);
|
|
||||||
}
|
|
||||||
if (result.y() < 0) {
|
|
||||||
result.setY((height() - st->button.icon.height()) / 2);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::Button::mixIconMasks() {
|
|
||||||
_iconMixedMask.fill(Qt::black);
|
|
||||||
|
|
||||||
Painter p(&_iconMixedMask);
|
|
||||||
PainterHighQualityEnabler hq(p);
|
|
||||||
auto paintIconMask = [this, &p](const QImage &mask, float64 angle) {
|
|
||||||
auto skipFrom = _stFrom->button.rippleAreaSize / 2;
|
|
||||||
p.translate(skipFrom, skipFrom);
|
|
||||||
p.rotate(angle);
|
|
||||||
p.translate(-skipFrom, -skipFrom);
|
|
||||||
p.drawImage(0, 0, mask);
|
|
||||||
};
|
|
||||||
p.save();
|
|
||||||
paintIconMask(_iconFrom, (_stFrom->angle - _stTo->angle) * _progress);
|
|
||||||
p.restore();
|
|
||||||
p.setOpacity(_progress);
|
|
||||||
paintIconMask(_iconTo, (_stTo->angle - _stFrom->angle) * (1. - _progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::Button::onStateChanged(State was, StateChangeSource source) {
|
|
||||||
RippleButton::onStateChanged(was, source);
|
|
||||||
|
|
||||||
auto over = isOver();
|
|
||||||
auto wasOver = static_cast<bool>(was & StateFlag::Over);
|
|
||||||
if (over != wasOver) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint Panel::Button::prepareRippleStartPosition() const {
|
|
||||||
return mapFromGlobal(QCursor::pos()) - _stFrom->button.rippleAreaPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage Panel::Button::prepareRippleMask() const {
|
|
||||||
return Ui::RippleAnimation::ellipseMask(QSize(_stFrom->button.rippleAreaSize, _stFrom->button.rippleAreaSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
Panel::Panel(not_null<Call*> call)
|
Panel::Panel(not_null<Call*> call)
|
||||||
: _call(call)
|
: _call(call)
|
||||||
, _user(call->user())
|
, _user(call->user())
|
||||||
|
@ -402,8 +182,8 @@ Panel::Panel(not_null<Call*> call)
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
, _bodySt(&st::callBodyLayout)
|
, _bodySt(&st::callBodyLayout)
|
||||||
, _answerHangupRedial(widget(), st::callAnswer, &st::callHangup)
|
, _answerHangupRedial(widget(), st::callAnswer, &st::callHangup)
|
||||||
, _decline(widget(), object_ptr<Button>(widget(), st::callHangup))
|
, _decline(widget(), object_ptr<Ui::CallButton>(widget(), st::callHangup))
|
||||||
, _cancel(widget(), object_ptr<Button>(widget(), st::callCancel))
|
, _cancel(widget(), object_ptr<Ui::CallButton>(widget(), st::callCancel))
|
||||||
, _camera(widget(), st::callCameraMute, &st::callCameraUnmute)
|
, _camera(widget(), st::callCameraMute, &st::callCameraUnmute)
|
||||||
, _mute(widget(), st::callMicrophoneMute, &st::callMicrophoneUnmute)
|
, _mute(widget(), st::callMicrophoneMute, &st::callMicrophoneUnmute)
|
||||||
, _name(widget(), st::callName)
|
, _name(widget(), st::callName)
|
||||||
|
|
|
@ -23,6 +23,7 @@ class CloudImageView;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class IconButton;
|
class IconButton;
|
||||||
|
class CallButton;
|
||||||
class FlatLabel;
|
class FlatLabel;
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
class FadeWrap;
|
class FadeWrap;
|
||||||
|
@ -56,7 +57,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Incoming;
|
class Incoming;
|
||||||
class Button;
|
|
||||||
using State = Call::State;
|
using State = Call::State;
|
||||||
using Type = Call::Type;
|
using Type = Call::Type;
|
||||||
enum class AnswerHangupRedialState : uchar {
|
enum class AnswerHangupRedialState : uchar {
|
||||||
|
@ -115,15 +115,15 @@ private:
|
||||||
rpl::lifetime _callLifetime;
|
rpl::lifetime _callLifetime;
|
||||||
|
|
||||||
not_null<const style::CallBodyLayout*> _bodySt;
|
not_null<const style::CallBodyLayout*> _bodySt;
|
||||||
object_ptr<Button> _answerHangupRedial;
|
object_ptr<Ui::CallButton> _answerHangupRedial;
|
||||||
object_ptr<Ui::FadeWrap<Button>> _decline;
|
object_ptr<Ui::FadeWrap<Ui::CallButton>> _decline;
|
||||||
object_ptr<Ui::FadeWrap<Button>> _cancel;
|
object_ptr<Ui::FadeWrap<Ui::CallButton>> _cancel;
|
||||||
bool _hangupShown = false;
|
bool _hangupShown = false;
|
||||||
bool _outgoingPreviewInBody = false;
|
bool _outgoingPreviewInBody = false;
|
||||||
std::optional<AnswerHangupRedialState> _answerHangupRedialState;
|
std::optional<AnswerHangupRedialState> _answerHangupRedialState;
|
||||||
Ui::Animations::Simple _hangupShownProgress;
|
Ui::Animations::Simple _hangupShownProgress;
|
||||||
object_ptr<Button> _camera;
|
object_ptr<Ui::CallButton> _camera;
|
||||||
object_ptr<Button> _mute;
|
object_ptr<Ui::CallButton> _mute;
|
||||||
object_ptr<Ui::FlatLabel> _name;
|
object_ptr<Ui::FlatLabel> _name;
|
||||||
object_ptr<Ui::FlatLabel> _status;
|
object_ptr<Ui::FlatLabel> _status;
|
||||||
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
|
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e73bae12e26811f70adff5adfafbdb2548e02cd0
|
Subproject commit 962c8d89e52a8c92bc7987eba4cfaef77a3a9ebf
|
Loading…
Reference in New Issue