mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Snap floating player to column and corner.
This commit is contained in:
parent
8f290451b6
commit
ee6d80673a
@ -2648,18 +2648,7 @@ namespace {
|
||||
auto gifs = ::gifItems;
|
||||
for_const (auto item, gifs) {
|
||||
if (auto media = item->getMedia()) {
|
||||
media->stopInline();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stopRoundVideoPlayback() {
|
||||
if (!::gifItems.isEmpty()) {
|
||||
auto gifs = ::gifItems;
|
||||
for_const (auto item, gifs) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (media->isRoundVideoPlaying()) {
|
||||
if (!media->isRoundVideoPlaying()) {
|
||||
media->stopInline();
|
||||
}
|
||||
}
|
||||
|
@ -2631,7 +2631,7 @@ void HistoryWidget::onScroll() {
|
||||
}
|
||||
|
||||
bool HistoryWidget::isItemCompletelyHidden(HistoryItem *item) const {
|
||||
auto top = _list->itemTop(item);
|
||||
auto top = _list ? _list->itemTop(item) : -2;
|
||||
if (top < 0) {
|
||||
return true;
|
||||
}
|
||||
@ -3428,7 +3428,7 @@ bool HistoryWidget::wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn
|
||||
QRect HistoryWidget::rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) {
|
||||
if (playerColumn == Window::Column::Third && _tabbedSection) {
|
||||
auto tabbedColumn = (myColumn == Window::Column::First) ? Window::Column::Second : Window::Column::Third;
|
||||
return mapToGlobal(_tabbedSection->rectForFloatPlayer(tabbedColumn, playerColumn));
|
||||
return _tabbedSection->rectForFloatPlayer(tabbedColumn, playerColumn);
|
||||
}
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
@ -98,9 +98,11 @@ StackItemSection::StackItemSection(std::unique_ptr<Window::SectionMemento> &&mem
|
||||
StackItemSection::~StackItemSection() {
|
||||
}
|
||||
|
||||
template <typename ToggleCallback>
|
||||
MainWidget::Float::Float(QWidget *parent, HistoryItem *item, ToggleCallback callback) : widget(parent, item, [this, toggle = std::move(callback)](bool visible) {
|
||||
template <typename ToggleCallback, typename DraggedCallback>
|
||||
MainWidget::Float::Float(QWidget *parent, HistoryItem *item, ToggleCallback toggle, DraggedCallback dragged) : widget(parent, item, [this, toggle = std::move(toggle)](bool visible) {
|
||||
toggle(this, visible);
|
||||
}, [this, dragged = std::move(dragged)](bool closed) {
|
||||
dragged(this, closed);
|
||||
}) {
|
||||
}
|
||||
|
||||
@ -248,7 +250,11 @@ void MainWidget::checkCurrentFloatPlayer() {
|
||||
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](Float *instance, bool visible) {
|
||||
instance->hiddenByWidget = !visible;
|
||||
toggleFloatPlayer(instance);
|
||||
}, [this](Float *instance, bool closed) {
|
||||
finishFloatPlayerDrag(instance, closed);
|
||||
}));
|
||||
currentFloatPlayer()->corner = _playerFloatCorner;
|
||||
currentFloatPlayer()->column = _playerFloatColumn;
|
||||
checkFloatPlayerVisibility();
|
||||
}
|
||||
}
|
||||
@ -313,8 +319,15 @@ void MainWidget::updateFloatPlayerPosition(Float *instance) {
|
||||
position = mapFromGlobal(position);
|
||||
|
||||
auto hiddenTop = Window::IsTopCorner(instance->corner) ? -instance->widget->height() : height();
|
||||
auto visibleTop = position.y();
|
||||
instance->widget->move(position.x(), anim::interpolate(hiddenTop, visibleTop, visible));
|
||||
position.setY(anim::interpolate(hiddenTop, position.y(), visible));
|
||||
if (!instance->widget->dragged()) {
|
||||
auto dragged = instance->draggedAnimation.current(1.);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::removeFloatPlayer(Float *instance) {
|
||||
@ -358,6 +371,77 @@ Window::AbstractSectionWidget *MainWidget::getFloatPlayerSection(gsl::not_null<W
|
||||
return _history;
|
||||
}
|
||||
|
||||
void MainWidget::updateFloatPlayerColumnCorner(QPoint center) {
|
||||
Expects(!_playerFloats.empty());
|
||||
auto size = _playerFloats.back()->widget->size();
|
||||
auto min = INT_MAX;
|
||||
auto checkSection = [this, center, size, &min](Window::AbstractSectionWidget *widget, Window::Column myColumn, Window::Column playerColumn) {
|
||||
auto rect = mapFromGlobal(widget->rectForFloatPlayer(myColumn, playerColumn));
|
||||
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 = [this, playerColumn, &min](int distance, Window::Corner corner) {
|
||||
if (min > distance) {
|
||||
min = distance;
|
||||
_playerFloatColumn = playerColumn;
|
||||
_playerFloatCorner = corner;
|
||||
}
|
||||
};
|
||||
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);
|
||||
};
|
||||
|
||||
if (!Adaptive::Normal()) {
|
||||
if (Adaptive::OneColumn() && selectingPeer()) {
|
||||
checkSection(_dialogs, Window::Column::First, Window::Column::First);
|
||||
} else if (_overview) {
|
||||
checkSection(_overview, Window::Column::Second, Window::Column::Second);
|
||||
} else if (_wideSection) {
|
||||
checkSection(_wideSection, Window::Column::Second, Window::Column::Second);
|
||||
} else if (!Adaptive::OneColumn() || _history->peer()) {
|
||||
checkSection(_history, Window::Column::Second, Window::Column::Second);
|
||||
checkSection(_history, Window::Column::Second, Window::Column::Third);
|
||||
} else {
|
||||
checkSection(_dialogs, Window::Column::First, Window::Column::First);
|
||||
}
|
||||
} else {
|
||||
checkSection(_dialogs, Window::Column::First, Window::Column::First);
|
||||
if (_overview) {
|
||||
checkSection(_overview, Window::Column::Second, Window::Column::Second);
|
||||
} else if (_wideSection) {
|
||||
checkSection(_wideSection, Window::Column::Second, Window::Column::Second);
|
||||
} else {
|
||||
checkSection(_history, Window::Column::Second, Window::Column::Second);
|
||||
checkSection(_history, Window::Column::Second, Window::Column::Third);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::finishFloatPlayerDrag(Float *instance, bool closed) {
|
||||
instance->dragFrom = instance->widget->pos();
|
||||
|
||||
updateFloatPlayerColumnCorner(instance->widget->geometry().center());
|
||||
instance->column = _playerFloatColumn;
|
||||
instance->corner = _playerFloatCorner;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
instance->widget->detach();
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) {
|
||||
PeerData *p = App::peer(peer);
|
||||
if (!peer || (p->isChannel() && !p->asChannel()->canPublish() && p->asChannel()->isBroadcast()) || (p->isChat() && !p->asChat()->canWrite()) || (p->isUser() && p->asUser()->isInaccessible())) {
|
||||
@ -1816,7 +1900,6 @@ void MainWidget::setCurrentCall(Calls::Call *call) {
|
||||
destroyCallTopBar();
|
||||
}
|
||||
});
|
||||
App::stopRoundVideoPlayback();
|
||||
} else {
|
||||
destroyCallTopBar();
|
||||
}
|
||||
|
@ -463,8 +463,8 @@ protected:
|
||||
|
||||
private:
|
||||
struct Float {
|
||||
template <typename ToggleCallback>
|
||||
Float(QWidget *parent, HistoryItem *item, ToggleCallback callback);
|
||||
template <typename ToggleCallback, typename DraggedCallback>
|
||||
Float(QWidget *parent, HistoryItem *item, ToggleCallback callback, DraggedCallback dragged);
|
||||
|
||||
bool hiddenByWidget = false;
|
||||
bool hiddenByHistory = false;
|
||||
@ -472,7 +472,8 @@ private:
|
||||
Animation visibleAnimation;
|
||||
Window::Corner corner = Window::Corner::TopRight;
|
||||
Window::Column column = Window::Column::Second;
|
||||
QPoint position;
|
||||
QPoint dragFrom;
|
||||
Animation draggedAnimation;
|
||||
object_ptr<Media::Player::Float> widget;
|
||||
};
|
||||
|
||||
@ -584,6 +585,8 @@ private:
|
||||
return _playerFloats.empty() ? nullptr : _playerFloats.back().get();
|
||||
}
|
||||
Window::AbstractSectionWidget *getFloatPlayerSection(gsl::not_null<Window::Column*> column);
|
||||
void finishFloatPlayerDrag(Float *instance, bool closed);
|
||||
void updateFloatPlayerColumnCorner(QPoint center);
|
||||
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount);
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount, const MTPUpdates &updates);
|
||||
@ -637,6 +640,8 @@ private:
|
||||
object_ptr<Media::Player::Panel> _playerPanel;
|
||||
bool _playerUsingPanel = false;
|
||||
std::vector<std::unique_ptr<Float>> _playerFloats;
|
||||
Window::Corner _playerFloatCorner = Window::Corner::TopRight;
|
||||
Window::Column _playerFloatColumn = Window::Column::Second;
|
||||
|
||||
QPointer<ConfirmBox> _forwardConfirm; // for single column layout
|
||||
object_ptr<HistoryHider> _hider = { nullptr };
|
||||
|
@ -316,7 +316,7 @@ void Mixer::Track::reattach(AudioMsgId::Type type) {
|
||||
}
|
||||
|
||||
alSourcei(stream.source, AL_SAMPLE_OFFSET, qMax(state.position - bufferedPosition, 0LL));
|
||||
if (!IsStopped(state.state)) {
|
||||
if (!IsStopped(state.state) && state.state != State::PausedAtEnd) {
|
||||
alSourcef(stream.source, AL_GAIN, ComputeVolume(type));
|
||||
alSourcePlay(stream.source);
|
||||
if (IsPaused(state.state)) {
|
||||
@ -781,6 +781,7 @@ void Mixer::pause(const AudioMsgId &audio, bool fast) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::Pausing:
|
||||
case State::Stopping: {
|
||||
track->state.state = fast ? State::Paused : State::Pausing;
|
||||
} break;
|
||||
|
@ -29,9 +29,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
|
||||
Float::Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback) : TWidget(parent)
|
||||
Float::Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback, base::lambda<void(bool closed)> draggedCallback) : TWidget(parent)
|
||||
, _item(item)
|
||||
, _toggleCallback(std::move(toggleCallback)) {
|
||||
, _toggleCallback(std::move(toggleCallback))
|
||||
, _draggedCallback(std::move(draggedCallback)) {
|
||||
auto media = _item->getMedia();
|
||||
t_assert(media != nullptr);
|
||||
|
||||
@ -56,14 +57,56 @@ Float::Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)
|
||||
|
||||
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 snap(min, 0., 1.);
|
||||
}
|
||||
|
||||
void Float::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_down && _item) {
|
||||
if (auto media = _item->getMedia()) {
|
||||
if (_down) {
|
||||
_down = false;
|
||||
if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||
media->playInline();
|
||||
}
|
||||
}
|
||||
if (_drag) {
|
||||
finishDrag(outRatio() < 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
void Float::finishDrag(bool closed) {
|
||||
_drag = false;
|
||||
if (_draggedCallback) {
|
||||
_draggedCallback(closed);
|
||||
}
|
||||
}
|
||||
|
||||
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||
|
@ -29,21 +29,29 @@ namespace Player {
|
||||
|
||||
class Float : public TWidget, private base::Subscriber {
|
||||
public:
|
||||
Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback);
|
||||
Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback, base::lambda<void(bool closed)> draggedCallback);
|
||||
|
||||
HistoryItem *item() const {
|
||||
return _item;
|
||||
}
|
||||
void setOpacity(float64 opacity) {
|
||||
_opacity = opacity;
|
||||
update();
|
||||
if (_opacity != opacity) {
|
||||
_opacity = opacity;
|
||||
update();
|
||||
}
|
||||
}
|
||||
void detach();
|
||||
bool detached() const {
|
||||
return !_item;
|
||||
}
|
||||
bool dragged() const {
|
||||
return _drag;
|
||||
}
|
||||
void resetMouseState() {
|
||||
_down = false;
|
||||
if (_drag) {
|
||||
finishDrag(false);
|
||||
}
|
||||
}
|
||||
void ui_repaintHistoryItem(const HistoryItem *item) {
|
||||
if (item == _item) {
|
||||
@ -53,11 +61,13 @@ public:
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
float64 outRatio() const;
|
||||
Clip::Reader *getReader() const;
|
||||
void repaintItem();
|
||||
void prepareShadow();
|
||||
@ -65,6 +75,7 @@ private:
|
||||
bool fillFrame();
|
||||
QRect getInnerRect() const;
|
||||
void updatePlayback();
|
||||
void finishDrag(bool closed);
|
||||
|
||||
HistoryItem *_item = nullptr;
|
||||
base::lambda<void(bool visible)> _toggleCallback;
|
||||
@ -74,6 +85,11 @@ private:
|
||||
QPixmap _shadow;
|
||||
QImage _frame;
|
||||
bool _down = false;
|
||||
QPoint _downPoint;
|
||||
|
||||
bool _drag = false;
|
||||
QPoint _dragLocalPoint;
|
||||
base::lambda<void(bool closed)> _draggedCallback;
|
||||
|
||||
std::unique_ptr<Clip::Playback> _roundPlayback;
|
||||
|
||||
|
@ -187,18 +187,23 @@ void Widget::updateVolumeToggleIcon() {
|
||||
}
|
||||
|
||||
void Widget::setCloseCallback(base::lambda<void()> callback) {
|
||||
_close->setClickedCallback([this, callback = std::move(callback)] {
|
||||
_voiceIsActive = false;
|
||||
if (_type == AudioMsgId::Type::Voice) {
|
||||
auto songData = instance()->current(AudioMsgId::Type::Song);
|
||||
auto songState = mixer()->currentState(AudioMsgId::Type::Song);
|
||||
if (songData == songState.id && !IsStoppedOrStopping(songState.state)) {
|
||||
instance()->stop(AudioMsgId::Type::Voice);
|
||||
return;
|
||||
}
|
||||
_closeCallback = std::move(callback);
|
||||
_close->setClickedCallback([this] { stopAndClose(); });
|
||||
}
|
||||
|
||||
void Widget::stopAndClose() {
|
||||
_voiceIsActive = false;
|
||||
if (_type == AudioMsgId::Type::Voice) {
|
||||
auto songData = instance()->current(AudioMsgId::Type::Song);
|
||||
auto songState = mixer()->currentState(AudioMsgId::Type::Song);
|
||||
if (songData == songState.id && !IsStoppedOrStopping(songState.state)) {
|
||||
instance()->stop(AudioMsgId::Type::Voice);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
if (_closeCallback) {
|
||||
_closeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setShadowGeometryToLeft(int x, int y, int w, int h) {
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
Widget(QWidget *parent);
|
||||
|
||||
void setCloseCallback(base::lambda<void()> callback);
|
||||
|
||||
void stopAndClose();
|
||||
void setShadowGeometryToLeft(int x, int y, int w, int h);
|
||||
void showShadow();
|
||||
void hideShadow();
|
||||
@ -101,6 +101,7 @@ private:
|
||||
// We change _voiceIsActive to false only manually or from tracksFinished().
|
||||
AudioMsgId::Type _type = AudioMsgId::Type::Unknown;
|
||||
bool _voiceIsActive = false;
|
||||
base::lambda<void()> _closeCallback;
|
||||
|
||||
bool _labelsOver = false;
|
||||
bool _labelsDown = false;
|
||||
|
Loading…
Reference in New Issue
Block a user