init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
This commit is contained in:
667
Telegram/SourceFiles/media/player/media_player_float.cpp
Normal file
667
Telegram/SourceFiles/media/player/media_player_float.cpp
Normal file
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "media/player/media_player_float.h"
|
||||
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "media/streaming/media_streaming_instance.h"
|
||||
#include "media/view/media_view_playback_progress.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "styles/style_media_player.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
|
||||
using DoubleClickedCallback = Fn<void(not_null<const HistoryItem*>)>;
|
||||
|
||||
RoundPainter::RoundPainter(not_null<HistoryItem*> item)
|
||||
: _item(item) {
|
||||
}
|
||||
|
||||
bool RoundPainter::fillFrame(const QSize &size) {
|
||||
auto creating = _frame.isNull();
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
if (creating) {
|
||||
_frame = QImage(
|
||||
size * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_frame.setDevicePixelRatio(ratio);
|
||||
}
|
||||
auto frameInner = [&] {
|
||||
return QRect(QPoint(), _frame.size() / ratio);
|
||||
};
|
||||
if (const auto streamed = instance()->roundVideoStreamed(_item)) {
|
||||
auto request = Streaming::FrameRequest::NonStrict();
|
||||
request.outer = request.resize = _frame.size();
|
||||
if (_roundingMask.size() != request.outer) {
|
||||
_roundingMask = Images::EllipseMask(frameInner().size());
|
||||
}
|
||||
request.mask = _roundingMask;
|
||||
auto frame = streamed->frame(request);
|
||||
if (!frame.isNull()) {
|
||||
_frame.fill(Qt::transparent);
|
||||
|
||||
auto p = QPainter(&_frame);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawImage(frameInner(), frame);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (creating) {
|
||||
_frame.fill(Qt::transparent);
|
||||
|
||||
auto p = QPainter(&_frame);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::imageBg);
|
||||
p.drawEllipse(frameInner());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const QImage &RoundPainter::frame() const {
|
||||
return _frame;
|
||||
}
|
||||
|
||||
Float::Float(
|
||||
QWidget *parent,
|
||||
not_null<HistoryItem*> item,
|
||||
Fn<void(bool visible)> toggleCallback,
|
||||
Fn<void(bool closed)> draggedCallback,
|
||||
DoubleClickedCallback doubleClickedCallback)
|
||||
: RpWidget(parent)
|
||||
, _item(item)
|
||||
, _toggleCallback(std::move(toggleCallback))
|
||||
, _draggedCallback(std::move(draggedCallback))
|
||||
, _doubleClickedCallback(std::move(doubleClickedCallback)) {
|
||||
auto media = _item->media();
|
||||
Assert(media != nullptr);
|
||||
|
||||
auto document = media->document();
|
||||
Assert(document != nullptr);
|
||||
Assert(document->isVideoMessage());
|
||||
|
||||
auto margin = st::mediaPlayerFloatMargin;
|
||||
auto size = 2 * margin + st::mediaPlayerFloatSize;
|
||||
resize(size, size);
|
||||
|
||||
_roundPainter = std::make_unique<RoundPainter>(item);
|
||||
prepareShadow();
|
||||
|
||||
document->session().data().itemRepaintRequest(
|
||||
) | rpl::on_next([this](auto item) {
|
||||
if (_item == item) {
|
||||
repaintItem();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
document->session().data().itemRemoved(
|
||||
) | rpl::on_next([this](auto item) {
|
||||
if (_item == item) {
|
||||
detach();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
document->session().account().sessionChanges(
|
||||
) | rpl::on_next([=] {
|
||||
detach();
|
||||
}, lifetime());
|
||||
|
||||
setCursor(style::cur_pointer);
|
||||
}
|
||||
|
||||
void Float::mousePressEvent(QMouseEvent *e) {
|
||||
_down = true;
|
||||
_downPoint = e->pos();
|
||||
}
|
||||
|
||||
void Float::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (_down && (e->pos() - _downPoint).manhattanLength() > QApplication::startDragDistance()) {
|
||||
_down = false;
|
||||
_drag = true;
|
||||
_dragLocalPoint = e->pos();
|
||||
} else if (_drag) {
|
||||
auto delta = (e->pos() - _dragLocalPoint);
|
||||
move(pos() + delta);
|
||||
setOpacity(outRatio());
|
||||
}
|
||||
}
|
||||
|
||||
float64 Float::outRatio() const {
|
||||
auto parent = parentWidget()->rect();
|
||||
auto min = 1.;
|
||||
if (x() < parent.x()) {
|
||||
accumulate_min(min, 1. - (parent.x() - x()) / float64(width()));
|
||||
}
|
||||
if (y() < parent.y()) {
|
||||
accumulate_min(min, 1. - (parent.y() - y()) / float64(height()));
|
||||
}
|
||||
if (x() + width() > parent.x() + parent.width()) {
|
||||
accumulate_min(min, 1. - (x() + width() - parent.x() - parent.width()) / float64(width()));
|
||||
}
|
||||
if (y() + height() > parent.y() + parent.height()) {
|
||||
accumulate_min(min, 1. - (y() + height() - parent.y() - parent.height()) / float64(height()));
|
||||
}
|
||||
return std::clamp(min, 0., 1.);
|
||||
}
|
||||
|
||||
void Float::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (base::take(_down) && _item) {
|
||||
pauseResume();
|
||||
}
|
||||
if (_drag) {
|
||||
finishDrag(outRatio() < 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
void Float::finishDrag(bool closed) {
|
||||
_drag = false;
|
||||
if (_draggedCallback) {
|
||||
_draggedCallback(closed);
|
||||
}
|
||||
}
|
||||
|
||||
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||
if (_item && _doubleClickedCallback) {
|
||||
// Handle second click.
|
||||
pauseResume();
|
||||
_doubleClickedCallback(_item);
|
||||
}
|
||||
}
|
||||
|
||||
void Float::pauseResume() {
|
||||
if (const auto streamed = getStreamed()) {
|
||||
if (streamed->paused()) {
|
||||
streamed->resume();
|
||||
} else {
|
||||
streamed->pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Float::detach() {
|
||||
if (_item) {
|
||||
_item = nullptr;
|
||||
_roundPainter = nullptr;
|
||||
if (_toggleCallback) {
|
||||
_toggleCallback(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Float::prepareShadow() {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto shadow = QImage(
|
||||
size() * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
shadow.fill(Qt::transparent);
|
||||
shadow.setDevicePixelRatio(ratio);
|
||||
{
|
||||
auto p = QPainter(&shadow);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::shadowFg);
|
||||
auto extend = 2 * st::lineWidth;
|
||||
p.drawEllipse(getInnerRect().marginsAdded(QMargins(extend, extend, extend, extend)));
|
||||
}
|
||||
_shadow = Ui::PixmapFromImage(Images::Blur(std::move(shadow)));
|
||||
}
|
||||
|
||||
QRect Float::getInnerRect() const {
|
||||
auto margin = st::mediaPlayerFloatMargin;
|
||||
return rect().marginsRemoved(QMargins(margin, margin, margin, margin));
|
||||
}
|
||||
|
||||
void Float::paintEvent(QPaintEvent *e) {
|
||||
auto p = QPainter(this);
|
||||
|
||||
p.setOpacity(_opacity);
|
||||
p.drawPixmap(0, 0, _shadow);
|
||||
|
||||
const auto inner = getInnerRect();
|
||||
if (!(_roundPainter && _roundPainter->fillFrame(inner.size()))
|
||||
&& _toggleCallback) {
|
||||
_toggleCallback(false);
|
||||
}
|
||||
|
||||
if (_roundPainter) {
|
||||
p.drawImage(inner.topLeft(), _roundPainter->frame());
|
||||
}
|
||||
|
||||
const auto playback = getPlayback();
|
||||
const auto progress = playback ? playback->value() : 1.;
|
||||
if (progress > 0.) {
|
||||
auto pen = st::historyVideoMessageProgressFg->p;
|
||||
//auto was = p.pen();
|
||||
pen.setWidth(st::radialLine);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
p.setPen(pen);
|
||||
p.setOpacity(_opacity * st::historyVideoMessageProgressOpacity);
|
||||
|
||||
auto from = arc::kQuarterLength;
|
||||
auto len = -qRound(arc::kFullLength * progress);
|
||||
auto stepInside = st::radialLine / 2;
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawArc(inner.marginsRemoved(QMargins(stepInside, stepInside, stepInside, stepInside)), from, len);
|
||||
}
|
||||
|
||||
//p.setPen(was);
|
||||
//p.setOpacity(_opacity);
|
||||
}
|
||||
}
|
||||
|
||||
Streaming::Instance *Float::getStreamed() const {
|
||||
return instance()->roundVideoStreamed(_item);
|
||||
}
|
||||
|
||||
View::PlaybackProgress *Float::getPlayback() const {
|
||||
return instance()->roundVideoPlayback(_item);
|
||||
}
|
||||
|
||||
bool Float::hasFrame() const {
|
||||
return (getStreamed() != nullptr);
|
||||
}
|
||||
|
||||
void Float::repaintItem() {
|
||||
update();
|
||||
if (hasFrame() && _toggleCallback) {
|
||||
_toggleCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename ToggleCallback, typename DraggedCallback>
|
||||
FloatController::Item::Item(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<HistoryItem*> item,
|
||||
ToggleCallback toggle,
|
||||
DraggedCallback dragged,
|
||||
DoubleClickedCallback doubleClicked)
|
||||
: animationSide(RectPart::Right)
|
||||
, column(Window::Column::Second)
|
||||
, corner(RectPart::TopRight)
|
||||
, widget(
|
||||
parent,
|
||||
item,
|
||||
[=, toggle = std::move(toggle)](bool visible) {
|
||||
toggle(this, visible);
|
||||
},
|
||||
[=, dragged = std::move(dragged)](bool closed) {
|
||||
dragged(this, closed);
|
||||
},
|
||||
std::move(doubleClicked)) {
|
||||
}
|
||||
|
||||
FloatController::FloatController(not_null<FloatDelegate*> delegate)
|
||||
: _delegate(delegate)
|
||||
, _parent(_delegate->floatPlayerWidget()) {
|
||||
Media::Player::instance()->trackChanged(
|
||||
) | rpl::filter([=](AudioMsgId::Type type) {
|
||||
return (type == AudioMsgId::Type::Voice);
|
||||
}) | rpl::on_next([=] {
|
||||
checkCurrent();
|
||||
}, _lifetime);
|
||||
|
||||
startDelegateHandling();
|
||||
}
|
||||
|
||||
void FloatController::replaceDelegate(not_null<FloatDelegate*> delegate) {
|
||||
_delegateLifetime.destroy();
|
||||
|
||||
_delegate = delegate;
|
||||
_parent = _delegate->floatPlayerWidget();
|
||||
|
||||
startDelegateHandling();
|
||||
|
||||
for (const auto &item : _items) {
|
||||
item->widget->setParent(_parent);
|
||||
}
|
||||
checkVisibility();
|
||||
}
|
||||
|
||||
void FloatController::startDelegateHandling() {
|
||||
_delegate->floatPlayerCheckVisibilityRequests(
|
||||
) | rpl::on_next([=] {
|
||||
checkVisibility();
|
||||
}, _delegateLifetime);
|
||||
|
||||
_delegate->floatPlayerHideAllRequests(
|
||||
) | rpl::on_next([=] {
|
||||
hideAll();
|
||||
}, _delegateLifetime);
|
||||
|
||||
_delegate->floatPlayerShowVisibleRequests(
|
||||
) | rpl::on_next([=] {
|
||||
showVisible();
|
||||
}, _delegateLifetime);
|
||||
|
||||
_delegate->floatPlayerRaiseAllRequests(
|
||||
) | rpl::on_next([=] {
|
||||
raiseAll();
|
||||
}, _delegateLifetime);
|
||||
|
||||
_delegate->floatPlayerUpdatePositionsRequests(
|
||||
) | rpl::on_next([=] {
|
||||
updatePositions();
|
||||
}, _delegateLifetime);
|
||||
|
||||
_delegate->floatPlayerFilterWheelEventRequests(
|
||||
) | rpl::on_next([=](
|
||||
const FloatDelegate::FloatPlayerFilterWheelEventRequest &request) {
|
||||
*request.result = filterWheelEvent(request.object, request.event);
|
||||
}, _delegateLifetime);
|
||||
|
||||
_delegate->floatPlayerAreaUpdates(
|
||||
) | rpl::on_next([=] {
|
||||
checkVisibility();
|
||||
}, _delegateLifetime);
|
||||
}
|
||||
|
||||
void FloatController::checkCurrent() {
|
||||
const auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
|
||||
const auto audio = state.audio();
|
||||
const auto fullId = state.contextId();
|
||||
const auto last = current();
|
||||
if (last
|
||||
&& audio
|
||||
&& !last->widget->detached()
|
||||
&& (&last->widget->item()->history()->session() == &audio->session())
|
||||
&& (last->widget->item()->fullId() == fullId)) {
|
||||
return;
|
||||
}
|
||||
if (last) {
|
||||
last->widget->detach();
|
||||
}
|
||||
if (!audio) {
|
||||
return;
|
||||
}
|
||||
if (const auto item = audio->session().data().message(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,
|
||||
item,
|
||||
[=](not_null<Item*> instance, bool visible) {
|
||||
instance->hiddenByWidget = !visible;
|
||||
toggle(instance);
|
||||
},
|
||||
[=](not_null<Item*> instance, bool closed) {
|
||||
finishDrag(instance, closed);
|
||||
},
|
||||
[=](not_null<const HistoryItem*> item) {
|
||||
_delegate->floatPlayerDoubleClickEvent(item);
|
||||
}));
|
||||
current()->column = Core::App().settings().floatPlayerColumn();
|
||||
current()->corner = Core::App().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() {
|
||||
const auto instance = current();
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto item = instance->widget->item();
|
||||
instance->hiddenByHistory = item
|
||||
? _delegate->floatPlayerIsVisible(item)
|
||||
: false;
|
||||
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(
|
||||
not_null<QObject*> object,
|
||||
not_null<QEvent*> event) {
|
||||
for (const auto &instance : _items) {
|
||||
if (instance->widget == object) {
|
||||
const auto section = _delegate->floatPlayerGetSection(
|
||||
instance->column);
|
||||
return section->floatPlayerHandleWheelEvent(event);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void FloatController::updatePosition(not_null<Item*> instance) {
|
||||
auto visible = instance->visibleAnimation.value(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.value(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->floatPlayerAvailableRect();
|
||||
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 = Core::App().settings().floatPlayerColumn();
|
||||
auto corner = Core::App().settings().floatPlayerCorner();
|
||||
auto checkSection = [&](
|
||||
not_null<FloatSectionDelegate*> widget,
|
||||
Window::Column widgetColumn) {
|
||||
auto rect = _parent->mapFromGlobal(
|
||||
widget->floatPlayerAvailableRect());
|
||||
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);
|
||||
|
||||
auto &settings = Core::App().settings();
|
||||
if (settings.floatPlayerColumn() != column) {
|
||||
settings.setFloatPlayerColumn(column);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
if (settings.floatPlayerCorner() != corner) {
|
||||
settings.setFloatPlayerCorner(corner);
|
||||
Core::App().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 = Core::App().settings().floatPlayerColumn();
|
||||
instance->corner = Core::App().settings().floatPlayerCorner();
|
||||
|
||||
instance->draggedAnimation.stop();
|
||||
instance->draggedAnimation.start(
|
||||
[=] { updatePosition(instance); },
|
||||
0.,
|
||||
1.,
|
||||
st::slideDuration,
|
||||
anim::sineInOut);
|
||||
updatePosition(instance);
|
||||
|
||||
if (closed) {
|
||||
if (const auto item = instance->widget->item()) {
|
||||
_closeEvents.fire(item->fullId());
|
||||
}
|
||||
instance->widget->detach();
|
||||
Media::Player::instance()->stop(AudioMsgId::Type::Voice);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Player
|
||||
} // namespace Media
|
||||
Reference in New Issue
Block a user