mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Extract float player controller.
This commit is contained in:
parent
b420f5b41b
commit
466444e17d
4 changed files with 460 additions and 355 deletions
|
@ -214,28 +214,6 @@ StackItemSection::StackItemSection(
|
||||||
, _memento(std::move(memento)) {
|
, _memento(std::move(memento)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ToggleCallback, typename DraggedCallback>
|
|
||||||
MainWidget::Float::Float(
|
|
||||||
QWidget *parent,
|
|
||||||
not_null<Window::Controller*> controller,
|
|
||||||
not_null<HistoryItem*> item,
|
|
||||||
ToggleCallback toggle,
|
|
||||||
DraggedCallback dragged)
|
|
||||||
: animationSide(RectPart::Right)
|
|
||||||
, column(Window::Column::Second)
|
|
||||||
, corner(RectPart::TopRight)
|
|
||||||
, widget(
|
|
||||||
parent,
|
|
||||||
controller,
|
|
||||||
item,
|
|
||||||
[this, toggle = std::move(toggle)](bool visible) {
|
|
||||||
toggle(this, visible);
|
|
||||||
},
|
|
||||||
[this, dragged = std::move(dragged)](bool closed) {
|
|
||||||
dragged(this, closed);
|
|
||||||
}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
MainWidget::MainWidget(
|
MainWidget::MainWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::Controller*> controller)
|
not_null<Window::Controller*> controller)
|
||||||
|
@ -250,7 +228,8 @@ MainWidget::MainWidget(
|
||||||
this,
|
this,
|
||||||
_controller,
|
_controller,
|
||||||
Media::Player::Panel::Layout::OnlyPlaylist)
|
Media::Player::Panel::Layout::OnlyPlaylist)
|
||||||
, _playerPanel(this, _controller, Media::Player::Panel::Layout::Full) {
|
, _playerPanel(this, _controller, Media::Player::Panel::Layout::Full)
|
||||||
|
, _playerFloats(floatPlayerDelegate()) {
|
||||||
Messenger::Instance().mtp()->setUpdatesHandler(rpcDone(&MainWidget::updateReceived));
|
Messenger::Instance().mtp()->setUpdatesHandler(rpcDone(&MainWidget::updateReceived));
|
||||||
Messenger::Instance().mtp()->setGlobalFailHandler(rpcFail(&MainWidget::updateFail));
|
Messenger::Instance().mtp()->setGlobalFailHandler(rpcFail(&MainWidget::updateFail));
|
||||||
|
|
||||||
|
@ -294,9 +273,6 @@ MainWidget::MainWidget(
|
||||||
) | rpl::start_with_next(
|
) | rpl::start_with_next(
|
||||||
[this] { updateControlsGeometry(); },
|
[this] { updateControlsGeometry(); },
|
||||||
lifetime());
|
lifetime());
|
||||||
subscribe(_controller->floatPlayerAreaUpdated(), [this] {
|
|
||||||
checkFloatPlayerVisibility();
|
|
||||||
});
|
|
||||||
|
|
||||||
// MSVC BUG + REGRESSION rpl::mappers::tuple :(
|
// MSVC BUG + REGRESSION rpl::mappers::tuple :(
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
@ -358,11 +334,6 @@ MainWidget::MainWidget(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
subscribe(Media::Player::instance()->trackChangedNotifier(), [this](AudioMsgId::Type type) {
|
|
||||||
if (type == AudioMsgId::Type::Voice) {
|
|
||||||
checkCurrentFloatPlayer();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
subscribe(Adaptive::Changed(), [this]() { handleAdaptiveLayoutUpdate(); });
|
subscribe(Adaptive::Changed(), [this]() { handleAdaptiveLayoutUpdate(); });
|
||||||
|
|
||||||
|
@ -388,170 +359,20 @@ void MainWidget::setupConnectingWidget() {
|
||||||
Window::AdaptiveIsOneColumn() | rpl::map(!_1));
|
Window::AdaptiveIsOneColumn() | rpl::map(!_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::checkCurrentFloatPlayer() {
|
not_null<Media::Player::FloatDelegate*> MainWidget::floatPlayerDelegate() {
|
||||||
const auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
|
return static_cast<Media::Player::FloatDelegate*>(this);
|
||||||
const auto fullId = state.contextId();
|
|
||||||
const auto last = currentFloatPlayer();
|
|
||||||
if (last
|
|
||||||
&& !last->widget->detached()
|
|
||||||
&& last->widget->item()->fullId() == fullId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (last) {
|
|
||||||
last->widget->detach();
|
|
||||||
}
|
|
||||||
if (const auto item = App::histItemById(fullId)) {
|
|
||||||
if (const auto media = item->media()) {
|
|
||||||
if (const auto document = media->document()) {
|
|
||||||
if (document->isVideoMessage()) {
|
|
||||||
createFloatPlayer(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::createFloatPlayer(not_null<HistoryItem*> item) {
|
not_null<Ui::RpWidget*> MainWidget::floatPlayerWidget() {
|
||||||
_playerFloats.push_back(std::make_unique<Float>(
|
return this;
|
||||||
this,
|
|
||||||
_controller,
|
|
||||||
item,
|
|
||||||
[this](not_null<Float*> instance, bool visible) {
|
|
||||||
instance->hiddenByWidget = !visible;
|
|
||||||
toggleFloatPlayer(instance);
|
|
||||||
},
|
|
||||||
[this](not_null<Float*> instance, bool closed) {
|
|
||||||
finishFloatPlayerDrag(instance, closed);
|
|
||||||
}));
|
|
||||||
currentFloatPlayer()->column = Auth().settings().floatPlayerColumn();
|
|
||||||
currentFloatPlayer()->corner = Auth().settings().floatPlayerCorner();
|
|
||||||
checkFloatPlayerVisibility();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::toggleFloatPlayer(not_null<Float*> instance) {
|
not_null<Window::Controller*> MainWidget::floatPlayerController() {
|
||||||
auto visible = !instance->hiddenByHistory && !instance->hiddenByWidget && instance->widget->isReady();
|
return _controller;
|
||||||
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);
|
|
||||||
updateFloatPlayerPosition(instance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::checkFloatPlayerVisibility() {
|
not_null<Window::AbstractSectionWidget*> MainWidget::floatPlayerGetSection(
|
||||||
auto instance = currentFloatPlayer();
|
Window::Column column) {
|
||||||
if (!instance) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto amVisible = false;
|
|
||||||
if (auto item = instance->widget->item()) {
|
|
||||||
Auth().data().queryItemVisibility().notify({ item, &amVisible }, true);
|
|
||||||
}
|
|
||||||
instance->hiddenByHistory = amVisible;
|
|
||||||
toggleFloatPlayer(instance);
|
|
||||||
updateFloatPlayerPosition(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWidget::updateFloatPlayerPosition(not_null<Float*> instance) {
|
|
||||||
auto visible = instance->visibleAnimation.current(instance->visible ? 1. : 0.);
|
|
||||||
if (visible == 0. && !instance->visible) {
|
|
||||||
instance->widget->hide();
|
|
||||||
if (instance->widget->detached()) {
|
|
||||||
InvokeQueued(instance->widget, [this, instance] {
|
|
||||||
removeFloatPlayer(instance);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
instance->widget->move(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(not_null<Float*> instance) const {
|
|
||||||
auto section = getFloatPlayerSection(instance->column);
|
|
||||||
auto rect = section->rectForFloatPlayer();
|
|
||||||
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(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);
|
|
||||||
});
|
|
||||||
Assert(i != _playerFloats.end());
|
|
||||||
_playerFloats.erase(i);
|
|
||||||
|
|
||||||
// ~QWidget() can call HistoryInner::enterEvent() which can
|
|
||||||
// lead to repaintHistoryItem() and we'll have an instance
|
|
||||||
// in _playerFloats with destroyed widget. So we destroy the
|
|
||||||
// instance first and only after that destroy the widget.
|
|
||||||
widget.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
Window::AbstractSectionWidget *MainWidget::getFloatPlayerSection(Window::Column column) const {
|
|
||||||
if (Adaptive::ThreeColumn()) {
|
if (Adaptive::ThreeColumn()) {
|
||||||
if (column == Window::Column::First) {
|
if (column == Window::Column::First) {
|
||||||
return _dialogs;
|
return _dialogs;
|
||||||
|
@ -581,100 +402,52 @@ Window::AbstractSectionWidget *MainWidget::getFloatPlayerSection(Window::Column
|
||||||
return _dialogs;
|
return _dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::updateFloatPlayerColumnCorner(QPoint center) {
|
void MainWidget::floatPlayerEnumerateSections(Fn<void(
|
||||||
Expects(!_playerFloats.empty());
|
not_null<Window::AbstractSectionWidget*> widget,
|
||||||
auto size = _playerFloats.back()->widget->size();
|
Window::Column widgetColumn)> callback) {
|
||||||
auto min = INT_MAX;
|
|
||||||
auto column = Auth().settings().floatPlayerColumn();
|
|
||||||
auto corner = Auth().settings().floatPlayerCorner();
|
|
||||||
auto checkSection = [this, center, size, &min, &column, &corner](
|
|
||||||
Window::AbstractSectionWidget *widget,
|
|
||||||
Window::Column widgetColumn) {
|
|
||||||
auto rect = mapFromGlobal(widget->rectForFloatPlayer());
|
|
||||||
auto left = rect.x() + (size.width() / 2);
|
|
||||||
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 = [&](QPoint point, RectPart checked) {
|
|
||||||
auto distance = (point - center).manhattanLength();
|
|
||||||
if (min > distance) {
|
|
||||||
min = distance;
|
|
||||||
column = widgetColumn;
|
|
||||||
corner = checked;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
checkCorner({ left, top }, RectPart::TopLeft);
|
|
||||||
checkCorner({ right, top }, RectPart::TopRight);
|
|
||||||
checkCorner({ left, bottom }, RectPart::BottomLeft);
|
|
||||||
checkCorner({ right, bottom }, RectPart::BottomRight);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Adaptive::ThreeColumn()) {
|
if (Adaptive::ThreeColumn()) {
|
||||||
checkSection(_dialogs, Window::Column::First);
|
callback(_dialogs, Window::Column::First);
|
||||||
if (_mainSection) {
|
if (_mainSection) {
|
||||||
checkSection(_mainSection, Window::Column::Second);
|
callback(_mainSection, Window::Column::Second);
|
||||||
} else {
|
} else {
|
||||||
checkSection(_history, Window::Column::Second);
|
callback(_history, Window::Column::Second);
|
||||||
}
|
}
|
||||||
if (_thirdSection) {
|
if (_thirdSection) {
|
||||||
checkSection(_thirdSection, Window::Column::Third);
|
callback(_thirdSection, Window::Column::Third);
|
||||||
}
|
}
|
||||||
} else if (Adaptive::Normal()) {
|
} else if (Adaptive::Normal()) {
|
||||||
checkSection(_dialogs, Window::Column::First);
|
callback(_dialogs, Window::Column::First);
|
||||||
if (_mainSection) {
|
if (_mainSection) {
|
||||||
checkSection(_mainSection, Window::Column::Second);
|
callback(_mainSection, Window::Column::Second);
|
||||||
} else {
|
} else {
|
||||||
checkSection(_history, Window::Column::Second);
|
callback(_history, Window::Column::Second);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Adaptive::OneColumn() && selectingPeer()) {
|
if (Adaptive::OneColumn() && selectingPeer()) {
|
||||||
checkSection(_dialogs, Window::Column::First);
|
callback(_dialogs, Window::Column::First);
|
||||||
} else if (_mainSection) {
|
} else if (_mainSection) {
|
||||||
checkSection(_mainSection, Window::Column::Second);
|
callback(_mainSection, Window::Column::Second);
|
||||||
} else if (!Adaptive::OneColumn() || _history->peer()) {
|
} else if (!Adaptive::OneColumn() || _history->peer()) {
|
||||||
checkSection(_history, Window::Column::Second);
|
callback(_history, Window::Column::Second);
|
||||||
} else {
|
} else {
|
||||||
checkSection(_dialogs, Window::Column::First);
|
callback(_dialogs, Window::Column::First);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Auth().settings().floatPlayerColumn() != column) {
|
|
||||||
Auth().settings().setFloatPlayerColumn(column);
|
|
||||||
Auth().saveSettingsDelayed();
|
|
||||||
}
|
|
||||||
if (Auth().settings().floatPlayerCorner() != corner) {
|
|
||||||
Auth().settings().setFloatPlayerCorner(corner);
|
|
||||||
Auth().saveSettingsDelayed();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::finishFloatPlayerDrag(not_null<Float*> instance, bool closed) {
|
void MainWidget::floatPlayerCloseHook(FullMsgId itemId) {
|
||||||
instance->dragFrom = instance->widget->pos();
|
if (_player) {
|
||||||
auto center = instance->widget->geometry().center();
|
const auto voiceData = Media::Player::instance()->current(
|
||||||
if (closed) {
|
AudioMsgId::Type::Voice);
|
||||||
instance->hiddenByDrag = true;
|
if (voiceData.contextId() == itemId) {
|
||||||
instance->animationSide = getFloatPlayerSide(center);
|
|
||||||
}
|
|
||||||
updateFloatPlayerColumnCorner(center);
|
|
||||||
instance->column = Auth().settings().floatPlayerColumn();
|
|
||||||
instance->corner = Auth().settings().floatPlayerCorner();
|
|
||||||
|
|
||||||
instance->draggedAnimation.finish();
|
|
||||||
instance->draggedAnimation.start([this, instance] { updateFloatPlayerPosition(instance); }, 0., 1., st::slideDuration, anim::sineInOut);
|
|
||||||
updateFloatPlayerPosition(instance);
|
|
||||||
|
|
||||||
if (closed) {
|
|
||||||
if (auto item = instance->widget->item()) {
|
|
||||||
auto voiceData = Media::Player::instance()->current(AudioMsgId::Type::Voice);
|
|
||||||
if (_player && voiceData.contextId() == item->fullId()) {
|
|
||||||
_player->entity()->stopAndClose();
|
_player->entity()->stopAndClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instance->widget->detach();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWidget::setForwardDraft(PeerId peerId, MessageIdsList &&items) {
|
bool MainWidget::setForwardDraft(PeerId peerId, MessageIdsList &&items) {
|
||||||
Expects(peerId != 0);
|
Expects(peerId != 0);
|
||||||
|
|
||||||
const auto peer = App::peer(peerId);
|
const auto peer = App::peer(peerId);
|
||||||
const auto error = GetErrorTextForForward(
|
const auto error = GetErrorTextForForward(
|
||||||
peer,
|
peer,
|
||||||
|
@ -876,7 +649,7 @@ void MainWidget::noHider(HistoryHider *destroyed) {
|
||||||
} else {
|
} else {
|
||||||
_history->showAnimated(Window::SlideDirection::FromRight, animationParams);
|
_history->showAnimated(Window::SlideDirection::FromRight, animationParams);
|
||||||
}
|
}
|
||||||
checkFloatPlayerVisibility();
|
_playerFloats.checkVisibility();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_forwardConfirm) {
|
if (_forwardConfirm) {
|
||||||
|
@ -915,7 +688,7 @@ void MainWidget::hiderLayer(object_ptr<HistoryHider> h) {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
_dialogs->activate();
|
_dialogs->activate();
|
||||||
}
|
}
|
||||||
checkFloatPlayerVisibility();
|
_playerFloats.checkVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::showForwardLayer(MessageIdsList &&items) {
|
void MainWidget::showForwardLayer(MessageIdsList &&items) {
|
||||||
|
@ -2047,7 +1820,7 @@ void MainWidget::ui_showPeerHistory(
|
||||||
_dialogs->update();
|
_dialogs->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkFloatPlayerVisibility();
|
_playerFloats.checkVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerData *MainWidget::ui_getPeerForMouseAction() {
|
PeerData *MainWidget::ui_getPeerForMouseAction() {
|
||||||
|
@ -2132,16 +1905,10 @@ Window::SectionSlideParams MainWidget::prepareThirdSectionAnimation(Window::Sect
|
||||||
if (!_thirdSection->hasTopBarShadow()) {
|
if (!_thirdSection->hasTopBarShadow()) {
|
||||||
result.withTopBarShadow = false;
|
result.withTopBarShadow = false;
|
||||||
}
|
}
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.hideAll();
|
||||||
instance->widget->hide();
|
|
||||||
}
|
|
||||||
auto sectionTop = getThirdSectionTop();
|
auto sectionTop = getThirdSectionTop();
|
||||||
result.oldContentCache = _thirdSection->grabForShowAnimation(result);
|
result.oldContentCache = _thirdSection->grabForShowAnimation(result);
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.showVisible();
|
||||||
if (instance->visible) {
|
|
||||||
instance->widget->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2159,9 +1926,7 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(
|
||||||
result.withTopBarShadow = false;
|
result.withTopBarShadow = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.hideAll();
|
||||||
instance->widget->hide();
|
|
||||||
}
|
|
||||||
if (_player) {
|
if (_player) {
|
||||||
_player->hideShadow();
|
_player->hideShadow();
|
||||||
}
|
}
|
||||||
|
@ -2209,11 +1974,7 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(
|
||||||
if (_player) {
|
if (_player) {
|
||||||
_player->showShadow();
|
_player->showShadow();
|
||||||
}
|
}
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.showVisible();
|
||||||
if (instance->visible) {
|
|
||||||
instance->widget->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -2355,7 +2116,7 @@ void MainWidget::showNewSection(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkFloatPlayerVisibility();
|
_playerFloats.checkVisibility();
|
||||||
orderWidgets();
|
orderWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2462,9 +2223,7 @@ void MainWidget::orderWidgets() {
|
||||||
_connecting->raise();
|
_connecting->raise();
|
||||||
_playerPlaylist->raise();
|
_playerPlaylist->raise();
|
||||||
_playerPanel->raise();
|
_playerPanel->raise();
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.raiseAll();
|
||||||
instance->widget->raise();
|
|
||||||
}
|
|
||||||
if (_hider) _hider->raise();
|
if (_hider) _hider->raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2477,9 +2236,7 @@ QRect MainWidget::historyRect() const {
|
||||||
|
|
||||||
QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
||||||
QPixmap result;
|
QPixmap result;
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.hideAll();
|
||||||
instance->widget->hide();
|
|
||||||
}
|
|
||||||
if (_player) {
|
if (_player) {
|
||||||
_player->hideShadow();
|
_player->hideShadow();
|
||||||
}
|
}
|
||||||
|
@ -2530,11 +2287,7 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m
|
||||||
if (_player) {
|
if (_player) {
|
||||||
_player->showShadow();
|
_player->showShadow();
|
||||||
}
|
}
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.showVisible();
|
||||||
if (instance->visible) {
|
|
||||||
instance->widget->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2671,9 +2424,7 @@ void MainWidget::hideAll() {
|
||||||
_player->setVisible(false);
|
_player->setVisible(false);
|
||||||
_playerHeight = 0;
|
_playerHeight = 0;
|
||||||
}
|
}
|
||||||
for (auto &instance : _playerFloats) {
|
_playerFloats.hideAll();
|
||||||
instance->widget->hide();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::showAll() {
|
void MainWidget::showAll() {
|
||||||
|
@ -2746,12 +2497,8 @@ void MainWidget::showAll() {
|
||||||
_playerHeight = _player->contentHeight();
|
_playerHeight = _player->contentHeight();
|
||||||
}
|
}
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
if (auto instance = currentFloatPlayer()) {
|
_playerFloats.checkVisibility();
|
||||||
checkFloatPlayerVisibility();
|
_playerFloats.showVisible();
|
||||||
if (instance->visible) {
|
|
||||||
instance->widget->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
App::wnd()->checkHistoryActivation();
|
App::wnd()->checkHistoryActivation();
|
||||||
}
|
}
|
||||||
|
@ -2861,9 +2608,8 @@ void MainWidget::updateControlsGeometry() {
|
||||||
updateMediaPlayerPosition();
|
updateMediaPlayerPosition();
|
||||||
updateMediaPlaylistPosition(_playerPlaylist->x());
|
updateMediaPlaylistPosition(_playerPlaylist->x());
|
||||||
_contentScrollAddToY = 0;
|
_contentScrollAddToY = 0;
|
||||||
for (auto &instance : _playerFloats) {
|
|
||||||
updateFloatPlayerPosition(instance.get());
|
_playerFloats.updatePositions();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::refreshResizeAreas() {
|
void MainWidget::refreshResizeAreas() {
|
||||||
|
@ -3105,16 +2851,12 @@ bool MainWidget::eventFilter(QObject *o, QEvent *e) {
|
||||||
_controller->showBackFromStack();
|
_controller->showBackFromStack();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (e->type() == QEvent::Wheel && !_playerFloats.empty()) {
|
} else if (e->type() == QEvent::Wheel) {
|
||||||
for (auto &instance : _playerFloats) {
|
if (const auto result = _playerFloats.filterWheelEvent(o, e)) {
|
||||||
if (instance->widget == o) {
|
return *result;
|
||||||
auto section = getFloatPlayerSection(
|
|
||||||
instance->column);
|
|
||||||
return section->wheelEventFromFloatPlayer(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return RpWidget::eventFilter(o, e);
|
||||||
return TWidget::eventFilter(o, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::handleAdaptiveLayoutUpdate() {
|
void MainWidget::handleAdaptiveLayoutUpdate() {
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "core/single_timer.h"
|
#include "core/single_timer.h"
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
#include "media/player/media_player_float.h"
|
||||||
|
|
||||||
struct HistoryMessageMarkupButton;
|
struct HistoryMessageMarkupButton;
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
@ -36,7 +37,6 @@ namespace Player {
|
||||||
class Widget;
|
class Widget;
|
||||||
class VolumeWidget;
|
class VolumeWidget;
|
||||||
class Panel;
|
class Panel;
|
||||||
class Float;
|
|
||||||
} // namespace Player
|
} // namespace Player
|
||||||
} // namespace Media
|
} // namespace Media
|
||||||
|
|
||||||
|
@ -80,7 +80,11 @@ class ItemBase;
|
||||||
} // namespace Layout
|
} // namespace Layout
|
||||||
} // namespace InlineBots
|
} // namespace InlineBots
|
||||||
|
|
||||||
class MainWidget : public Ui::RpWidget, public RPCSender, private base::Subscriber {
|
class MainWidget
|
||||||
|
: public Ui::RpWidget
|
||||||
|
, public RPCSender
|
||||||
|
, private base::Subscriber
|
||||||
|
, private Media::Player::FloatDelegate {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -349,28 +353,6 @@ protected:
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
bool eventFilter(QObject *o, QEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Float {
|
|
||||||
template <typename ToggleCallback, typename DraggedCallback>
|
|
||||||
Float(
|
|
||||||
QWidget *parent,
|
|
||||||
not_null<Window::Controller*> controller,
|
|
||||||
not_null<HistoryItem*> item,
|
|
||||||
ToggleCallback toggle,
|
|
||||||
DraggedCallback dragged);
|
|
||||||
|
|
||||||
bool hiddenByWidget = false;
|
|
||||||
bool hiddenByHistory = false;
|
|
||||||
bool visible = false;
|
|
||||||
RectPart animationSide;
|
|
||||||
Animation visibleAnimation;
|
|
||||||
Window::Column column;
|
|
||||||
RectPart corner;
|
|
||||||
QPoint dragFrom;
|
|
||||||
Animation draggedAnimation;
|
|
||||||
bool hiddenByDrag = false;
|
|
||||||
object_ptr<Media::Player::Float> widget;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ChannelGetDifferenceTime = QMap<ChannelData*, TimeMs>;
|
using ChannelGetDifferenceTime = QMap<ChannelData*, TimeMs>;
|
||||||
enum class ChannelDifferenceRequest {
|
enum class ChannelDifferenceRequest {
|
||||||
Unknown,
|
Unknown,
|
||||||
|
@ -479,27 +461,16 @@ private:
|
||||||
void showAll();
|
void showAll();
|
||||||
|
|
||||||
void clearCachedBackground();
|
void clearCachedBackground();
|
||||||
void checkCurrentFloatPlayer();
|
|
||||||
void createFloatPlayer(not_null<HistoryItem*> item);
|
not_null<Media::Player::FloatDelegate*> floatPlayerDelegate();
|
||||||
void toggleFloatPlayer(not_null<Float*> instance);
|
not_null<Ui::RpWidget*> floatPlayerWidget() override;
|
||||||
void checkFloatPlayerVisibility();
|
not_null<Window::Controller*> floatPlayerController() override;
|
||||||
void updateFloatPlayerPosition(not_null<Float*> instance);
|
not_null<Window::AbstractSectionWidget*> floatPlayerGetSection(
|
||||||
void removeFloatPlayer(not_null<Float*> instance);
|
Window::Column column) override;
|
||||||
Float *currentFloatPlayer() const {
|
void floatPlayerEnumerateSections(Fn<void(
|
||||||
return _playerFloats.empty() ? nullptr : _playerFloats.back().get();
|
not_null<Window::AbstractSectionWidget*> widget,
|
||||||
}
|
Window::Column widgetColumn)> callback) override;
|
||||||
Window::AbstractSectionWidget *getFloatPlayerSection(
|
void floatPlayerCloseHook(FullMsgId itemId) override;
|
||||||
Window::Column column) const;
|
|
||||||
void finishFloatPlayerDrag(
|
|
||||||
not_null<Float*> instance,
|
|
||||||
bool closed);
|
|
||||||
void updateFloatPlayerColumnCorner(QPoint center);
|
|
||||||
QPoint getFloatPlayerPosition(not_null<Float*> instance) const;
|
|
||||||
QPoint getFloatPlayerHiddenPosition(
|
|
||||||
QPoint position,
|
|
||||||
QSize size,
|
|
||||||
RectPart side) const;
|
|
||||||
RectPart getFloatPlayerSide(QPoint center) const;
|
|
||||||
|
|
||||||
bool getDifferenceTimeChanged(ChannelData *channel, int32 ms, ChannelGetDifferenceTime &channelCurTime, TimeMs &curTime);
|
bool getDifferenceTimeChanged(ChannelData *channel, int32 ms, ChannelGetDifferenceTime &channelCurTime, TimeMs &curTime);
|
||||||
|
|
||||||
|
@ -552,7 +523,7 @@ private:
|
||||||
object_ptr<Media::Player::Panel> _playerPlaylist;
|
object_ptr<Media::Player::Panel> _playerPlaylist;
|
||||||
object_ptr<Media::Player::Panel> _playerPanel;
|
object_ptr<Media::Player::Panel> _playerPanel;
|
||||||
bool _playerUsingPanel = false;
|
bool _playerUsingPanel = false;
|
||||||
std::vector<std::unique_ptr<Float>> _playerFloats;
|
Media::Player::FloatController _playerFloats;
|
||||||
|
|
||||||
QPointer<ConfirmBox> _forwardConfirm; // for single column layout
|
QPointer<ConfirmBox> _forwardConfirm; // for single column layout
|
||||||
object_ptr<HistoryHider> _hider = { nullptr };
|
object_ptr<HistoryHider> _hider = { nullptr };
|
||||||
|
|
|
@ -17,8 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/media_clip_reader.h"
|
#include "media/media_clip_reader.h"
|
||||||
#include "media/media_audio.h"
|
#include "media/media_audio.h"
|
||||||
#include "media/view/media_clip_playback.h"
|
#include "media/view/media_clip_playback.h"
|
||||||
|
#include "media/player/media_player_instance.h"
|
||||||
#include "media/player/media_player_round_controller.h"
|
#include "media/player/media_player_round_controller.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
#include "window/section_widget.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "styles/style_media_player.h"
|
#include "styles/style_media_player.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
@ -266,5 +268,318 @@ void Float::repaintItem() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ToggleCallback, typename DraggedCallback>
|
||||||
|
FloatController::Item::Item(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Window::Controller*> controller,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
ToggleCallback toggle,
|
||||||
|
DraggedCallback dragged)
|
||||||
|
: animationSide(RectPart::Right)
|
||||||
|
, column(Window::Column::Second)
|
||||||
|
, corner(RectPart::TopRight)
|
||||||
|
, widget(
|
||||||
|
parent,
|
||||||
|
controller,
|
||||||
|
item,
|
||||||
|
[=, toggle = std::move(toggle)](bool visible) {
|
||||||
|
toggle(this, visible);
|
||||||
|
},
|
||||||
|
[=, dragged = std::move(dragged)](bool closed) {
|
||||||
|
dragged(this, closed);
|
||||||
|
}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatController::FloatController(not_null<FloatDelegate*> delegate)
|
||||||
|
: _delegate(delegate)
|
||||||
|
, _parent(_delegate->floatPlayerWidget())
|
||||||
|
, _controller(_delegate->floatPlayerController()) {
|
||||||
|
subscribe(_controller->floatPlayerAreaUpdated(), [=] {
|
||||||
|
checkVisibility();
|
||||||
|
});
|
||||||
|
|
||||||
|
subscribe(Media::Player::instance()->trackChangedNotifier(), [=](
|
||||||
|
AudioMsgId::Type type) {
|
||||||
|
if (type == AudioMsgId::Type::Voice) {
|
||||||
|
checkCurrent();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::checkCurrent() {
|
||||||
|
const auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
|
||||||
|
const auto fullId = state.contextId();
|
||||||
|
const auto last = current();
|
||||||
|
if (last
|
||||||
|
&& !last->widget->detached()
|
||||||
|
&& last->widget->item()->fullId() == fullId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (last) {
|
||||||
|
last->widget->detach();
|
||||||
|
}
|
||||||
|
if (const auto item = App::histItemById(fullId)) {
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (const auto document = media->document()) {
|
||||||
|
if (document->isVideoMessage()) {
|
||||||
|
create(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::create(not_null<HistoryItem*> item) {
|
||||||
|
_items.push_back(std::make_unique<Item>(
|
||||||
|
_parent,
|
||||||
|
_controller,
|
||||||
|
item,
|
||||||
|
[=](not_null<Item*> instance, bool visible) {
|
||||||
|
instance->hiddenByWidget = !visible;
|
||||||
|
toggle(instance);
|
||||||
|
},
|
||||||
|
[=](not_null<Item*> instance, bool closed) {
|
||||||
|
finishDrag(instance, closed);
|
||||||
|
}));
|
||||||
|
current()->column = Auth().settings().floatPlayerColumn();
|
||||||
|
current()->corner = Auth().settings().floatPlayerCorner();
|
||||||
|
checkVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::toggle(not_null<Item*> instance) {
|
||||||
|
auto visible = !instance->hiddenByHistory && !instance->hiddenByWidget && instance->widget->isReady();
|
||||||
|
if (instance->visible != visible) {
|
||||||
|
instance->widget->resetMouseState();
|
||||||
|
instance->visible = visible;
|
||||||
|
if (!instance->visibleAnimation.animating() && !instance->hiddenByDrag) {
|
||||||
|
auto finalRect = QRect(getPosition(instance), instance->widget->size());
|
||||||
|
instance->animationSide = getSide(finalRect.center());
|
||||||
|
}
|
||||||
|
instance->visibleAnimation.start([=] {
|
||||||
|
updatePosition(instance);
|
||||||
|
}, visible ? 0. : 1., visible ? 1. : 0., st::slideDuration, visible ? anim::easeOutCirc : anim::linear);
|
||||||
|
updatePosition(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::checkVisibility() {
|
||||||
|
auto instance = current();
|
||||||
|
if (!instance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto amVisible = false;
|
||||||
|
if (auto item = instance->widget->item()) {
|
||||||
|
Auth().data().queryItemVisibility().notify({ item, &amVisible }, true);
|
||||||
|
}
|
||||||
|
instance->hiddenByHistory = amVisible;
|
||||||
|
toggle(instance);
|
||||||
|
updatePosition(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::hideAll() {
|
||||||
|
for (const auto &instance : _items) {
|
||||||
|
instance->widget->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::showVisible() {
|
||||||
|
for (const auto &instance : _items) {
|
||||||
|
if (instance->visible) {
|
||||||
|
instance->widget->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::raiseAll() {
|
||||||
|
for (const auto &instance : _items) {
|
||||||
|
instance->widget->raise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::updatePositions() {
|
||||||
|
for (const auto &instance : _items) {
|
||||||
|
updatePosition(instance.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> FloatController::filterWheelEvent(
|
||||||
|
QObject *object,
|
||||||
|
QEvent *event) {
|
||||||
|
for (const auto &instance : _items) {
|
||||||
|
if (instance->widget == object) {
|
||||||
|
const auto section = _delegate->floatPlayerGetSection(
|
||||||
|
instance->column);
|
||||||
|
return section->wheelEventFromFloatPlayer(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::updatePosition(not_null<Item*> instance) {
|
||||||
|
auto visible = instance->visibleAnimation.current(instance->visible ? 1. : 0.);
|
||||||
|
if (visible == 0. && !instance->visible) {
|
||||||
|
instance->widget->hide();
|
||||||
|
if (instance->widget->detached()) {
|
||||||
|
InvokeQueued(instance->widget, [=] {
|
||||||
|
remove(instance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = getHiddenPosition(instance->dragFrom, instance->widget->size(), instance->animationSide);
|
||||||
|
} else {
|
||||||
|
instance->widget->setOpacity(visible * visible);
|
||||||
|
position = getPosition(instance);
|
||||||
|
if (visible < 1.) {
|
||||||
|
auto hiddenPosition = getHiddenPosition(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));
|
||||||
|
}
|
||||||
|
instance->widget->move(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint FloatController::getHiddenPosition(
|
||||||
|
QPoint position,
|
||||||
|
QSize size,
|
||||||
|
RectPart side) const {
|
||||||
|
switch (side) {
|
||||||
|
case RectPart::Left: return { -size.width(), position.y() };
|
||||||
|
case RectPart::Top: return { position.x(), -size.height() };
|
||||||
|
case RectPart::Right: return { _parent->width(), position.y() };
|
||||||
|
case RectPart::Bottom: return { position.x(), _parent->height() };
|
||||||
|
}
|
||||||
|
Unexpected("Bad side in MainWidget::getFloatPlayerHiddenPosition().");
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint FloatController::getPosition(not_null<Item*> instance) const {
|
||||||
|
const auto section = _delegate->floatPlayerGetSection(instance->column);
|
||||||
|
const auto rect = section->rectForFloatPlayer();
|
||||||
|
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 _parent->mapFromGlobal(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
RectPart FloatController::getSide(QPoint center) const {
|
||||||
|
const auto left = std::abs(center.x());
|
||||||
|
const auto right = std::abs(_parent->width() - center.x());
|
||||||
|
const auto top = std::abs(center.y());
|
||||||
|
const auto bottom = std::abs(_parent->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 FloatController::remove(not_null<Item*> instance) {
|
||||||
|
auto widget = std::move(instance->widget);
|
||||||
|
auto i = ranges::find_if(_items, [&](auto &item) {
|
||||||
|
return (item.get() == instance);
|
||||||
|
});
|
||||||
|
Assert(i != _items.end());
|
||||||
|
_items.erase(i);
|
||||||
|
|
||||||
|
// ~QWidget() can call HistoryInner::enterEvent() which can
|
||||||
|
// lead to repaintHistoryItem() and we'll have an instance
|
||||||
|
// in _items with destroyed widget. So we destroy the
|
||||||
|
// instance first and only after that destroy the widget.
|
||||||
|
widget.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::updateColumnCorner(QPoint center) {
|
||||||
|
Expects(!_items.empty());
|
||||||
|
|
||||||
|
auto size = _items.back()->widget->size();
|
||||||
|
auto min = INT_MAX;
|
||||||
|
auto column = Auth().settings().floatPlayerColumn();
|
||||||
|
auto corner = Auth().settings().floatPlayerCorner();
|
||||||
|
auto checkSection = [&](
|
||||||
|
not_null<Window::AbstractSectionWidget*> widget,
|
||||||
|
Window::Column widgetColumn) {
|
||||||
|
auto rect = _parent->mapFromGlobal(widget->rectForFloatPlayer());
|
||||||
|
auto left = rect.x() + (size.width() / 2);
|
||||||
|
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 = [&](QPoint point, RectPart checked) {
|
||||||
|
auto distance = (point - center).manhattanLength();
|
||||||
|
if (min > distance) {
|
||||||
|
min = distance;
|
||||||
|
column = widgetColumn;
|
||||||
|
corner = checked;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
checkCorner({ left, top }, RectPart::TopLeft);
|
||||||
|
checkCorner({ right, top }, RectPart::TopRight);
|
||||||
|
checkCorner({ left, bottom }, RectPart::BottomLeft);
|
||||||
|
checkCorner({ right, bottom }, RectPart::BottomRight);
|
||||||
|
};
|
||||||
|
|
||||||
|
_delegate->floatPlayerEnumerateSections(checkSection);
|
||||||
|
|
||||||
|
if (Auth().settings().floatPlayerColumn() != column) {
|
||||||
|
Auth().settings().setFloatPlayerColumn(column);
|
||||||
|
Auth().saveSettingsDelayed();
|
||||||
|
}
|
||||||
|
if (Auth().settings().floatPlayerCorner() != corner) {
|
||||||
|
Auth().settings().setFloatPlayerCorner(corner);
|
||||||
|
Auth().saveSettingsDelayed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatController::finishDrag(not_null<Item*> instance, bool closed) {
|
||||||
|
instance->dragFrom = instance->widget->pos();
|
||||||
|
const auto center = instance->widget->geometry().center();
|
||||||
|
if (closed) {
|
||||||
|
instance->hiddenByDrag = true;
|
||||||
|
instance->animationSide = getSide(center);
|
||||||
|
}
|
||||||
|
updateColumnCorner(center);
|
||||||
|
instance->column = Auth().settings().floatPlayerColumn();
|
||||||
|
instance->corner = Auth().settings().floatPlayerCorner();
|
||||||
|
|
||||||
|
instance->draggedAnimation.finish();
|
||||||
|
instance->draggedAnimation.start(
|
||||||
|
[=] { updatePosition(instance); },
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
st::slideDuration,
|
||||||
|
anim::sineInOut);
|
||||||
|
updatePosition(instance);
|
||||||
|
|
||||||
|
if (closed) {
|
||||||
|
if (const auto item = instance->widget->item()) {
|
||||||
|
_delegate->floatPlayerCloseHook(item->fullId());
|
||||||
|
}
|
||||||
|
instance->widget->detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Player
|
} // namespace Player
|
||||||
} // namespace Media
|
} // namespace Media
|
||||||
|
|
|
@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class Controller;
|
class Controller;
|
||||||
|
class AbstractSectionWidget;
|
||||||
|
enum class Column;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
||||||
namespace Media {
|
namespace Media {
|
||||||
|
@ -93,5 +95,80 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FloatDelegate {
|
||||||
|
public:
|
||||||
|
virtual not_null<Ui::RpWidget*> floatPlayerWidget() = 0;
|
||||||
|
virtual not_null<Window::Controller*> floatPlayerController() = 0;
|
||||||
|
virtual not_null<Window::AbstractSectionWidget*> floatPlayerGetSection(
|
||||||
|
Window::Column column) = 0;
|
||||||
|
virtual void floatPlayerEnumerateSections(Fn<void(
|
||||||
|
not_null<Window::AbstractSectionWidget*> widget,
|
||||||
|
Window::Column widgetColumn)> callback) = 0;
|
||||||
|
virtual void floatPlayerCloseHook(FullMsgId itemId) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatController : private base::Subscriber {
|
||||||
|
public:
|
||||||
|
explicit FloatController(not_null<FloatDelegate*> delegate);
|
||||||
|
|
||||||
|
void checkVisibility();
|
||||||
|
void hideAll();
|
||||||
|
void showVisible();
|
||||||
|
void raiseAll();
|
||||||
|
void updatePositions();
|
||||||
|
std::optional<bool> filterWheelEvent(
|
||||||
|
QObject *object,
|
||||||
|
QEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Item {
|
||||||
|
template <typename ToggleCallback, typename DraggedCallback>
|
||||||
|
Item(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Window::Controller*> controller,
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
ToggleCallback toggle,
|
||||||
|
DraggedCallback dragged);
|
||||||
|
|
||||||
|
bool hiddenByWidget = false;
|
||||||
|
bool hiddenByHistory = false;
|
||||||
|
bool visible = false;
|
||||||
|
RectPart animationSide;
|
||||||
|
Animation visibleAnimation;
|
||||||
|
Window::Column column;
|
||||||
|
RectPart corner;
|
||||||
|
QPoint dragFrom;
|
||||||
|
Animation draggedAnimation;
|
||||||
|
bool hiddenByDrag = false;
|
||||||
|
object_ptr<Float> widget;
|
||||||
|
};
|
||||||
|
|
||||||
|
void checkCurrent();
|
||||||
|
void create(not_null<HistoryItem*> item);
|
||||||
|
void toggle(not_null<Item*> instance);
|
||||||
|
void updatePosition(not_null<Item*> instance);
|
||||||
|
void remove(not_null<Item*> instance);
|
||||||
|
Item *current() const {
|
||||||
|
return _items.empty() ? nullptr : _items.back().get();
|
||||||
|
}
|
||||||
|
void finishDrag(
|
||||||
|
not_null<Item*> instance,
|
||||||
|
bool closed);
|
||||||
|
void updateColumnCorner(QPoint center);
|
||||||
|
QPoint getPosition(not_null<Item*> instance) const;
|
||||||
|
QPoint getHiddenPosition(
|
||||||
|
QPoint position,
|
||||||
|
QSize size,
|
||||||
|
RectPart side) const;
|
||||||
|
RectPart getSide(QPoint center) const;
|
||||||
|
|
||||||
|
not_null<FloatDelegate*> _delegate;
|
||||||
|
not_null<Ui::RpWidget*> _parent;
|
||||||
|
not_null<Window::Controller*> _controller;
|
||||||
|
std::vector<std::unique_ptr<Item>> _items;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Player
|
} // namespace Player
|
||||||
} // namespace Media
|
} // namespace Media
|
||||||
|
|
Loading…
Add table
Reference in a new issue