mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-16 12:13:02 +00:00
Improve floating player show / hide animations.
Also replace Window::Corner with a generic RectPart enumeration.
This commit is contained in:
parent
03aeaadd5b
commit
c91e29d15d
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -40,6 +40,9 @@ public:
|
||||
update();
|
||||
}
|
||||
}
|
||||
float64 countOpacityByParent() const {
|
||||
return outRatio();
|
||||
}
|
||||
void detach();
|
||||
bool detached() const {
|
||||
return !_item;
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user