Improve floating player show / hide animations.

Also replace Window::Corner with a generic RectPart enumeration.
This commit is contained in:
John Preston 2017-05-24 15:07:58 +03:00
parent 03aeaadd5b
commit c91e29d15d
7 changed files with 155 additions and 78 deletions

View File

@ -40,7 +40,7 @@ constexpr auto kAutoLockTimeoutLateMs = TimeMs(3000);
AuthSessionData::Variables::Variables()
: selectorTab(ChatHelpers::SelectorTab::Emoji)
, floatPlayerColumn(Window::Column::Second)
, floatPlayerCorner(Window::Corner::TopRight) {
, floatPlayerCorner(RectPart::TopRight) {
}
QByteArray AuthSessionData::serialize() const {
@ -90,7 +90,7 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
qint32 tabbedSelectorSectionEnabled = 1;
qint32 tabbedSelectorSectionTooltipShown = 0;
qint32 floatPlayerColumn = static_cast<qint32>(Window::Column::Second);
qint32 floatPlayerCorner = static_cast<qint32>(Window::Corner::TopRight);
qint32 floatPlayerCorner = static_cast<qint32>(RectPart::TopRight);
QMap<QString, QString> soundOverrides;
stream >> selectorTab;
stream >> lastSeenWarningSeen;
@ -135,12 +135,12 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
case Window::Column::Second:
case Window::Column::Third: _variables.floatPlayerColumn = uncheckedColumn; break;
}
auto uncheckedCorner = static_cast<Window::Corner>(floatPlayerCorner);
auto uncheckedCorner = static_cast<RectPart>(floatPlayerCorner);
switch (uncheckedCorner) {
case Window::Corner::TopLeft:
case Window::Corner::TopRight:
case Window::Corner::BottomLeft:
case Window::Corner::BottomRight: _variables.floatPlayerCorner = uncheckedCorner; break;
case RectPart::TopLeft:
case RectPart::TopRight:
case RectPart::BottomLeft:
case RectPart::BottomRight: _variables.floatPlayerCorner = uncheckedCorner; break;
}
}

View File

@ -31,7 +31,6 @@ namespace Notifications {
class System;
} // namespace Notifications
enum class Column;
enum class Corner;
} // namespace Window
namespace Calls {
@ -108,10 +107,10 @@ public:
Window::Column floatPlayerColumn() const {
return _variables.floatPlayerColumn;
}
void setFloatPlayerCorner(Window::Corner corner) {
void setFloatPlayerCorner(RectPart corner) {
_variables.floatPlayerCorner = corner;
}
Window::Corner floatPlayerCorner() const {
RectPart floatPlayerCorner() const {
return _variables.floatPlayerCorner;
}
@ -125,7 +124,7 @@ private:
int tabbedSelectorSectionTooltipShown = 0;
QMap<QString, QString> soundOverrides;
Window::Column floatPlayerColumn;
Window::Corner floatPlayerCorner;
RectPart floatPlayerCorner;
};
base::Variable<bool> _contactsLoaded = { false };

View File

@ -102,8 +102,9 @@ StackItemSection::~StackItemSection() {
template <typename ToggleCallback, typename DraggedCallback>
MainWidget::Float::Float(QWidget *parent, HistoryItem *item, ToggleCallback toggle, DraggedCallback dragged)
: column(Window::Column::Second)
, corner(Window::Corner::TopRight)
: animationSide(RectPart::Right)
, column(Window::Column::Second)
, corner(RectPart::TopRight)
, widget(parent, item, [this, toggle = std::move(toggle)](bool visible) {
toggle(this, visible);
}, [this, dragged = std::move(dragged)](bool closed) {
@ -252,10 +253,10 @@ void MainWidget::checkCurrentFloatPlayer() {
if (auto media = item->getMedia()) {
if (auto document = media->getDocument()) {
if (document->isRoundVideo()) {
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](Float *instance, bool visible) {
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](gsl::not_null<Float*> instance, bool visible) {
instance->hiddenByWidget = !visible;
toggleFloatPlayer(instance);
}, [this](Float *instance, bool closed) {
}, [this](gsl::not_null<Float*> instance, bool closed) {
finishFloatPlayerDrag(instance, closed);
}));
currentFloatPlayer()->column = AuthSession::Current().data().floatPlayerColumn();
@ -268,11 +269,15 @@ void MainWidget::checkCurrentFloatPlayer() {
}
}
void MainWidget::toggleFloatPlayer(Float *instance) {
void MainWidget::toggleFloatPlayer(gsl::not_null<Float*> instance) {
auto visible = !instance->hiddenByHistory && !instance->hiddenByWidget && !instance->widget->detached();
if (instance->visible != visible) {
instance->widget->resetMouseState();
instance->visible = visible;
if (!instance->visibleAnimation.animating() && !instance->hiddenByDrag) {
auto finalRect = QRect(getFloatPlayerPosition(instance), instance->widget->size());
instance->animationSide = getFloatPlayerSide(finalRect.center());
}
instance->visibleAnimation.start([this, instance] {
updateFloatPlayerPosition(instance);
}, visible ? 0. : 1., visible ? 1. : 0., st::slideDuration, visible ? anim::easeOutCirc : anim::linear);
@ -295,7 +300,7 @@ void MainWidget::checkFloatPlayerVisibility() {
updateFloatPlayerPosition(instance);
}
void MainWidget::updateFloatPlayerPosition(Float *instance) {
void MainWidget::updateFloatPlayerPosition(gsl::not_null<Float*> instance) {
auto visible = instance->visibleAnimation.current(instance->visible ? 1. : 0.);
if (visible == 0. && !instance->visible) {
instance->widget->hide();
@ -307,27 +312,25 @@ void MainWidget::updateFloatPlayerPosition(Float *instance) {
return;
}
instance->widget->setOpacity(visible * visible);
if (instance->widget->isHidden()) {
instance->widget->show();
}
auto column = instance->column;
auto section = getFloatPlayerSection(&column);
auto rect = section->rectForFloatPlayer(column, instance->column);
auto position = rect.topLeft();
if (Window::IsBottomCorner(instance->corner)) {
position.setY(position.y() + rect.height() - instance->widget->height());
}
if (Window::IsRightCorner(instance->corner)) {
position.setX(position.x() + rect.width() - instance->widget->width());
}
position = mapFromGlobal(position);
auto hiddenTop = Window::IsTopCorner(instance->corner) ? -instance->widget->height() : height();
position.setY(anim::interpolate(hiddenTop, position.y(), visible));
if (!instance->widget->dragged()) {
if (instance->widget->isHidden()) {
instance->widget->show();
}
auto dragged = instance->draggedAnimation.current(1.);
auto position = QPoint();
if (instance->hiddenByDrag) {
instance->widget->setOpacity(instance->widget->countOpacityByParent());
position = getFloatPlayerHiddenPosition(instance->dragFrom, instance->widget->size(), instance->animationSide);
} else {
instance->widget->setOpacity(visible * visible);
position = getFloatPlayerPosition(instance);
if (visible < 1.) {
auto hiddenPosition = getFloatPlayerHiddenPosition(position, instance->widget->size(), instance->animationSide);
position.setX(anim::interpolate(hiddenPosition.x(), position.x(), visible));
position.setY(anim::interpolate(hiddenPosition.y(), position.y(), visible));
}
}
if (dragged < 1.) {
position.setX(anim::interpolate(instance->dragFrom.x(), position.x(), dragged));
position.setY(anim::interpolate(instance->dragFrom.y(), position.y(), dragged));
@ -336,7 +339,46 @@ void MainWidget::updateFloatPlayerPosition(Float *instance) {
}
}
void MainWidget::removeFloatPlayer(Float *instance) {
QPoint MainWidget::getFloatPlayerHiddenPosition(QPoint position, QSize size, RectPart side) const {
switch (side) {
case RectPart::Left: return QPoint(-size.width(), position.y());
case RectPart::Top: return QPoint(position.x(), -size.height());
case RectPart::Right: return QPoint(width(), position.y());
case RectPart::Bottom: return QPoint(position.x(), height());
}
Unexpected("Bad side in MainWidget::getFloatPlayerHiddenPosition().");
}
QPoint MainWidget::getFloatPlayerPosition(gsl::not_null<Float*> instance) const {
auto column = instance->column;
auto section = getFloatPlayerSection(&column);
auto rect = section->rectForFloatPlayer(column, instance->column);
auto position = rect.topLeft();
if (IsBottomCorner(instance->corner)) {
position.setY(position.y() + rect.height() - instance->widget->height());
}
if (IsRightCorner(instance->corner)) {
position.setX(position.x() + rect.width() - instance->widget->width());
}
return mapFromGlobal(position);
}
RectPart MainWidget::getFloatPlayerSide(QPoint center) const {
auto left = qAbs(center.x());
auto right = qAbs(width() - center.x());
auto top = qAbs(center.y());
auto bottom = qAbs(height() - center.y());
if (left < right && left < top && left < bottom) {
return RectPart::Left;
} else if (right < top && right < bottom) {
return RectPart::Right;
} else if (top < bottom) {
return RectPart::Top;
}
return RectPart::Bottom;
}
void MainWidget::removeFloatPlayer(gsl::not_null<Float*> instance) {
auto widget = std::move(instance->widget);
auto i = std::find_if(_playerFloats.begin(), _playerFloats.end(), [instance](auto &item) {
return (item.get() == instance);
@ -351,7 +393,7 @@ void MainWidget::removeFloatPlayer(Float *instance) {
widget.destroy();
}
Window::AbstractSectionWidget *MainWidget::getFloatPlayerSection(gsl::not_null<Window::Column*> column) {
Window::AbstractSectionWidget *MainWidget::getFloatPlayerSection(gsl::not_null<Window::Column*> column) const {
if (!Adaptive::Normal()) {
*column = Adaptive::OneColumn() ? Window::Column::First : Window::Column::Second;
if (Adaptive::OneColumn() && selectingPeer()) {
@ -389,17 +431,17 @@ void MainWidget::updateFloatPlayerColumnCorner(QPoint center) {
auto right = rect.x() + rect.width() - (size.width() / 2);
auto top = rect.y() + (size.height() / 2);
auto bottom = rect.y() + rect.height() - (size.height() / 2);
auto checkCorner = [this, playerColumn, &min, &column, &corner](int distance, Window::Corner checked) {
auto checkCorner = [this, playerColumn, &min, &column, &corner](int distance, RectPart checked) {
if (min > distance) {
min = distance;
column = playerColumn;
corner = checked;
}
};
checkCorner((QPoint(left, top) - center).manhattanLength(), Window::Corner::TopLeft);
checkCorner((QPoint(right, top) - center).manhattanLength(), Window::Corner::TopRight);
checkCorner((QPoint(left, bottom) - center).manhattanLength(), Window::Corner::BottomLeft);
checkCorner((QPoint(right, bottom) - center).manhattanLength(), Window::Corner::BottomRight);
checkCorner((QPoint(left, top) - center).manhattanLength(), RectPart::TopLeft);
checkCorner((QPoint(right, top) - center).manhattanLength(), RectPart::TopRight);
checkCorner((QPoint(left, bottom) - center).manhattanLength(), RectPart::BottomLeft);
checkCorner((QPoint(right, bottom) - center).manhattanLength(), RectPart::BottomRight);
};
if (!Adaptive::Normal()) {
@ -436,10 +478,14 @@ void MainWidget::updateFloatPlayerColumnCorner(QPoint center) {
}
}
void MainWidget::finishFloatPlayerDrag(Float *instance, bool closed) {
void MainWidget::finishFloatPlayerDrag(gsl::not_null<Float*> instance, bool closed) {
instance->dragFrom = instance->widget->pos();
updateFloatPlayerColumnCorner(instance->widget->geometry().center());
auto center = instance->widget->geometry().center();
if (closed) {
instance->hiddenByDrag = true;
instance->animationSide = getFloatPlayerSide(center);
}
updateFloatPlayerColumnCorner(center);
instance->column = AuthSession::Current().data().floatPlayerColumn();
instance->corner = AuthSession::Current().data().floatPlayerCorner();
@ -1001,7 +1047,7 @@ void MainWidget::inlineSwitchLayer(const QString &botAndQuery) {
hiderLayer(object_ptr<HistoryHider>(this, botAndQuery));
}
bool MainWidget::selectingPeer(bool withConfirm) {
bool MainWidget::selectingPeer(bool withConfirm) const {
return _hider ? (withConfirm ? _hider->withConfirm() : true) : false;
}

View File

@ -58,7 +58,6 @@ class SectionWidget;
class AbstractSectionWidget;
struct SectionSlideParams;
enum class Column;
enum class Corner;
} // namespace Window
namespace Calls {
@ -256,7 +255,7 @@ public:
void onShareContact(const PeerId &peer, UserData *contact);
bool onSendPaths(const PeerId &peer);
void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data);
bool selectingPeer(bool withConfirm = false);
bool selectingPeer(bool withConfirm = false) const;
bool selectingPeerForInlineSwitch();
void offerPeer(PeerId peer);
void dialogsActivate();
@ -471,11 +470,13 @@ private:
bool hiddenByWidget = false;
bool hiddenByHistory = false;
bool visible = false;
RectPart animationSide;
Animation visibleAnimation;
Window::Column column;
Window::Corner corner;
RectPart corner;
QPoint dragFrom;
Animation draggedAnimation;
bool hiddenByDrag = false;
object_ptr<Media::Player::Float> widget;
};
@ -579,16 +580,19 @@ private:
void clearCachedBackground();
void checkCurrentFloatPlayer();
void toggleFloatPlayer(Float *instance);
void toggleFloatPlayer(gsl::not_null<Float*> instance);
void checkFloatPlayerVisibility();
void updateFloatPlayerPosition(Float *instance);
void removeFloatPlayer(Float *instance);
void updateFloatPlayerPosition(gsl::not_null<Float*> instance);
void removeFloatPlayer(gsl::not_null<Float*> instance);
Float *currentFloatPlayer() const {
return _playerFloats.empty() ? nullptr : _playerFloats.back().get();
}
Window::AbstractSectionWidget *getFloatPlayerSection(gsl::not_null<Window::Column*> column);
void finishFloatPlayerDrag(Float *instance, bool closed);
Window::AbstractSectionWidget *getFloatPlayerSection(gsl::not_null<Window::Column*> column) const;
void finishFloatPlayerDrag(gsl::not_null<Float*> instance, bool closed);
void updateFloatPlayerColumnCorner(QPoint center);
QPoint getFloatPlayerPosition(gsl::not_null<Float*> instance) const;
QPoint getFloatPlayerHiddenPosition(QPoint position, QSize size, RectPart side) const;
RectPart getFloatPlayerSide(QPoint center) const;
bool ptsUpdated(int32 pts, int32 ptsCount);
bool ptsUpdated(int32 pts, int32 ptsCount, const MTPUpdates &updates);

View File

@ -40,6 +40,9 @@ public:
update();
}
}
float64 countOpacityByParent() const {
return outRatio();
}
void detach();
bool detached() const {
return !_item;

View File

@ -27,6 +27,54 @@ QString GetOverride(const QString &familyName);
} // namespace
enum class RectPart {
None = 0,
TopLeft = (1 << 0),
Top = (1 << 1),
TopRight = (1 << 2),
Left = (1 << 3),
Center = (1 << 4),
Right = (1 << 5),
BottomLeft = (1 << 6),
Bottom = (1 << 7),
BottomRight = (1 << 8),
FullTop = TopLeft | Top | TopRight,
NoTopBottom = Left | Center | Right,
FullBottom = BottomLeft | Bottom | BottomRight,
NoTop = NoTopBottom | FullBottom,
NoBottom = FullTop | NoTopBottom,
FullLeft = TopLeft | Left | BottomLeft,
NoLeftRight = Top | Center | Bottom,
FullRight = TopRight | Right | BottomRight,
NoLeft = NoLeftRight | FullRight,
NoRight = FullLeft | NoLeftRight,
CornersMask = TopLeft | TopRight | BottomLeft | BottomRight,
SidesMask = Top | Bottom | Left | Right,
All = FullTop | NoTop,
};
Q_DECLARE_FLAGS(RectParts, RectPart);
inline bool IsTopCorner(RectPart corner) {
return (corner == RectPart::TopLeft) || (corner == RectPart::TopRight);
}
inline bool IsBottomCorner(RectPart corner) {
return (corner == RectPart::BottomLeft) || (corner == RectPart::BottomRight);
}
inline bool IsLeftCorner(RectPart corner) {
return (corner == RectPart::TopLeft) || (corner == RectPart::BottomLeft);
}
inline bool IsRightCorner(RectPart corner) {
return (corner == RectPart::TopRight) || (corner == RectPart::BottomRight);
}
class Painter : public QPainter {
public:
explicit Painter(QPaintDevice *device) : QPainter(device) {

View File

@ -33,29 +33,6 @@ enum class Column {
Third,
};
enum class Corner {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
};
inline bool IsTopCorner(Corner corner) {
return (corner == Corner::TopLeft) || (corner == Corner::TopRight);
}
inline bool IsBottomCorner(Corner corner) {
return !IsTopCorner(corner);
}
inline bool IsLeftCorner(Corner corner) {
return (corner == Corner::TopLeft) || (corner == Corner::BottomLeft);
}
inline bool IsRightCorner(Corner corner) {
return !IsLeftCorner(corner);
}
class AbstractSectionWidget : public TWidget, protected base::Subscriber {
public:
AbstractSectionWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller) : TWidget(parent), _controller(controller) {