Improve design for all controls states.

This commit is contained in:
John Preston 2020-08-07 17:28:41 +04:00
parent d4b8fa70a7
commit 4971e281fa
7 changed files with 274 additions and 98 deletions

View file

@ -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;

View file

@ -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();
} }

View file

@ -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;

View file

@ -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));

View file

@ -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() {

View file

@ -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