mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Improve design for all controls states.
This commit is contained in:
parent
d4b8fa70a7
commit
4971e281fa
7 changed files with 274 additions and 98 deletions
|
@ -18,8 +18,6 @@ CallSignalBars {
|
||||||
inactiveOpacity: double;
|
inactiveOpacity: double;
|
||||||
}
|
}
|
||||||
|
|
||||||
callWidth: 960px;
|
|
||||||
callHeight: 540px;
|
|
||||||
callRadius: 6px;
|
callRadius: 6px;
|
||||||
callShadow: Shadow {
|
callShadow: Shadow {
|
||||||
left: icon {{ "call_shadow_left", windowShadowFg }};
|
left: icon {{ "call_shadow_left", windowShadowFg }};
|
||||||
|
@ -33,22 +31,60 @@ callShadow: Shadow {
|
||||||
extend: margins(9px, 8px, 9px, 10px);
|
extend: margins(9px, 8px, 9px, 10px);
|
||||||
fallback: windowShadowFgFallback;
|
fallback: windowShadowFgFallback;
|
||||||
}
|
}
|
||||||
callPhotoSize: 180px;
|
|
||||||
callPhotoSmallSize: 100px;
|
|
||||||
|
|
||||||
callOutgoingPreviewSize: size(340px, 180px);
|
callWidthMin: 380px;
|
||||||
|
callHeightMin: 440px;
|
||||||
|
callWidth: 960px;
|
||||||
|
callHeight: 540px;
|
||||||
|
|
||||||
|
callBottomControlsHeight: 85px;
|
||||||
|
|
||||||
|
CallBodyLayout {
|
||||||
|
height: pixels;
|
||||||
|
photoTop: pixels;
|
||||||
|
photoSize: pixels;
|
||||||
|
nameTop: pixels;
|
||||||
|
statusTop: pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
callBodyLayout: CallBodyLayout {
|
||||||
|
height: 284px;
|
||||||
|
photoTop: 21px;
|
||||||
|
photoSize: 180px;
|
||||||
|
nameTop: 221px;
|
||||||
|
statusTop: 254px;
|
||||||
|
}
|
||||||
|
callBodyWithPreview: CallBodyLayout {
|
||||||
|
height: 185px;
|
||||||
|
photoTop: 21px;
|
||||||
|
photoSize: 100px;
|
||||||
|
nameTop: 132px;
|
||||||
|
statusTop: 163px;
|
||||||
|
}
|
||||||
|
|
||||||
|
callOutgoingPreviewMin: size(360px, 120px);
|
||||||
|
callOutgoingPreview: size(540px, 180px); // default, for height == callHeight.
|
||||||
|
callOutgoingPreviewMax: size(1080px, 360px);
|
||||||
callOutgoingDefaultSize: size(160px, 110px);
|
callOutgoingDefaultSize: size(160px, 110px);
|
||||||
|
|
||||||
|
callFingerprintPadding: margins(9px, 4px, 9px, 5px);
|
||||||
|
callFingerprintTop: 11px;
|
||||||
|
callFingerprintSkip: 3px;
|
||||||
|
callFingerprintBottom: -16px;
|
||||||
|
|
||||||
callButton: IconButton {
|
callButton: IconButton {
|
||||||
width: 72px;
|
width: 64px;
|
||||||
height: 72px;
|
height: 64px;
|
||||||
|
|
||||||
iconPosition: point(-1px, -1px);
|
iconPosition: point(-1px, -1px);
|
||||||
|
|
||||||
rippleAreaPosition: point(12px, 12px);
|
rippleAreaPosition: point(10px, 10px);
|
||||||
rippleAreaSize: 48px;
|
rippleAreaSize: 44px;
|
||||||
ripple: defaultRippleAnimation;
|
ripple: defaultRippleAnimation;
|
||||||
}
|
}
|
||||||
|
callButtonLabel: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: callNameFg;
|
||||||
|
}
|
||||||
|
|
||||||
callAnswer: CallButton {
|
callAnswer: CallButton {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
|
@ -61,6 +97,7 @@ callAnswer: CallButton {
|
||||||
angle: 135.;
|
angle: 135.;
|
||||||
outerRadius: 12px;
|
outerRadius: 12px;
|
||||||
outerBg: callAnswerBgOuter;
|
outerBg: callAnswerBgOuter;
|
||||||
|
label: callButtonLabel;
|
||||||
}
|
}
|
||||||
callHangup: CallButton {
|
callHangup: CallButton {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
|
@ -71,6 +108,7 @@ callHangup: CallButton {
|
||||||
}
|
}
|
||||||
bg: callHangupBg;
|
bg: callHangupBg;
|
||||||
outerBg: callHangupBg;
|
outerBg: callHangupBg;
|
||||||
|
label: callButtonLabel;
|
||||||
}
|
}
|
||||||
callCancel: CallButton {
|
callCancel: CallButton {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
|
@ -81,27 +119,30 @@ callCancel: CallButton {
|
||||||
}
|
}
|
||||||
bg: callCancelBg;
|
bg: callCancelBg;
|
||||||
outerBg: callCancelBg;
|
outerBg: callCancelBg;
|
||||||
|
label: callButtonLabel;
|
||||||
}
|
}
|
||||||
callMuteToggle: IconButton(callButton) {
|
callMuteToggle: CallButton {
|
||||||
icon: icon {{ "call_record_active", callIconFg }};
|
button: IconButton(callButton) {
|
||||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
icon: icon {{ "call_record_active", callIconFg }};
|
||||||
color: callMuteRipple;
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: callMuteRipple;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
bg: callMuteRipple;
|
||||||
|
outerBg: callMuteRipple;
|
||||||
|
label: callButtonLabel;
|
||||||
}
|
}
|
||||||
callUnmuteIcon: icon {{ "call_record_muted", callIconFg }};
|
callUnmuteIcon: icon {{ "call_record_muted", callIconFg }};
|
||||||
callCameraToggle: IconButton(callButton) {
|
callCameraToggle: CallButton(callMuteToggle) {
|
||||||
icon: icon {{ "call_camera_active", callIconFg }};
|
button: IconButton(callButton) {
|
||||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
icon: icon {{ "call_camera_active", callIconFg }};
|
||||||
color: callMuteRipple;
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: callMuteRipple;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callNoCameraIcon: icon {{ "call_camera_muted", callIconFg }};
|
callNoCameraIcon: icon {{ "call_camera_muted", callIconFg }};
|
||||||
|
|
||||||
callControlsTop: 460px;
|
|
||||||
callControlsSkip: 0px;
|
|
||||||
callMuteRight: 8px;
|
|
||||||
|
|
||||||
callNameTop: 15px;
|
|
||||||
callName: FlatLabel(defaultFlatLabel) {
|
callName: FlatLabel(defaultFlatLabel) {
|
||||||
minWidth: 260px;
|
minWidth: 260px;
|
||||||
maxHeight: 30px;
|
maxHeight: 30px;
|
||||||
|
@ -113,7 +154,6 @@ callName: FlatLabel(defaultFlatLabel) {
|
||||||
linkFontOver: font(21px semibold underline);
|
linkFontOver: font(21px semibold underline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callStatusTop: 46px;
|
|
||||||
callStatus: FlatLabel(defaultFlatLabel) {
|
callStatus: FlatLabel(defaultFlatLabel) {
|
||||||
minWidth: 260px;
|
minWidth: 260px;
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
|
@ -126,10 +166,6 @@ callStatus: FlatLabel(defaultFlatLabel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callFingerprintPadding: margins(9px, 4px, 9px, 5px);
|
|
||||||
callFingerprintSkip: 3px;
|
|
||||||
callFingerprintBottom: 8px;
|
|
||||||
|
|
||||||
callBarHeight: 38px;
|
callBarHeight: 38px;
|
||||||
callBarMuteToggle: IconButton {
|
callBarMuteToggle: IconButton {
|
||||||
width: 41px;
|
width: 41px;
|
||||||
|
|
|
@ -57,6 +57,8 @@ public:
|
||||||
void setProgress(float64 progress);
|
void setProgress(float64 progress);
|
||||||
void setOuterValue(float64 value);
|
void setOuterValue(float64 value);
|
||||||
|
|
||||||
|
void setIconOverride(const style::icon *iconOverride);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
@ -73,6 +75,8 @@ private:
|
||||||
const style::CallButton *_stTo = nullptr;
|
const style::CallButton *_stTo = nullptr;
|
||||||
float64 _progress = 0.;
|
float64 _progress = 0.;
|
||||||
|
|
||||||
|
const style::icon *_iconOverride = nullptr;
|
||||||
|
|
||||||
QImage _bgMask, _bg;
|
QImage _bgMask, _bg;
|
||||||
QPixmap _bgFrom, _bgTo;
|
QPixmap _bgFrom, _bgTo;
|
||||||
QImage _iconMixedMask, _iconFrom, _iconTo, _iconMixed;
|
QImage _iconMixedMask, _iconFrom, _iconTo, _iconMixed;
|
||||||
|
@ -130,6 +134,11 @@ void Panel::Button::setOuterValue(float64 value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::Button::setIconOverride(const style::icon *iconOverride) {
|
||||||
|
_iconOverride = iconOverride;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::Button::setProgress(float64 progress) {
|
void Panel::Button::setProgress(float64 progress) {
|
||||||
_progress = progress;
|
_progress = progress;
|
||||||
update();
|
update();
|
||||||
|
@ -183,7 +192,8 @@ void Panel::Button::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
auto positionFrom = iconPosition(_stFrom);
|
auto positionFrom = iconPosition(_stFrom);
|
||||||
if (paintFrom) {
|
if (paintFrom) {
|
||||||
_stFrom->button.icon.paint(p, positionFrom, width());
|
const auto icon = _iconOverride ? _iconOverride : &_stFrom->button.icon;
|
||||||
|
icon->paint(p, positionFrom, width());
|
||||||
} else {
|
} else {
|
||||||
auto positionTo = iconPosition(_stTo);
|
auto positionTo = iconPosition(_stTo);
|
||||||
if (paintTo) {
|
if (paintTo) {
|
||||||
|
@ -248,6 +258,7 @@ Panel::Panel(not_null<Call*> call)
|
||||||
: RpWidget(Core::App().getModalParent())
|
: RpWidget(Core::App().getModalParent())
|
||||||
, _call(call)
|
, _call(call)
|
||||||
, _user(call->user())
|
, _user(call->user())
|
||||||
|
, _bodySt(&st::callBodyLayout)
|
||||||
, _answerHangupRedial(this, st::callAnswer, &st::callHangup)
|
, _answerHangupRedial(this, st::callAnswer, &st::callHangup)
|
||||||
, _decline(this, object_ptr<Button>(this, st::callHangup))
|
, _decline(this, object_ptr<Button>(this, st::callHangup))
|
||||||
, _cancel(this, object_ptr<Button>(this, st::callCancel))
|
, _cancel(this, object_ptr<Button>(this, st::callCancel))
|
||||||
|
@ -381,8 +392,6 @@ void Panel::reinitWithCall(Call *call) {
|
||||||
_outgoingVideoBubble = std::make_unique<VideoBubble>(
|
_outgoingVideoBubble = std::make_unique<VideoBubble>(
|
||||||
this,
|
this,
|
||||||
_call->videoOutgoing());
|
_call->videoOutgoing());
|
||||||
_outgoingVideoBubble->setSizeConstraints(
|
|
||||||
st::callOutgoingPreviewSize);
|
|
||||||
|
|
||||||
_call->mutedValue(
|
_call->mutedValue(
|
||||||
) | rpl::start_with_next([=](bool mute) {
|
) | rpl::start_with_next([=](bool mute) {
|
||||||
|
@ -414,6 +423,19 @@ void Panel::reinitWithCall(Call *call) {
|
||||||
checkForInactiveShow();
|
checkForInactiveShow();
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
_call->stateValue(),
|
||||||
|
_call->videoOutgoing()->renderNextFrame()
|
||||||
|
) | rpl::start_with_next([=](State state, auto) {
|
||||||
|
if (state != State::Ended
|
||||||
|
&& state != State::EndedByOtherDevice
|
||||||
|
&& state != State::Failed
|
||||||
|
&& state != State::FailedHangingUp
|
||||||
|
&& state != State::HangingUp) {
|
||||||
|
refreshOutgoingPreviewInBody(state);
|
||||||
|
}
|
||||||
|
}, _callLifetime);
|
||||||
|
|
||||||
_name->setText(_user->name);
|
_name->setText(_user->name);
|
||||||
updateStatusText(_call->state());
|
updateStatusText(_call->state());
|
||||||
}
|
}
|
||||||
|
@ -512,8 +534,6 @@ void Panel::initGeometry() {
|
||||||
_useTransparency = Ui::Platform::TranslucentWindowsSupported(center);
|
_useTransparency = Ui::Platform::TranslucentWindowsSupported(center);
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
|
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
|
||||||
_padding = _useTransparency ? st::callShadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth);
|
_padding = _useTransparency ? st::callShadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth);
|
||||||
_controlsTop = _padding.top() + st::callControlsTop;
|
|
||||||
_contentTop = _padding.top() + 2 * st::callPhotoSize;
|
|
||||||
const auto rect = [&] {
|
const auto rect = [&] {
|
||||||
const QRect initRect(0, 0, st::callWidth, st::callHeight);
|
const QRect initRect(0, 0, st::callWidth, st::callHeight);
|
||||||
return initRect.translated(center - initRect.center()).marginsAdded(_padding);
|
return initRect.translated(center - initRect.center()).marginsAdded(_padding);
|
||||||
|
@ -525,6 +545,18 @@ void Panel::initGeometry() {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::refreshOutgoingPreviewInBody(State state) {
|
||||||
|
const auto inBody = (state != State::Established)
|
||||||
|
&& (_call->videoOutgoing()->state() != webrtc::VideoState::Inactive)
|
||||||
|
&& !_call->videoOutgoing()->frameSize().isEmpty();
|
||||||
|
if (_outgoingPreviewInBody == inBody) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_outgoingPreviewInBody = inBody;
|
||||||
|
_bodySt = inBody ? &st::callBodyWithPreview : &st::callBodyLayout;
|
||||||
|
updateControlsGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::createBottomImage() {
|
void Panel::createBottomImage() {
|
||||||
if (!_useTransparency) {
|
if (!_useTransparency) {
|
||||||
return;
|
return;
|
||||||
|
@ -571,22 +603,54 @@ void Panel::resizeEvent(QResizeEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::updateControlsGeometry() {
|
void Panel::updateControlsGeometry() {
|
||||||
const auto size = st::callPhotoSize;
|
const auto innerHeight = height() - _padding.top() - _padding.bottom();
|
||||||
_userpic->setGeometry((width() - size) / 2, st::callPhotoSize, size);
|
const auto availableTop = _padding.top() + _fingerprintHeight;
|
||||||
_name->moveToLeft((width() - _name->width()) / 2, _contentTop + st::callNameTop);
|
const auto available = height()
|
||||||
|
- (st::callBottomControlsHeight + _padding.bottom())
|
||||||
|
- availableTop;
|
||||||
|
const auto bodyPreviewSizeMax = st::callOutgoingPreviewMin
|
||||||
|
+ ((st::callOutgoingPreview
|
||||||
|
- st::callOutgoingPreviewMin)
|
||||||
|
* (innerHeight - st::callHeightMin)
|
||||||
|
/ (st::callHeight - st::callHeightMin));
|
||||||
|
const auto bodyPreviewSize = QSize(
|
||||||
|
std::min(bodyPreviewSizeMax.width(), st::callOutgoingPreviewMax.width()),
|
||||||
|
std::min(bodyPreviewSizeMax.height(), st::callOutgoingPreviewMax.height()));
|
||||||
|
const auto contentHeight = _bodySt->height
|
||||||
|
+ (_outgoingPreviewInBody ? bodyPreviewSize.height() : 0);
|
||||||
|
const auto remainingHeight = available - contentHeight;
|
||||||
|
const auto skipHeight = remainingHeight
|
||||||
|
/ (_outgoingPreviewInBody ? 3 : 2);
|
||||||
|
|
||||||
|
_bodyTop = availableTop + skipHeight;
|
||||||
|
_buttonsTop = availableTop + available;
|
||||||
|
const auto previewTop = _bodyTop + _bodySt->height + skipHeight;
|
||||||
|
|
||||||
|
_userpic->setGeometry(
|
||||||
|
(width() - _bodySt->photoSize) / 2,
|
||||||
|
_bodyTop + _bodySt->photoTop,
|
||||||
|
_bodySt->photoSize);
|
||||||
|
|
||||||
|
_name->moveToLeft(
|
||||||
|
(width() - _name->width()) / 2,
|
||||||
|
_bodyTop + _bodySt->nameTop);
|
||||||
updateStatusGeometry();
|
updateStatusGeometry();
|
||||||
|
|
||||||
_outgoingVideoBubble->setBoundingRect({
|
if (_outgoingPreviewInBody) {
|
||||||
(width() - st::callOutgoingPreviewSize.width()) / 2,
|
_outgoingVideoBubble->updateGeometry(
|
||||||
_contentTop + st::callStatusTop + _status->height(),
|
VideoBubble::DragMode::None,
|
||||||
st::callOutgoingPreviewSize.width(),
|
QRect(
|
||||||
st::callOutgoingPreviewSize.height()
|
(width() - bodyPreviewSize.width()) / 2,
|
||||||
});
|
previewTop,
|
||||||
|
bodyPreviewSize.width(),
|
||||||
|
bodyPreviewSize.height()));
|
||||||
|
} else {
|
||||||
|
updateOutgoingVideoBubbleGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
auto controlsTop = _padding.top() + st::callControlsTop;
|
auto bothWidth = _answerHangupRedial->width() + st::callCancel.button.width;
|
||||||
auto bothWidth = _answerHangupRedial->width() + st::callControlsSkip + st::callCancel.button.width;
|
_decline->moveToLeft((width() - bothWidth) / 2, _buttonsTop);
|
||||||
_decline->moveToLeft((width() - bothWidth) / 2, controlsTop);
|
_cancel->moveToLeft((width() - bothWidth) / 2, _buttonsTop);
|
||||||
_cancel->moveToLeft((width() - bothWidth) / 2, controlsTop);
|
|
||||||
|
|
||||||
updateHangupGeometry();
|
updateHangupGeometry();
|
||||||
|
|
||||||
|
@ -597,22 +661,37 @@ void Panel::updateControlsGeometry() {
|
||||||
_padding.top() + skip + delta / 2);
|
_padding.top() + skip + delta / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::updateOutgoingVideoBubbleGeometry() {
|
||||||
|
Expects(!_outgoingPreviewInBody);
|
||||||
|
|
||||||
|
const auto size = st::callOutgoingDefaultSize;
|
||||||
|
const auto availableHeight = height() - st::callBottomControlsHeight;
|
||||||
|
const auto padding = 2 * _padding;
|
||||||
|
_outgoingVideoBubble->updateGeometry(
|
||||||
|
VideoBubble::DragMode::SnapToCorners,
|
||||||
|
QRect(
|
||||||
|
padding.left(),
|
||||||
|
padding.top(),
|
||||||
|
width() - padding.left() - padding.right(),
|
||||||
|
height() - padding.left() - padding.bottom()),
|
||||||
|
size);
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::updateHangupGeometry() {
|
void Panel::updateHangupGeometry() {
|
||||||
auto singleWidth = _answerHangupRedial->width();
|
auto singleWidth = _answerHangupRedial->width();
|
||||||
auto bothWidth = singleWidth + st::callControlsSkip + st::callCancel.button.width;
|
auto bothWidth = singleWidth + st::callCancel.button.width;
|
||||||
auto rightFrom = (width() - bothWidth) / 2;
|
auto rightFrom = (width() - bothWidth) / 2;
|
||||||
auto rightTo = (width() - singleWidth) / 2;
|
auto rightTo = (width() - singleWidth) / 2;
|
||||||
auto hangupProgress = _hangupShownProgress.value(_hangupShown ? 1. : 0.);
|
auto hangupProgress = _hangupShownProgress.value(_hangupShown ? 1. : 0.);
|
||||||
auto hangupRight = anim::interpolate(rightFrom, rightTo, hangupProgress);
|
auto hangupRight = anim::interpolate(rightFrom, rightTo, hangupProgress);
|
||||||
auto controlsTop = _padding.top() + st::callControlsTop;
|
_answerHangupRedial->moveToRight(hangupRight, _buttonsTop);
|
||||||
_answerHangupRedial->moveToRight(hangupRight, controlsTop);
|
|
||||||
_answerHangupRedial->setProgress(hangupProgress);
|
_answerHangupRedial->setProgress(hangupProgress);
|
||||||
_mute->moveToRight(hangupRight - _mute->width(), controlsTop);
|
_mute->moveToRight(hangupRight - _mute->width(), _buttonsTop);
|
||||||
_camera->moveToLeft(hangupRight - _mute->width(), controlsTop);
|
_camera->moveToLeft(hangupRight - _mute->width(), _buttonsTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::updateStatusGeometry() {
|
void Panel::updateStatusGeometry() {
|
||||||
_status->moveToLeft((width() - _status->width()) / 2, _contentTop + st::callStatusTop);
|
_status->moveToLeft((width() - _status->width()) / 2, _bodyTop + _bodySt->statusTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::paintEvent(QPaintEvent *e) {
|
void Panel::paintEvent(QPaintEvent *e) {
|
||||||
|
@ -707,10 +786,6 @@ void Panel::mousePressEvent(QMouseEvent *e) {
|
||||||
_dragging = true;
|
_dragging = true;
|
||||||
_dragStartMousePosition = e->globalPos();
|
_dragStartMousePosition = e->globalPos();
|
||||||
_dragStartMyPosition = QPoint(x(), y());
|
_dragStartMyPosition = QPoint(x(), y());
|
||||||
} else if (!rect().contains(e->pos())) {
|
|
||||||
if (_call && _call->state() == State::Established) {
|
|
||||||
hideDeactivated();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -833,8 +908,14 @@ void Panel::fillFingerprint() {
|
||||||
auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip;
|
auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip;
|
||||||
auto rectHeight = size;
|
auto rectHeight = size;
|
||||||
auto left = (width() - rectWidth) / 2;
|
auto left = (width() - rectWidth) / 2;
|
||||||
auto top = _padding.top() + st::callFingerprintBottom;
|
_fingerprintArea = QRect(
|
||||||
_fingerprintArea = QRect(left, top, rectWidth, rectHeight).marginsAdded(st::callFingerprintPadding);
|
left,
|
||||||
|
_padding.top() + st::callFingerprintTop + st::callFingerprintPadding.top(),
|
||||||
|
rectWidth,
|
||||||
|
rectHeight
|
||||||
|
).marginsAdded(st::callFingerprintPadding);
|
||||||
|
_fingerprintHeight = st::callFingerprintTop + _fingerprintArea.height() + st::callFingerprintBottom;
|
||||||
|
updateControlsGeometry();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ class FadeWrap;
|
||||||
|
|
||||||
namespace style {
|
namespace style {
|
||||||
struct CallSignalBars;
|
struct CallSignalBars;
|
||||||
|
struct CallBodyLayout;
|
||||||
} // namespace style
|
} // namespace style
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
|
@ -60,6 +61,7 @@ protected:
|
||||||
bool eventHook(QEvent *e) override;
|
bool eventHook(QEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class Button;
|
||||||
using State = Call::State;
|
using State = Call::State;
|
||||||
using Type = Call::Type;
|
using Type = Call::Type;
|
||||||
|
|
||||||
|
@ -82,6 +84,7 @@ private:
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void updateHangupGeometry();
|
void updateHangupGeometry();
|
||||||
void updateStatusGeometry();
|
void updateStatusGeometry();
|
||||||
|
void updateOutgoingVideoBubbleGeometry();
|
||||||
void stateChanged(State state);
|
void stateChanged(State state);
|
||||||
void showControls();
|
void showControls();
|
||||||
void updateStatusText(State state);
|
void updateStatusText(State state);
|
||||||
|
@ -95,6 +98,7 @@ private:
|
||||||
[[nodiscard]] bool hasActiveVideo() const;
|
[[nodiscard]] bool hasActiveVideo() const;
|
||||||
void checkForInactiveHide();
|
void checkForInactiveHide();
|
||||||
void checkForInactiveShow();
|
void checkForInactiveShow();
|
||||||
|
void refreshOutgoingPreviewInBody(State state);
|
||||||
|
|
||||||
Call *_call = nullptr;
|
Call *_call = nullptr;
|
||||||
not_null<UserData*> _user;
|
not_null<UserData*> _user;
|
||||||
|
@ -102,8 +106,6 @@ private:
|
||||||
bool _useTransparency = true;
|
bool _useTransparency = true;
|
||||||
bool _incomingShown = false;
|
bool _incomingShown = false;
|
||||||
style::margins _padding;
|
style::margins _padding;
|
||||||
int _contentTop = 0;
|
|
||||||
int _controlsTop = 0;
|
|
||||||
|
|
||||||
bool _dragging = false;
|
bool _dragging = false;
|
||||||
QPoint _dragStartMousePosition;
|
QPoint _dragStartMousePosition;
|
||||||
|
@ -111,14 +113,15 @@ private:
|
||||||
|
|
||||||
rpl::lifetime _callLifetime;
|
rpl::lifetime _callLifetime;
|
||||||
|
|
||||||
class Button;
|
not_null<const style::CallBodyLayout*> _bodySt;
|
||||||
object_ptr<Button> _answerHangupRedial;
|
object_ptr<Button> _answerHangupRedial;
|
||||||
object_ptr<Ui::FadeWrap<Button>> _decline;
|
object_ptr<Ui::FadeWrap<Button>> _decline;
|
||||||
object_ptr<Ui::FadeWrap<Button>> _cancel;
|
object_ptr<Ui::FadeWrap<Button>> _cancel;
|
||||||
bool _hangupShown = false;
|
bool _hangupShown = false;
|
||||||
|
bool _outgoingPreviewInBody = false;
|
||||||
Ui::Animations::Simple _hangupShownProgress;
|
Ui::Animations::Simple _hangupShownProgress;
|
||||||
object_ptr<Ui::IconButton> _camera;
|
object_ptr<Button> _camera;
|
||||||
object_ptr<Ui::IconButton> _mute;
|
object_ptr<Button> _mute;
|
||||||
object_ptr<Ui::FlatLabel> _name;
|
object_ptr<Ui::FlatLabel> _name;
|
||||||
object_ptr<Ui::FlatLabel> _status;
|
object_ptr<Ui::FlatLabel> _status;
|
||||||
object_ptr<SignalBars> _signalBars = { nullptr };
|
object_ptr<SignalBars> _signalBars = { nullptr };
|
||||||
|
@ -126,6 +129,9 @@ private:
|
||||||
std::unique_ptr<VideoBubble> _outgoingVideoBubble;
|
std::unique_ptr<VideoBubble> _outgoingVideoBubble;
|
||||||
std::vector<EmojiPtr> _fingerprint;
|
std::vector<EmojiPtr> _fingerprint;
|
||||||
QRect _fingerprintArea;
|
QRect _fingerprintArea;
|
||||||
|
int _bodyTop = 0;
|
||||||
|
int _buttonsTop = 0;
|
||||||
|
int _fingerprintHeight = 0;
|
||||||
|
|
||||||
base::Timer _updateDurationTimer;
|
base::Timer _updateDurationTimer;
|
||||||
base::Timer _updateOuterRippleTimer;
|
base::Timer _updateOuterRippleTimer;
|
||||||
|
|
|
@ -44,10 +44,12 @@ void Userpic::setGeometry(int x, int y, int size) {
|
||||||
if (this->size() != size) {
|
if (this->size() != size) {
|
||||||
_userPhoto = QPixmap();
|
_userPhoto = QPixmap();
|
||||||
_userPhotoFull = false;
|
_userPhotoFull = false;
|
||||||
refreshPhoto();
|
|
||||||
}
|
}
|
||||||
_content.setGeometry(x, y, size, size);
|
_content.setGeometry(x, y, size, size);
|
||||||
_content.update();
|
_content.update();
|
||||||
|
if (_userPhoto.isNull()) {
|
||||||
|
refreshPhoto();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Userpic::setup(rpl::producer<bool> muted) {
|
void Userpic::setup(rpl::producer<bool> muted) {
|
||||||
|
@ -139,28 +141,29 @@ void Userpic::refreshPhoto() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Userpic::createCache(Image *image) {
|
void Userpic::createCache(Image *image) {
|
||||||
auto size = this->size() * cIntRetinaFactor();
|
const auto size = this->size();
|
||||||
|
const auto real = size * cIntRetinaFactor();
|
||||||
auto options = Images::Option::Smooth | Images::Option::Circled;
|
auto options = Images::Option::Smooth | Images::Option::Circled;
|
||||||
// _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None;
|
// _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None;
|
||||||
if (image) {
|
if (image) {
|
||||||
auto width = image->width();
|
auto width = image->width();
|
||||||
auto height = image->height();
|
auto height = image->height();
|
||||||
if (width > height) {
|
if (width > height) {
|
||||||
width = qMax((width * size) / height, 1);
|
width = qMax((width * real) / height, 1);
|
||||||
height = size;
|
height = real;
|
||||||
} else {
|
} else {
|
||||||
height = qMax((height * size) / width, 1);
|
height = qMax((height * real) / width, 1);
|
||||||
width = size;
|
width = real;
|
||||||
}
|
}
|
||||||
_userPhoto = image->pixNoCache(
|
_userPhoto = image->pixNoCache(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
options,
|
options,
|
||||||
st::callPhotoSize,
|
size,
|
||||||
st::callPhotoSize);
|
size);
|
||||||
_userPhoto.setDevicePixelRatio(cRetinaFactor());
|
_userPhoto.setDevicePixelRatio(cRetinaFactor());
|
||||||
} else {
|
} else {
|
||||||
auto filled = QImage(QSize(st::callPhotoSize, st::callPhotoSize) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
auto filled = QImage(QSize(real, real), QImage::Format_ARGB32_Premultiplied);
|
||||||
filled.setDevicePixelRatio(cRetinaFactor());
|
filled.setDevicePixelRatio(cRetinaFactor());
|
||||||
filled.fill(Qt::transparent);
|
filled.fill(Qt::transparent);
|
||||||
{
|
{
|
||||||
|
@ -168,7 +171,7 @@ void Userpic::createCache(Image *image) {
|
||||||
Ui::EmptyUserpic(
|
Ui::EmptyUserpic(
|
||||||
Data::PeerUserpicColor(_peer->id),
|
Data::PeerUserpicColor(_peer->id),
|
||||||
_peer->name
|
_peer->name
|
||||||
).paint(p, 0, 0, st::callPhotoSize, st::callPhotoSize);
|
).paint(p, 0, 0, size, size);
|
||||||
}
|
}
|
||||||
//Images::prepareRound(filled, ImageRoundRadius::Large, RectPart::TopLeft | RectPart::TopRight);
|
//Images::prepareRound(filled, ImageRoundRadius::Large, RectPart::TopLeft | RectPart::TopRight);
|
||||||
_userPhoto = Images::PixmapFast(std::move(filled));
|
_userPhoto = Images::PixmapFast(std::move(filled));
|
||||||
|
|
|
@ -47,15 +47,39 @@ void VideoBubble::setup() {
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBubble::setDragMode(DragMode mode) {
|
void VideoBubble::updateGeometry(
|
||||||
|
DragMode mode,
|
||||||
|
QRect boundingRect,
|
||||||
|
QSize sizeMin,
|
||||||
|
QSize sizeMax) {
|
||||||
|
Expects(!boundingRect.isEmpty());
|
||||||
|
Expects(sizeMax.isEmpty() || !sizeMin.isEmpty());
|
||||||
|
Expects(sizeMax.isEmpty() || sizeMin.width() <= sizeMax.width());
|
||||||
|
Expects(sizeMax.isEmpty() || sizeMin.height() <= sizeMax.height());
|
||||||
|
|
||||||
|
if (sizeMin.isEmpty()) {
|
||||||
|
sizeMin = boundingRect.size();
|
||||||
|
}
|
||||||
|
if (sizeMax.isEmpty()) {
|
||||||
|
sizeMax = sizeMin;
|
||||||
|
}
|
||||||
if (_dragMode != mode) {
|
if (_dragMode != mode) {
|
||||||
applyDragMode(mode);
|
applyDragMode(mode);
|
||||||
}
|
}
|
||||||
|
if (_boundingRect != boundingRect) {
|
||||||
|
applyBoundingRect(boundingRect);
|
||||||
|
}
|
||||||
|
if (_min != sizeMin || _max != sizeMax) {
|
||||||
|
applySizeConstraints(sizeMin, sizeMax);
|
||||||
|
}
|
||||||
|
if (_geometryDirty && !_lastFrameSize.isEmpty()) {
|
||||||
|
updateSizeToFrame(base::take(_lastFrameSize));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBubble::setBoundingRect(QRect rect) {
|
void VideoBubble::applyBoundingRect(QRect rect) {
|
||||||
_boundingRect = rect;
|
_boundingRect = rect;
|
||||||
setSizeConstraints(rect.size());
|
_geometryDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBubble::applyDragMode(DragMode mode) {
|
void VideoBubble::applyDragMode(DragMode mode) {
|
||||||
|
@ -66,23 +90,21 @@ void VideoBubble::applyDragMode(DragMode mode) {
|
||||||
}
|
}
|
||||||
_content.setAttribute(
|
_content.setAttribute(
|
||||||
Qt::WA_TransparentForMouseEvents,
|
Qt::WA_TransparentForMouseEvents,
|
||||||
(_dragMode == DragMode::None));
|
true/*(_dragMode == DragMode::None)*/);
|
||||||
}
|
if (_dragMode == DragMode::SnapToCorners) {
|
||||||
|
_corner = RectPart::BottomRight;
|
||||||
void VideoBubble::setSizeConstraints(QSize min, QSize max) {
|
} else {
|
||||||
Expects(!min.isEmpty());
|
_corner = RectPart::None;
|
||||||
Expects(max.isEmpty() || min.width() <= max.width());
|
_lastDraggableSize = _size;
|
||||||
Expects(max.isEmpty() || min.height() <= max.height());
|
|
||||||
|
|
||||||
if (max.isEmpty()) {
|
|
||||||
max = min;
|
|
||||||
}
|
}
|
||||||
applySizeConstraints(min, max);
|
_size = QSize();
|
||||||
|
_geometryDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBubble::applySizeConstraints(QSize min, QSize max) {
|
void VideoBubble::applySizeConstraints(QSize min, QSize max) {
|
||||||
_min = min;
|
_min = min;
|
||||||
_max = max;
|
_max = max;
|
||||||
|
_geometryDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBubble::paint() {
|
void VideoBubble::paint() {
|
||||||
|
@ -114,7 +136,11 @@ void VideoBubble::updateSizeToFrame(QSize frame) {
|
||||||
}
|
}
|
||||||
_lastFrameSize = frame;
|
_lastFrameSize = frame;
|
||||||
|
|
||||||
auto size = _size;
|
auto size = !_size.isEmpty()
|
||||||
|
? _size
|
||||||
|
: (_dragMode == DragMode::None || _lastDraggableSize.isEmpty())
|
||||||
|
? QSize()
|
||||||
|
: _lastDraggableSize;
|
||||||
if (size.isEmpty()) {
|
if (size.isEmpty()) {
|
||||||
size = frame.scaled((_min + _max) / 2, Qt::KeepAspectRatio);
|
size = frame.scaled((_min + _max) / 2, Qt::KeepAspectRatio);
|
||||||
} else {
|
} else {
|
||||||
|
@ -130,15 +156,34 @@ void VideoBubble::updateSizeToFrame(QSize frame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBubble::setInnerSize(QSize size) {
|
void VideoBubble::setInnerSize(QSize size) {
|
||||||
if (_size == size) {
|
if (_size == size && !_geometryDirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_geometryDirty = false;
|
||||||
_size = size;
|
_size = size;
|
||||||
_content.setGeometry(
|
_content.setGeometry(QRect([&] {
|
||||||
_boundingRect.x() + (_boundingRect.width() - size.width()) / 2,
|
switch (_corner) {
|
||||||
_boundingRect.y() + (_boundingRect.height() - size.height()) / 2,
|
case RectPart::None:
|
||||||
size.width(),
|
return _boundingRect.topLeft() + QPoint(
|
||||||
size.height());
|
(_boundingRect.width() - size.width()) / 2,
|
||||||
|
(_boundingRect.height() - size.height()) / 2);
|
||||||
|
case RectPart::TopLeft:
|
||||||
|
return _boundingRect.topLeft();
|
||||||
|
case RectPart::TopRight:
|
||||||
|
return QPoint(
|
||||||
|
_boundingRect.x() + _boundingRect.width() - size.width(),
|
||||||
|
_boundingRect.y());
|
||||||
|
case RectPart::BottomRight:
|
||||||
|
return QPoint(
|
||||||
|
_boundingRect.x() + _boundingRect.width() - size.width(),
|
||||||
|
_boundingRect.y() + _boundingRect.height() - size.height());
|
||||||
|
case RectPart::BottomLeft:
|
||||||
|
return QPoint(
|
||||||
|
_boundingRect.x(),
|
||||||
|
_boundingRect.y() + _boundingRect.height() - size.height());
|
||||||
|
}
|
||||||
|
Unexpected("Corner value in VideoBubble::setInnerSize.");
|
||||||
|
}(), size));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBubble::updateVisibility() {
|
void VideoBubble::updateVisibility() {
|
||||||
|
|
|
@ -26,9 +26,11 @@ public:
|
||||||
None,
|
None,
|
||||||
SnapToCorners,
|
SnapToCorners,
|
||||||
};
|
};
|
||||||
void setDragMode(DragMode mode);
|
void updateGeometry(
|
||||||
void setBoundingRect(QRect rect);
|
DragMode mode,
|
||||||
void setSizeConstraints(QSize min, QSize max = QSize());
|
QRect boundingRect,
|
||||||
|
QSize sizeMin = QSize(),
|
||||||
|
QSize sizeMax = QSize());
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
return _content.lifetime();
|
return _content.lifetime();
|
||||||
|
@ -39,6 +41,7 @@ private:
|
||||||
void paint();
|
void paint();
|
||||||
void setState(webrtc::VideoState state);
|
void setState(webrtc::VideoState state);
|
||||||
void applyDragMode(DragMode mode);
|
void applyDragMode(DragMode mode);
|
||||||
|
void applyBoundingRect(QRect rect);
|
||||||
void applySizeConstraints(QSize min, QSize max);
|
void applySizeConstraints(QSize min, QSize max);
|
||||||
void updateSizeToFrame(QSize frame);
|
void updateSizeToFrame(QSize frame);
|
||||||
void updateVisibility();
|
void updateVisibility();
|
||||||
|
@ -48,10 +51,12 @@ private:
|
||||||
const not_null<webrtc::VideoTrack*> _track;
|
const not_null<webrtc::VideoTrack*> _track;
|
||||||
webrtc::VideoState _state = webrtc::VideoState();
|
webrtc::VideoState _state = webrtc::VideoState();
|
||||||
QImage _pausedFrame;
|
QImage _pausedFrame;
|
||||||
QSize _min, _max, _size, _lastFrameSize;
|
QSize _min, _max, _size, _lastDraggableSize, _lastFrameSize;
|
||||||
QRect _boundingRect;
|
QRect _boundingRect;
|
||||||
DragMode _dragMode = DragMode::None;
|
DragMode _dragMode = DragMode::None;
|
||||||
|
RectPart _corner = RectPart::None;
|
||||||
bool _dragging = false;
|
bool _dragging = false;
|
||||||
|
bool _geometryDirty = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 697f2851b0625ae784e405e7fc596d1629e8668a
|
Subproject commit c9235ec9c25565516da04abf083e9c0500de58bc
|
Loading…
Add table
Reference in a new issue