mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-28 18:23:38 +00:00
Add a floating player for round video messages.
This commit is contained in:
parent
0bfff65306
commit
b7550f63c9
@ -28,7 +28,7 @@ namespace ChatHelpers {
|
||||
TabbedSection::TabbedSection(QWidget *parent, gsl::not_null<Window::Controller*> controller) : TabbedSection(parent, controller, object_ptr<TabbedSelector>(this, controller)) {
|
||||
}
|
||||
|
||||
TabbedSection::TabbedSection(QWidget *parent, gsl::not_null<Window::Controller*> controller, object_ptr<TabbedSelector> selector) : TWidget(parent)
|
||||
TabbedSection::TabbedSection(QWidget *parent, gsl::not_null<Window::Controller*> controller, object_ptr<TabbedSelector> selector) : Window::AbstractSectionWidget(parent, controller)
|
||||
, _selector(std::move(selector)) {
|
||||
resize(st::emojiPanWidth, st::emojiPanMaxHeight);
|
||||
|
||||
@ -73,4 +73,12 @@ void TabbedSection::stickersInstalled(uint64 setId) {
|
||||
_selector->stickersInstalled(setId);
|
||||
}
|
||||
|
||||
bool TabbedSection::wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) {
|
||||
return _selector->wheelEventFromFloatPlayer(e);
|
||||
}
|
||||
|
||||
QRect TabbedSection::rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) {
|
||||
return _selector->rectForFloatPlayer();
|
||||
}
|
||||
|
||||
} // namespace ChatHelpers
|
||||
|
@ -20,17 +20,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/twidget.h"
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
#include "window/section_widget.h"
|
||||
|
||||
namespace ChatHelpers {
|
||||
|
||||
class TabbedSelector;
|
||||
|
||||
class TabbedSection : public TWidget {
|
||||
class TabbedSection : public Window::AbstractSectionWidget {
|
||||
public:
|
||||
TabbedSection(QWidget *parent, gsl::not_null<Window::Controller*> controller);
|
||||
TabbedSection(QWidget *parent, gsl::not_null<Window::Controller*> controller, object_ptr<TabbedSelector> selector);
|
||||
@ -46,6 +42,10 @@ public:
|
||||
|
||||
void stickersInstalled(uint64 setId);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) override;
|
||||
QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
|
@ -453,6 +453,14 @@ QImage TabbedSelector::grabForAnimation() {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TabbedSelector::wheelEventFromFloatPlayer(QEvent *e) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect TabbedSelector::rectForFloatPlayer() {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
TabbedSelector::~TabbedSelector() = default;
|
||||
|
||||
void TabbedSelector::hideFinished() {
|
||||
|
@ -77,6 +77,10 @@ public:
|
||||
_beforeHidingCallback = std::move(callback);
|
||||
}
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e);
|
||||
QRect rectForFloatPlayer();
|
||||
|
||||
~TabbedSelector();
|
||||
|
||||
class Inner;
|
||||
|
@ -2265,15 +2265,14 @@ void DialogsWidget::UpdateButton::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
DialogsWidget::DialogsWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller) : TWidget(parent)
|
||||
, _controller(controller)
|
||||
DialogsWidget::DialogsWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller) : Window::AbstractSectionWidget(parent, controller)
|
||||
, _mainMenuToggle(this, st::dialogsMenuToggle)
|
||||
, _filter(this, st::dialogsFilter, lang(lng_dlg_filter))
|
||||
, _jumpToDate(this, object_ptr<Ui::IconButton>(this, st::dialogsCalendar))
|
||||
, _cancelSearch(this, st::dialogsCancelSearch)
|
||||
, _lockUnlock(this, st::dialogsLock)
|
||||
, _scroll(this, st::dialogsScroll) {
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<DialogsInner>(this, _controller, parent));
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<DialogsInner>(this, controller, parent));
|
||||
connect(_inner, SIGNAL(draggingScrollDelta(int)), this, SLOT(onDraggingScrollDelta(int)));
|
||||
connect(_inner, SIGNAL(mustScrollTo(int,int)), _scroll, SLOT(scrollToY(int,int)));
|
||||
connect(_inner, SIGNAL(dialogMoved(int,int)), this, SLOT(onDialogMoved(int,int)));
|
||||
@ -2434,6 +2433,14 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window:
|
||||
_a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition());
|
||||
}
|
||||
|
||||
bool DialogsWidget::wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect DialogsWidget::rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
void DialogsWidget::animationCallback() {
|
||||
update();
|
||||
if (!_a_show.animating()) {
|
||||
@ -2986,7 +2993,7 @@ void DialogsWidget::setSearchInPeer(PeerData *peer) {
|
||||
_searchInMigrated = newSearchInPeer ? newSearchInPeer->migrateFrom() : nullptr;
|
||||
if (newSearchInPeer != _searchInPeer) {
|
||||
_searchInPeer = newSearchInPeer;
|
||||
_controller->searchInPeerChanged().notify(_searchInPeer, true);
|
||||
controller()->searchInPeerChanged().notify(_searchInPeer, true);
|
||||
updateJumpToDateVisibility();
|
||||
}
|
||||
_inner->searchInPeer(_searchInPeer);
|
||||
|
@ -305,7 +305,7 @@ private:
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(DialogsInner::UpdateRowSections);
|
||||
|
||||
class DialogsWidget : public TWidget, public RPCSender, private base::Subscriber {
|
||||
class DialogsWidget : public Window::AbstractSectionWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -347,6 +347,10 @@ public:
|
||||
void searchMessages(const QString &query, PeerData *inPeer = 0);
|
||||
void onSearchMore();
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) override;
|
||||
QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) override;
|
||||
|
||||
void notify_userIsContactChanged(UserData *user, bool fromThisApp);
|
||||
void notify_historyMuteUpdated(History *history);
|
||||
|
||||
@ -409,8 +413,6 @@ private:
|
||||
bool searchFailed(DialogsSearchRequestType type, const RPCError &error, mtpRequestId req);
|
||||
bool peopleFailed(const RPCError &error, mtpRequestId req);
|
||||
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
|
||||
bool _dragInScroll = false;
|
||||
bool _dragForward = false;
|
||||
QTimer _chooseByDragTimer;
|
||||
|
@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "profile/profile_section_memento.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "media/media_clip_reader.h"
|
||||
#include "observer_peer.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
@ -348,8 +349,8 @@ void inlineKeyboardMoved(const HistoryItem *item, int oldKeyboardTop, int newKey
|
||||
}
|
||||
|
||||
bool switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot, MsgId samePeerReplyTo) {
|
||||
if (auto m = App::main()) {
|
||||
return m->notify_switchInlineBotButtonReceived(query, samePeerBot, samePeerReplyTo);
|
||||
if (auto main = App::main()) {
|
||||
return main->notify_switchInlineBotButtonReceived(query, samePeerBot, samePeerReplyTo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -367,13 +368,23 @@ void historyMuteUpdated(History *history) {
|
||||
}
|
||||
|
||||
void handlePendingHistoryUpdate() {
|
||||
if (MainWidget *m = App::main()) {
|
||||
m->notify_handlePendingHistoryUpdate();
|
||||
if (auto main = App::main()) {
|
||||
main->notify_handlePendingHistoryUpdate();
|
||||
}
|
||||
for_const (HistoryItem *item, Global::PendingRepaintItems()) {
|
||||
for (auto item : base::take(Global::RefPendingRepaintItems())) {
|
||||
Ui::repaintHistoryItem(item);
|
||||
|
||||
// Start the video if it is waiting for that.
|
||||
if (item->pendingInitDimensions()) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (auto reader = media->getClipReader()) {
|
||||
if (!reader->started() && reader->mode() == Media::Clip::Reader::Mode::Video) {
|
||||
item->history()->resizeGetHeight(item->history()->width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Global::RefPendingRepaintItems().clear();
|
||||
}
|
||||
|
||||
void unreadCounterUpdated() {
|
||||
|
@ -973,6 +973,7 @@ void HistoryItem::clipCallback(Media::Clip::Notification notification) {
|
||||
if (!stopped) {
|
||||
setPendingInitDimensions();
|
||||
Notify::historyItemLayoutChanged(this);
|
||||
Global::RefPendingRepaintItems().insert(this);
|
||||
}
|
||||
} break;
|
||||
|
||||
|
@ -478,10 +478,9 @@ QPoint SilentToggle::tooltipPos() const {
|
||||
return QCursor::pos();
|
||||
}
|
||||
|
||||
HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller) : TWidget(parent)
|
||||
, _controller(controller)
|
||||
HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller) : Window::AbstractSectionWidget(parent, controller)
|
||||
, _fieldBarCancel(this, st::historyReplyCancel)
|
||||
, _topBar(this, _controller)
|
||||
, _topBar(this, controller)
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _historyDown(_scroll, st::historyToDown)
|
||||
, _fieldAutocomplete(this)
|
||||
@ -496,11 +495,11 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null<Window::Controller*>
|
||||
, _botKeyboardHide(this, st::historyBotKeyboardHide)
|
||||
, _botCommandStart(this, st::historyBotCommandStart)
|
||||
, _silent(this)
|
||||
, _field(this, _controller, st::historyComposeField, lang(lng_message_ph))
|
||||
, _field(this, controller, st::historyComposeField, lang(lng_message_ph))
|
||||
, _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel)))
|
||||
, _a_recording(animation(this, &HistoryWidget::step_recording))
|
||||
, _kbScroll(this, st::botKbScroll)
|
||||
, _tabbedPanel(this, _controller)
|
||||
, _tabbedPanel(this, controller)
|
||||
, _tabbedSelector(_tabbedPanel->getSelector())
|
||||
, _attachDragDocument(this)
|
||||
, _attachDragPhoto(this)
|
||||
@ -770,7 +769,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
|
||||
inlineBotChanged();
|
||||
}
|
||||
if (!_inlineResults) {
|
||||
_inlineResults.create(this, _controller);
|
||||
_inlineResults.create(this, controller());
|
||||
_inlineResults->setResultSelectedCallback([this](InlineBots::Result *result, UserData *bot) {
|
||||
onInlineResultSend(result, bot);
|
||||
});
|
||||
@ -1856,7 +1855,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||
}
|
||||
|
||||
_scroll->hide();
|
||||
_list = _scroll->setOwnedWidget(object_ptr<HistoryInner>(this, _controller, _scroll, _history));
|
||||
_list = _scroll->setOwnedWidget(object_ptr<HistoryInner>(this, controller(), _scroll, _history));
|
||||
_list->show();
|
||||
|
||||
_updateHistoryItems.stop();
|
||||
@ -1904,7 +1903,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||
App::main()->dlgUpdated(wasHistory ? wasHistory->peer : nullptr, wasMsgId);
|
||||
emit historyShown(_history, _showAtMsgId);
|
||||
|
||||
_controller->historyPeerChanged().notify(_peer, true);
|
||||
controller()->historyPeerChanged().notify(_peer, true);
|
||||
update();
|
||||
}
|
||||
|
||||
@ -3411,6 +3410,22 @@ bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) {
|
||||
return TWidget::eventFilter(obj, e);
|
||||
}
|
||||
|
||||
bool HistoryWidget::wheelEventFromFloatPlayer(QEvent *e, 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 _tabbedSection->wheelEventFromFloatPlayer(e, tabbedColumn, playerColumn);
|
||||
}
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
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 mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
DragState HistoryWidget::getDragState(const QMimeData *d) {
|
||||
if (!d
|
||||
|| d->hasFormat(qsl("application/x-td-forward-selected"))
|
||||
@ -3792,14 +3807,14 @@ void HistoryWidget::updateTabbedSelectorSectionShown() {
|
||||
// sendPendingMoveAndResizeEvents() for all widgets in the window, which can lead
|
||||
// to a new HistoryWidget::resizeEvent() call and an infinite recursion here.
|
||||
if (_tabbedSectionUsed) {
|
||||
_tabbedSection.create(this, _controller, _tabbedPanel->takeSelector());
|
||||
_tabbedSection.create(this, controller(), _tabbedPanel->takeSelector());
|
||||
_tabbedSection->setCancelledCallback([this] { setInnerFocus(); });
|
||||
_tabbedSelectorToggle->setColorOverrides(&st::historyAttachEmojiActive, &st::historyRecordVoiceFgActive, &st::historyRecordVoiceRippleBgActive);
|
||||
_rightShadow.create(this, st::shadowFg);
|
||||
auto destroyingPanel = std::move(_tabbedPanel);
|
||||
updateControlsVisibility();
|
||||
} else {
|
||||
_tabbedPanel.create(this, _controller, _tabbedSection->takeSelector());
|
||||
_tabbedPanel.create(this, controller(), _tabbedSection->takeSelector());
|
||||
_tabbedSelectorToggle->installEventFilter(_tabbedPanel);
|
||||
_tabbedSection.destroy();
|
||||
_tabbedSelectorToggle->setColorOverrides(nullptr, nullptr, nullptr);
|
||||
@ -3859,12 +3874,12 @@ void HistoryWidget::toggleTabbedSelectorMode() {
|
||||
updateTabbedSelectorSectionShown();
|
||||
recountChatWidth();
|
||||
updateControlsGeometry();
|
||||
} else if (_controller->canProvideChatWidth(minimalWidthForTabbedSelectorSection())) {
|
||||
} else if (controller()->canProvideChatWidth(minimalWidthForTabbedSelectorSection())) {
|
||||
if (!AuthSession::Current().data().tabbedSelectorSectionEnabled()) {
|
||||
AuthSession::Current().data().setTabbedSelectorSectionEnabled(true);
|
||||
AuthSession::Current().saveDataDelayed(kSaveTabbedSelectorSectionTimeoutMs);
|
||||
}
|
||||
_controller->provideChatWidth(minimalWidthForTabbedSelectorSection());
|
||||
controller()->provideChatWidth(minimalWidthForTabbedSelectorSection());
|
||||
updateTabbedSelectorSectionShown();
|
||||
recountChatWidth();
|
||||
updateControlsGeometry();
|
||||
|
@ -161,7 +161,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class HistoryWidget : public TWidget, public RPCSender, private base::Subscriber {
|
||||
class HistoryWidget final : public Window::AbstractSectionWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -337,6 +337,10 @@ public:
|
||||
void deleteContextItem(bool forEveryone);
|
||||
void deleteSelectedItems(bool forEveryone);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) override;
|
||||
QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) override;
|
||||
|
||||
void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col);
|
||||
|
||||
void ui_repaintHistoryItem(const HistoryItem *item);
|
||||
@ -532,8 +536,6 @@ private:
|
||||
void hideSelectorControlsAnimated();
|
||||
int countMembersDropdownHeightMax() const;
|
||||
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
|
||||
MsgId _replyToId = 0;
|
||||
Text _replyToName;
|
||||
int _replyToNameVersion = 0;
|
||||
|
@ -57,6 +57,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "media/player/media_player_widget.h"
|
||||
#include "media/player/media_player_volume_controller.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "media/player/media_player_float.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
@ -97,6 +98,12 @@ 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) {
|
||||
toggle(this, visible);
|
||||
}) {
|
||||
}
|
||||
|
||||
MainWidget::MainWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller) : TWidget(parent)
|
||||
, _controller(controller)
|
||||
, _dialogsWidth(st::dialogsWidthMin)
|
||||
@ -194,6 +201,11 @@ MainWidget::MainWidget(QWidget *parent, gsl::not_null<Window::Controller*> contr
|
||||
}
|
||||
}
|
||||
});
|
||||
subscribe(Media::Player::instance()->trackChangedNotifier(), [this](AudioMsgId::Type type) {
|
||||
if (type == AudioMsgId::Type::Voice) {
|
||||
checkCurrentFloatPlayer();
|
||||
}
|
||||
});
|
||||
|
||||
subscribe(Adaptive::Changed(), [this]() { handleAdaptiveLayoutUpdate(); });
|
||||
|
||||
@ -218,6 +230,114 @@ MainWidget::MainWidget(QWidget *parent, gsl::not_null<Window::Controller*> contr
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
}
|
||||
|
||||
void MainWidget::checkCurrentFloatPlayer() {
|
||||
auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
|
||||
auto fullId = state.contextId();
|
||||
auto last = currentFloatPlayer();
|
||||
if (!last || last->widget->itemId() != fullId) {
|
||||
if (last) {
|
||||
last->widget->detach();
|
||||
}
|
||||
if (auto item = App::histItemById(fullId)) {
|
||||
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) {
|
||||
toggleFloatPlayer(instance, visible);
|
||||
}));
|
||||
toggleFloatPlayer(currentFloatPlayer(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::toggleFloatPlayer(Float *instance, bool visible) {
|
||||
if (instance->visible != visible) {
|
||||
instance->visible = visible;
|
||||
instance->visibleAnimation.start([this, instance] {
|
||||
updateFloatPlayerPosition(instance);
|
||||
}, visible ? 0. : 1., visible ? 1. : 0., st::slideDuration, visible ? anim::easeOutCirc : anim::linear);
|
||||
updateFloatPlayerPosition(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::updateFloatPlayerPosition(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;
|
||||
}
|
||||
|
||||
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();
|
||||
auto visibleTop = position.y();
|
||||
instance->widget->move(position.x(), anim::interpolate(hiddenTop, visibleTop, visible));
|
||||
}
|
||||
|
||||
void MainWidget::removeFloatPlayer(Float *instance) {
|
||||
auto widget = std::move(instance->widget);
|
||||
auto i = std::find_if(_playerFloats.begin(), _playerFloats.end(), [instance](auto &item) {
|
||||
return (item.get() == instance);
|
||||
});
|
||||
t_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(gsl::not_null<Window::Column*> column) {
|
||||
if (!Adaptive::Normal()) {
|
||||
*column = Adaptive::OneColumn() ? Window::Column::First : Window::Column::Second;
|
||||
if (Adaptive::OneColumn() && selectingPeer()) {
|
||||
return _dialogs;
|
||||
} else if (_overview) {
|
||||
return _overview;
|
||||
} else if (_wideSection) {
|
||||
return _wideSection;
|
||||
} else if (!Adaptive::OneColumn() || _history->peer()) {
|
||||
return _history;
|
||||
}
|
||||
return _dialogs;
|
||||
}
|
||||
if (*column == Window::Column::First) {
|
||||
return _dialogs;
|
||||
}
|
||||
*column = Window::Column::Second;
|
||||
if (_overview) {
|
||||
return _overview;
|
||||
} else if (_wideSection) {
|
||||
return _wideSection;
|
||||
}
|
||||
return _history;
|
||||
}
|
||||
|
||||
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())) {
|
||||
@ -573,11 +693,17 @@ void MainWidget::ui_repaintHistoryItem(const HistoryItem *item) {
|
||||
_playerPlaylist->ui_repaintHistoryItem(item);
|
||||
_playerPanel->ui_repaintHistoryItem(item);
|
||||
if (_overview) _overview->ui_repaintHistoryItem(item);
|
||||
if (auto last = currentFloatPlayer()) {
|
||||
last->widget->ui_repaintHistoryItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::notify_historyItemLayoutChanged(const HistoryItem *item) {
|
||||
_history->notify_historyItemLayoutChanged(item);
|
||||
if (_overview) _overview->notify_historyItemLayoutChanged(item);
|
||||
if (auto last = currentFloatPlayer()) {
|
||||
last->widget->ui_repaintHistoryItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::notify_historyMuteUpdated(History *history) {
|
||||
@ -2331,6 +2457,8 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show
|
||||
_overview = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
updateControlsGeometry();
|
||||
if (onlyDialogs) {
|
||||
_history->hide();
|
||||
if (!_a_show.animating()) {
|
||||
@ -2342,9 +2470,7 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (noPeer) {
|
||||
updateControlsGeometry();
|
||||
} else if (wasActivePeer != activePeer()) {
|
||||
if (!noPeer && wasActivePeer != activePeer()) {
|
||||
if (activePeer()->isChannel()) {
|
||||
activePeer()->asChannel()->ptsWaitingForShortPoll(WaitForChannelGetDifference);
|
||||
}
|
||||
@ -2527,6 +2653,9 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarS
|
||||
result.withTabbedSection = false;
|
||||
}
|
||||
|
||||
for (auto &instance : _playerFloats) {
|
||||
instance->widget->hide();
|
||||
}
|
||||
if (_player) {
|
||||
_player->hideShadow();
|
||||
}
|
||||
@ -2579,6 +2708,11 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarS
|
||||
if (_player) {
|
||||
_player->showShadow();
|
||||
}
|
||||
for (auto &instance : _playerFloats) {
|
||||
if (instance->visible) {
|
||||
instance->widget->show();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -2607,7 +2741,7 @@ void MainWidget::showNewWideSection(const Window::SectionMemento *memento, bool
|
||||
|
||||
auto sectionTop = getSectionTop();
|
||||
auto newWideGeometry = QRect(_history->x(), sectionTop, _history->width(), height() - sectionTop);
|
||||
auto newWideSection = memento->createWidget(this, newWideGeometry);
|
||||
auto newWideSection = memento->createWidget(this, _controller, newWideGeometry);
|
||||
auto animatedShow = [this] {
|
||||
if (_a_show.animating() || App::passcoded()) {
|
||||
return false;
|
||||
@ -2713,6 +2847,9 @@ void MainWidget::orderWidgets() {
|
||||
_sideResizeArea->raise();
|
||||
_playerPlaylist->raise();
|
||||
_playerPanel->raise();
|
||||
for (auto &instance : _playerFloats) {
|
||||
instance->widget->raise();
|
||||
}
|
||||
if (_hider) _hider->raise();
|
||||
}
|
||||
|
||||
@ -2725,6 +2862,9 @@ QRect MainWidget::historyRect() const {
|
||||
|
||||
QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
||||
QPixmap result;
|
||||
for (auto &instance : _playerFloats) {
|
||||
instance->widget->hide();
|
||||
}
|
||||
if (_player) {
|
||||
_player->hideShadow();
|
||||
}
|
||||
@ -2761,6 +2901,11 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m
|
||||
if (_player) {
|
||||
_player->showShadow();
|
||||
}
|
||||
for (auto &instance : _playerFloats) {
|
||||
if (instance->visible) {
|
||||
instance->widget->show();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2786,7 +2931,7 @@ void MainWidget::dlgUpdated(PeerData *peer, MsgId msgId) {
|
||||
}
|
||||
|
||||
void MainWidget::showJumpToDate(PeerData *peer, QDate requestedDate) {
|
||||
t_assert(peer != nullptr);
|
||||
Expects(peer != nullptr);
|
||||
auto currentPeerDate = [peer] {
|
||||
if (auto history = App::historyLoaded(peer)) {
|
||||
if (history->scrollTopItem) {
|
||||
@ -3122,6 +3267,9 @@ void MainWidget::updateControlsGeometry() {
|
||||
updateMediaPlayerPosition();
|
||||
updateMediaPlaylistPosition(_playerPlaylist->x());
|
||||
_contentScrollAddToY = 0;
|
||||
for (auto &instance : _playerFloats) {
|
||||
updateFloatPlayerPosition(instance.get());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::updateDialogsWidthAnimated() {
|
||||
@ -3202,6 +3350,14 @@ bool MainWidget::eventFilter(QObject *o, QEvent *e) {
|
||||
showBackFromStack();
|
||||
return true;
|
||||
}
|
||||
} else if (e->type() == QEvent::Wheel && !_playerFloats.empty()) {
|
||||
for (auto &instance : _playerFloats) {
|
||||
if (instance->widget == o) {
|
||||
auto column = instance->column;
|
||||
auto section = getFloatPlayerSection(&column);
|
||||
return section->wheelEventFromFloatPlayer(e, column, instance->column);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TWidget::eventFilter(o, e);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "history/history_common.h"
|
||||
#include "core/single_timer.h"
|
||||
#include "base/weak_unique_ptr.h"
|
||||
#include "window/section_widget.h"
|
||||
|
||||
namespace Notify {
|
||||
struct PeerUpdate;
|
||||
@ -38,6 +39,7 @@ namespace Player {
|
||||
class Widget;
|
||||
class VolumeWidget;
|
||||
class Panel;
|
||||
class Float;
|
||||
} // namespace Player
|
||||
} // namespace Media
|
||||
|
||||
@ -460,6 +462,35 @@ protected:
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
||||
private:
|
||||
struct Float {
|
||||
template <typename ToggleCallback>
|
||||
Float(QWidget *parent, HistoryItem *item, ToggleCallback callback);
|
||||
|
||||
bool visible = false;
|
||||
Animation visibleAnimation;
|
||||
Window::Corner corner = Window::Corner::TopRight;
|
||||
Window::Column column = Window::Column::Second;
|
||||
QPoint position;
|
||||
object_ptr<Media::Player::Float> widget;
|
||||
};
|
||||
|
||||
using ChannelGetDifferenceTime = QMap<ChannelData*, TimeMs>;
|
||||
enum class ChannelDifferenceRequest {
|
||||
Unknown,
|
||||
PtsGapOrShortPoll,
|
||||
AfterFail,
|
||||
};
|
||||
|
||||
struct DeleteHistoryRequest {
|
||||
PeerData *peer;
|
||||
bool justClearHistory;
|
||||
};
|
||||
|
||||
struct DeleteAllFromUserParams {
|
||||
ChannelData *channel;
|
||||
UserData *from;
|
||||
};
|
||||
|
||||
void animationCallback();
|
||||
void handleAdaptiveLayoutUpdate();
|
||||
void updateWindowAdaptiveLayout();
|
||||
@ -506,6 +537,63 @@ private:
|
||||
|
||||
void saveSectionInStack();
|
||||
|
||||
void getChannelDifference(ChannelData *channel, ChannelDifferenceRequest from = ChannelDifferenceRequest::Unknown);
|
||||
void gotDifference(const MTPupdates_Difference &diff);
|
||||
bool failDifference(const RPCError &e);
|
||||
void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other);
|
||||
void gotState(const MTPupdates_State &state);
|
||||
void updSetState(int32 pts, int32 date, int32 qts, int32 seq);
|
||||
void gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff);
|
||||
bool failChannelDifference(ChannelData *channel, const RPCError &err);
|
||||
void failDifferenceStartTimerFor(ChannelData *channel);
|
||||
|
||||
void feedUpdateVector(const MTPVector<MTPUpdate> &updates, bool skipMessageIds = false);
|
||||
void feedMessageIds(const MTPVector<MTPUpdate> &updates);
|
||||
|
||||
void deleteHistoryPart(DeleteHistoryRequest request, const MTPmessages_AffectedHistory &result);
|
||||
void deleteAllFromUserPart(DeleteAllFromUserParams params, const MTPmessages_AffectedHistory &result);
|
||||
|
||||
void updateReceived(const mtpPrime *from, const mtpPrime *end);
|
||||
bool updateFail(const RPCError &e);
|
||||
|
||||
void usernameResolveDone(QPair<MsgId, QString> msgIdAndStartToken, const MTPcontacts_ResolvedPeer &result);
|
||||
bool usernameResolveFail(QString name, const RPCError &error);
|
||||
|
||||
void inviteCheckDone(QString hash, const MTPChatInvite &invite);
|
||||
bool inviteCheckFail(const RPCError &error);
|
||||
void inviteImportDone(const MTPUpdates &result);
|
||||
bool inviteImportFail(const RPCError &error);
|
||||
|
||||
int getSectionTop() const;
|
||||
|
||||
void hideAll();
|
||||
void showAll();
|
||||
|
||||
void overviewPreloaded(PeerData *data, const MTPmessages_Messages &result, mtpRequestId req);
|
||||
bool overviewFailed(PeerData *data, const RPCError &error, mtpRequestId req);
|
||||
|
||||
void clearCachedBackground();
|
||||
void checkCurrentFloatPlayer();
|
||||
void toggleFloatPlayer(Float *instance, bool visible);
|
||||
void updateFloatPlayerPosition(Float *instance);
|
||||
void removeFloatPlayer(Float *instance);
|
||||
Float *currentFloatPlayer() const {
|
||||
return _playerFloats.empty() ? nullptr : _playerFloats.back().get();
|
||||
}
|
||||
Window::AbstractSectionWidget *getFloatPlayerSection(gsl::not_null<Window::Column*> column);
|
||||
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount);
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount, const MTPUpdates &updates);
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount, const MTPUpdate &update);
|
||||
void ptsApplySkippedUpdates();
|
||||
bool requestingDifference() const {
|
||||
return _ptsWaiter.requesting();
|
||||
}
|
||||
bool getDifferenceTimeChanged(ChannelData *channel, int32 ms, ChannelGetDifferenceTime &channelCurTime, TimeMs &curTime);
|
||||
|
||||
void viewsIncrementDone(QVector<MTPint> ids, const MTPVector<MTPint> &result, mtpRequestId req);
|
||||
bool viewsIncrementFail(const RPCError &error, mtpRequestId req);
|
||||
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
bool _started = false;
|
||||
|
||||
@ -520,56 +608,7 @@ private:
|
||||
|
||||
SingleTimer _updateMutedTimer;
|
||||
|
||||
enum class ChannelDifferenceRequest {
|
||||
Unknown,
|
||||
PtsGapOrShortPoll,
|
||||
AfterFail,
|
||||
};
|
||||
void getChannelDifference(ChannelData *channel, ChannelDifferenceRequest from = ChannelDifferenceRequest::Unknown);
|
||||
void gotDifference(const MTPupdates_Difference &diff);
|
||||
bool failDifference(const RPCError &e);
|
||||
void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other);
|
||||
void gotState(const MTPupdates_State &state);
|
||||
void updSetState(int32 pts, int32 date, int32 qts, int32 seq);
|
||||
void gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff);
|
||||
bool failChannelDifference(ChannelData *channel, const RPCError &err);
|
||||
void failDifferenceStartTimerFor(ChannelData *channel);
|
||||
|
||||
void feedUpdateVector(const MTPVector<MTPUpdate> &updates, bool skipMessageIds = false);
|
||||
void feedMessageIds(const MTPVector<MTPUpdate> &updates);
|
||||
|
||||
struct DeleteHistoryRequest {
|
||||
PeerData *peer;
|
||||
bool justClearHistory;
|
||||
};
|
||||
void deleteHistoryPart(DeleteHistoryRequest request, const MTPmessages_AffectedHistory &result);
|
||||
struct DeleteAllFromUserParams {
|
||||
ChannelData *channel;
|
||||
UserData *from;
|
||||
};
|
||||
void deleteAllFromUserPart(DeleteAllFromUserParams params, const MTPmessages_AffectedHistory &result);
|
||||
|
||||
void updateReceived(const mtpPrime *from, const mtpPrime *end);
|
||||
bool updateFail(const RPCError &e);
|
||||
|
||||
void usernameResolveDone(QPair<MsgId, QString> msgIdAndStartToken, const MTPcontacts_ResolvedPeer &result);
|
||||
bool usernameResolveFail(QString name, const RPCError &error);
|
||||
|
||||
void inviteCheckDone(QString hash, const MTPChatInvite &invite);
|
||||
bool inviteCheckFail(const RPCError &error);
|
||||
QString _inviteHash;
|
||||
void inviteImportDone(const MTPUpdates &result);
|
||||
bool inviteImportFail(const RPCError &error);
|
||||
|
||||
int getSectionTop() const;
|
||||
|
||||
void hideAll();
|
||||
void showAll();
|
||||
|
||||
void overviewPreloaded(PeerData *data, const MTPmessages_Messages &result, mtpRequestId req);
|
||||
bool overviewFailed(PeerData *data, const RPCError &error, mtpRequestId req);
|
||||
|
||||
void clearCachedBackground();
|
||||
|
||||
Animation _a_show;
|
||||
bool _showBack = false;
|
||||
@ -594,6 +633,7 @@ private:
|
||||
object_ptr<Media::Player::Panel> _playerPlaylist;
|
||||
object_ptr<Media::Player::Panel> _playerPanel;
|
||||
bool _playerUsingPanel = false;
|
||||
std::vector<std::unique_ptr<Float>> _playerFloats;
|
||||
|
||||
QPointer<ConfirmBox> _forwardConfirm; // for single column layout
|
||||
object_ptr<HistoryHider> _hider = { nullptr };
|
||||
@ -610,22 +650,12 @@ private:
|
||||
int32 updSeq = 0;
|
||||
SingleTimer noUpdatesTimer;
|
||||
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount);
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount, const MTPUpdates &updates);
|
||||
bool ptsUpdated(int32 pts, int32 ptsCount, const MTPUpdate &update);
|
||||
void ptsApplySkippedUpdates();
|
||||
PtsWaiter _ptsWaiter;
|
||||
bool requestingDifference() const {
|
||||
return _ptsWaiter.requesting();
|
||||
}
|
||||
|
||||
typedef QMap<ChannelData*, TimeMs> ChannelGetDifferenceTime;
|
||||
ChannelGetDifferenceTime _channelGetDifferenceTimeByPts, _channelGetDifferenceTimeAfterFail;
|
||||
TimeMs _getDifferenceTimeByPts = 0;
|
||||
TimeMs _getDifferenceTimeAfterFail = 0;
|
||||
|
||||
bool getDifferenceTimeChanged(ChannelData *channel, int32 ms, ChannelGetDifferenceTime &channelCurTime, TimeMs &curTime);
|
||||
|
||||
SingleTimer _byPtsTimer;
|
||||
|
||||
QMap<int32, MTPUpdates> _bySeqUpdates;
|
||||
@ -677,8 +707,6 @@ private:
|
||||
typedef QMap<mtpRequestId, PeerData*> ViewsIncrementByRequest;
|
||||
ViewsIncrementByRequest _viewsIncrementByRequest;
|
||||
SingleTimer _viewsIncrementTimer;
|
||||
void viewsIncrementDone(QVector<MTPint> ids, const MTPVector<MTPint> &result, mtpRequestId req);
|
||||
bool viewsIncrementFail(const RPCError &error, mtpRequestId req);
|
||||
|
||||
std::unique_ptr<App::WallPaper> _background;
|
||||
|
||||
|
@ -272,6 +272,17 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh,
|
||||
return frame->pix;
|
||||
}
|
||||
|
||||
QPixmap Reader::current() {
|
||||
Expects(_mode == Mode::Video);
|
||||
|
||||
auto frame = frameToShow();
|
||||
t_assert(frame != nullptr);
|
||||
|
||||
frame->displayed.storeRelease(1);
|
||||
moveToNextShow();
|
||||
return frame->pix;
|
||||
}
|
||||
|
||||
bool Reader::ready() const {
|
||||
if (_width && _height) return true;
|
||||
|
||||
|
@ -82,6 +82,7 @@ public:
|
||||
|
||||
void start(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, ImageRoundCorners corners);
|
||||
QPixmap current(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, ImageRoundCorners corners, TimeMs ms);
|
||||
QPixmap current();
|
||||
QPixmap frameOriginal() const {
|
||||
if (auto frame = frameToShow()) {
|
||||
auto result = QPixmap::fromImage(frame->original);
|
||||
@ -91,7 +92,7 @@ public:
|
||||
return QPixmap();
|
||||
}
|
||||
bool currentDisplayed() const {
|
||||
Frame *frame = frameToShow();
|
||||
auto frame = frameToShow();
|
||||
return frame ? (frame->displayed.loadAcquire() != 0) : true;
|
||||
}
|
||||
bool autoPausedGif() const {
|
||||
@ -107,7 +108,7 @@ public:
|
||||
|
||||
State state() const;
|
||||
bool started() const {
|
||||
int step = _step.loadAcquire();
|
||||
auto step = _step.loadAcquire();
|
||||
return (step == WaitingForFirstFrameStep) || (step >= 0);
|
||||
}
|
||||
bool ready() const;
|
||||
|
@ -241,3 +241,6 @@ mediaPlayerFileLayout: OverviewFileLayout(overviewFileLayout) {
|
||||
songIconBg: mediaPlayerActiveFg;
|
||||
songOverBg: mediaPlayerActiveFg;
|
||||
}
|
||||
|
||||
mediaPlayerFloatSize: 128px;
|
||||
mediaPlayerFloatMargin: 12px;
|
||||
|
193
Telegram/SourceFiles/media/player/media_player_float.cpp
Normal file
193
Telegram/SourceFiles/media/player/media_player_float.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "media/player/media_player_float.h"
|
||||
|
||||
#include "styles/style_media_player.h"
|
||||
#include "media/media_clip_reader.h"
|
||||
#include "media/view/media_clip_playback.h"
|
||||
#include "media/media_audio.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
|
||||
Float::Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback) : TWidget(parent)
|
||||
, _item(item)
|
||||
, _toggleCallback(std::move(toggleCallback)) {
|
||||
auto media = _item->getMedia();
|
||||
t_assert(media != nullptr);
|
||||
|
||||
auto document = media->getDocument();
|
||||
t_assert(document != nullptr);
|
||||
t_assert(document->isRoundVideo());
|
||||
|
||||
auto margin = st::mediaPlayerFloatMargin;
|
||||
auto size = 2 * margin + st::mediaPlayerFloatSize;
|
||||
resize(size, size);
|
||||
|
||||
prepareShadow();
|
||||
|
||||
subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) {
|
||||
if (_item == item) {
|
||||
detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Float::detach() {
|
||||
if (_item) {
|
||||
_item = nullptr;
|
||||
if (_toggleCallback) {
|
||||
_toggleCallback(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Float::prepareShadow() {
|
||||
auto shadow = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
shadow.fill(Qt::transparent);
|
||||
shadow.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
Painter p(&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 = App::pixmapFromImageInPlace(Images::prepareBlur(shadow));
|
||||
}
|
||||
|
||||
QRect Float::getInnerRect() const {
|
||||
auto margin = st::mediaPlayerFloatMargin;
|
||||
return rect().marginsRemoved(QMargins(margin, margin, margin, margin));
|
||||
}
|
||||
|
||||
void Float::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
p.setOpacity(_opacity);
|
||||
p.drawPixmap(0, 0, _shadow);
|
||||
|
||||
if (!fillFrame() && _toggleCallback) {
|
||||
_toggleCallback(false);
|
||||
}
|
||||
|
||||
auto inner = getInnerRect();
|
||||
p.drawImage(inner.topLeft(), _frame);
|
||||
|
||||
auto progress = _roundPlayback ? _roundPlayback->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 = QuarterArcLength;
|
||||
auto len = -qRound(FullArcLength * 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);
|
||||
}
|
||||
}
|
||||
|
||||
Clip::Reader *Float::getReader() const {
|
||||
if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||
if (auto reader = media->getClipReader()) {
|
||||
if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Float::hasFrame() const {
|
||||
if (auto reader = getReader()) {
|
||||
return !reader->current().isNull();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Float::fillFrame() {
|
||||
auto creating = _frame.isNull();
|
||||
if (creating) {
|
||||
_frame = QImage(getInnerRect().size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
_frame.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
auto frameInner = [this] {
|
||||
return QRect(0, 0, _frame.width() / cIntRetinaFactor(), _frame.height() / cIntRetinaFactor());
|
||||
};
|
||||
if (auto reader = getReader()) {
|
||||
updatePlayback();
|
||||
auto frame = reader->current();
|
||||
if (!frame.isNull()) {
|
||||
_frame.fill(Qt::transparent);
|
||||
|
||||
Painter p(&_frame);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawPixmap(frameInner(), frame);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (creating) {
|
||||
_frame.fill(Qt::transparent);
|
||||
|
||||
Painter p(&_frame);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::imageBg);
|
||||
p.drawEllipse(frameInner());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Float::updatePlayback() {
|
||||
if (_item) {
|
||||
if (!_roundPlayback) {
|
||||
_roundPlayback = std::make_unique<Media::Clip::Playback>();
|
||||
_roundPlayback->setValueChangedCallback([this](float64 value) {
|
||||
update();
|
||||
});
|
||||
}
|
||||
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice);
|
||||
if (state.id.contextId() == _item->fullId()) {
|
||||
_roundPlayback->updateState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Float::repaintItem() {
|
||||
update();
|
||||
if (hasFrame() && _toggleCallback) {
|
||||
_toggleCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Player
|
||||
} // namespace Media
|
76
Telegram/SourceFiles/media/player/media_player_float.h
Normal file
76
Telegram/SourceFiles/media/player/media_player_float.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Media {
|
||||
namespace Clip {
|
||||
class Playback;
|
||||
} // namespace Clip
|
||||
|
||||
namespace Player {
|
||||
|
||||
class Float : public TWidget, private base::Subscriber {
|
||||
public:
|
||||
Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback);
|
||||
|
||||
FullMsgId itemId() const {
|
||||
return _item ? _item->fullId() : FullMsgId();
|
||||
}
|
||||
void setOpacity(float64 opacity) {
|
||||
_opacity = opacity;
|
||||
update();
|
||||
}
|
||||
void detach();
|
||||
bool detached() const {
|
||||
return !_item;
|
||||
}
|
||||
void ui_repaintHistoryItem(const HistoryItem *item) {
|
||||
if (item == _item) {
|
||||
repaintItem();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
private:
|
||||
Clip::Reader *getReader() const;
|
||||
void repaintItem();
|
||||
void prepareShadow();
|
||||
bool hasFrame() const;
|
||||
bool fillFrame();
|
||||
QRect getInnerRect() const;
|
||||
void updatePlayback();
|
||||
|
||||
HistoryItem *_item = nullptr;
|
||||
base::lambda<void(bool visible)> _toggleCallback;
|
||||
|
||||
float64 _opacity = 1.;
|
||||
|
||||
QPixmap _shadow;
|
||||
QImage _frame;
|
||||
|
||||
std::unique_ptr<Clip::Playback> _roundPlayback;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Player
|
||||
} // namespace Media
|
@ -179,11 +179,13 @@ bool Instance::moveInPlaylist(Data *data, int delta, bool autonext) {
|
||||
auto msgId = data->playlist[newIndex];
|
||||
if (auto item = App::histItemById(msgId)) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (autonext) {
|
||||
_switchToNextNotifier.notify({ data->current, msgId });
|
||||
if (auto document = media->getDocument()) {
|
||||
if (autonext) {
|
||||
_switchToNextNotifier.notify({ data->current, msgId });
|
||||
}
|
||||
DocumentOpenClickHandler::doOpen(media->getDocument(), item, ActionOnLoadPlayInline);
|
||||
return true;
|
||||
}
|
||||
media->playInline();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -207,7 +207,7 @@ void Widget::setShadowGeometryToLeft(int x, int y, int w, int h) {
|
||||
|
||||
void Widget::showShadow() {
|
||||
_shadow->show();
|
||||
_playbackSlider->show();
|
||||
_playbackSlider->setVisible(_type == AudioMsgId::Type::Song);
|
||||
}
|
||||
|
||||
void Widget::hideShadow() {
|
||||
@ -382,7 +382,9 @@ void Widget::setType(AudioMsgId::Type type) {
|
||||
_type = type;
|
||||
_repeatTrack->setVisible(_type == AudioMsgId::Type::Song);
|
||||
_volumeToggle->setVisible(_type == AudioMsgId::Type::Song);
|
||||
_playbackSlider->setVisible(_type == AudioMsgId::Type::Song);
|
||||
if (!_shadow->isHidden()) {
|
||||
_playbackSlider->setVisible(_type == AudioMsgId::Type::Song);
|
||||
}
|
||||
updateLabelsGeometry();
|
||||
handleSongChange();
|
||||
handleSongUpdate(mixer()->currentState(_type));
|
||||
|
@ -1917,9 +1917,8 @@ OverviewInner::~OverviewInner() {
|
||||
clear();
|
||||
}
|
||||
|
||||
OverviewWidget::OverviewWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, PeerData *peer, MediaOverviewType type) : TWidget(parent)
|
||||
, _controller(controller)
|
||||
, _topBar(this, _controller)
|
||||
OverviewWidget::OverviewWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, PeerData *peer, MediaOverviewType type) : Window::AbstractSectionWidget(parent, controller)
|
||||
, _topBar(this, controller)
|
||||
, _scroll(this, st::settingsScroll, false)
|
||||
, _mediaType(this, st::defaultDropdownMenu)
|
||||
, _topShadow(this, st::shadowFg) {
|
||||
@ -2113,6 +2112,14 @@ int32 OverviewWidget::lastScrollTop() const {
|
||||
return _scroll->scrollTop();
|
||||
}
|
||||
|
||||
bool OverviewWidget::wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect OverviewWidget::rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
int32 OverviewWidget::countBestScroll() const {
|
||||
if (type() == OverviewMusicFiles) {
|
||||
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song);
|
||||
|
@ -286,7 +286,7 @@ private:
|
||||
Ui::PopupMenu *_menu = nullptr;
|
||||
};
|
||||
|
||||
class OverviewWidget : public TWidget, public RPCSender {
|
||||
class OverviewWidget : public Window::AbstractSectionWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -351,6 +351,10 @@ public:
|
||||
void deleteContextItem(bool forEveryone);
|
||||
void deleteSelectedItems(bool forEveryone);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) override;
|
||||
QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) override;
|
||||
|
||||
void ui_repaintHistoryItem(const HistoryItem *item);
|
||||
|
||||
void notify_historyItemLayoutChanged(const HistoryItem *item);
|
||||
@ -375,8 +379,6 @@ private:
|
||||
void topBarClick();
|
||||
void animationCallback();
|
||||
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
|
||||
object_ptr<Ui::AbstractButton> _backAnimationButton = { nullptr };
|
||||
object_ptr<Window::TopBarWidget> _topBar;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
|
@ -42,8 +42,8 @@ constexpr int kCommonGroupsPerPage = 40;
|
||||
|
||||
} // namespace
|
||||
|
||||
object_ptr<Window::SectionWidget> SectionMemento::createWidget(QWidget *parent, const QRect &geometry) const {
|
||||
auto result = object_ptr<Widget>(parent, _peer);
|
||||
object_ptr<Window::SectionWidget> SectionMemento::createWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, const QRect &geometry) const {
|
||||
auto result = object_ptr<Widget>(parent, controller, _peer);
|
||||
result->setInternalState(geometry, this);
|
||||
return std::move(result);
|
||||
}
|
||||
@ -337,7 +337,7 @@ InnerWidget::~InnerWidget() {
|
||||
}
|
||||
}
|
||||
|
||||
Widget::Widget(QWidget *parent, PeerData *peer) : Window::SectionWidget(parent)
|
||||
Widget::Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, PeerData *peer) : Window::SectionWidget(parent, controller)
|
||||
, _scroll(this, st::settingsScroll)
|
||||
, _fixedBar(this)
|
||||
, _fixedBarShadow(this, st::shadowFg) {
|
||||
@ -447,5 +447,13 @@ void Widget::showFinishedHook() {
|
||||
_fixedBar->setAnimatingMode(false);
|
||||
}
|
||||
|
||||
bool Widget::wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect Widget::rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
} // namespace CommonGroups
|
||||
} // namespace Profile
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
SectionMemento(PeerData *peer) : _peer(peer) {
|
||||
}
|
||||
|
||||
object_ptr<Window::SectionWidget> createWidget(QWidget *parent, const QRect &geometry) const override;
|
||||
object_ptr<Window::SectionWidget> createWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, const QRect &geometry) const override;
|
||||
|
||||
PeerData *getPeer() const {
|
||||
return _peer;
|
||||
@ -171,7 +171,7 @@ class Widget final : public Window::SectionWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Widget(QWidget *parent, PeerData *peer);
|
||||
Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, PeerData *peer);
|
||||
|
||||
PeerData *peer() const;
|
||||
PeerData *peerForDialogs() const override {
|
||||
@ -189,6 +189,10 @@ public:
|
||||
|
||||
void setInternalState(const QRect &geometry, const SectionMemento *memento);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) override;
|
||||
QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
|
@ -24,8 +24,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace Profile {
|
||||
|
||||
object_ptr<Window::SectionWidget> SectionMemento::createWidget(QWidget *parent, const QRect &geometry) const {
|
||||
auto result = object_ptr<Widget>(parent, _peer);
|
||||
object_ptr<Window::SectionWidget> SectionMemento::createWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, const QRect &geometry) const {
|
||||
auto result = object_ptr<Widget>(parent, controller, _peer);
|
||||
result->setInternalState(geometry, this);
|
||||
return std::move(result);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
SectionMemento(PeerData *peer) : _peer(peer) {
|
||||
}
|
||||
|
||||
object_ptr<Window::SectionWidget> createWidget(QWidget *parent, const QRect &geometry) const override;
|
||||
object_ptr<Window::SectionWidget> createWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, const QRect &geometry) const override;
|
||||
|
||||
PeerData *getPeer() const {
|
||||
return _peer;
|
||||
|
@ -32,7 +32,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace Profile {
|
||||
|
||||
Widget::Widget(QWidget *parent, PeerData *peer) : Window::SectionWidget(parent)
|
||||
Widget::Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, PeerData *peer) : Window::SectionWidget(parent, controller)
|
||||
, _scroll(this, st::settingsScroll)
|
||||
, _fixedBar(this, peer)
|
||||
, _fixedBarShadow(this, object_ptr<Ui::PlainShadow>(this, st::shadowFg)) {
|
||||
@ -161,4 +161,12 @@ void Widget::showFinishedHook() {
|
||||
_inner->showFinished();
|
||||
}
|
||||
|
||||
bool Widget::wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect Widget::rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
} // namespace Profile
|
||||
|
@ -38,7 +38,7 @@ class Widget final : public Window::SectionWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Widget(QWidget *parent, PeerData *peer);
|
||||
Widget(QWidget *parent, gsl::not_null<Window::Controller*> controller, PeerData *peer);
|
||||
|
||||
PeerData *peer() const;
|
||||
PeerData *peerForDialogs() const override {
|
||||
@ -54,6 +54,10 @@ public:
|
||||
|
||||
void setInternalState(const QRect &geometry, const SectionMemento *memento);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) override;
|
||||
QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
|
@ -175,6 +175,9 @@ static const WebPageId CancelledWebPageId = 0xFFFFFFFFFFFFFFFFULL;
|
||||
inline bool operator==(const FullMsgId &a, const FullMsgId &b) {
|
||||
return (a.channel == b.channel) && (a.msg == b.msg);
|
||||
}
|
||||
inline bool operator!=(const FullMsgId &a, const FullMsgId &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
inline bool operator<(const FullMsgId &a, const FullMsgId &b) {
|
||||
if (a.msg < b.msg) return true;
|
||||
if (a.msg > b.msg) return false;
|
||||
|
@ -170,22 +170,35 @@ public:
|
||||
QPoint myrtlpoint(int x, int y) const {
|
||||
return rtlpoint(x, y, Base::width());
|
||||
}
|
||||
QPoint myrtlpoint(const QPoint p) const {
|
||||
return rtlpoint(p, Base::width());
|
||||
QPoint myrtlpoint(const QPoint point) const {
|
||||
return rtlpoint(point, Base::width());
|
||||
}
|
||||
QRect myrtlrect(int x, int y, int w, int h) const {
|
||||
return rtlrect(x, y, w, h, Base::width());
|
||||
}
|
||||
QRect myrtlrect(const QRect &r) const {
|
||||
return rtlrect(r, Base::width());
|
||||
QRect myrtlrect(const QRect &rect) const {
|
||||
return rtlrect(rect, Base::width());
|
||||
}
|
||||
void rtlupdate(const QRect &r) {
|
||||
Base::update(myrtlrect(r));
|
||||
void rtlupdate(const QRect &rect) {
|
||||
Base::update(myrtlrect(rect));
|
||||
}
|
||||
void rtlupdate(int x, int y, int w, int h) {
|
||||
Base::update(myrtlrect(x, y, w, h));
|
||||
}
|
||||
|
||||
QPoint mapFromGlobal(const QPoint &point) const {
|
||||
return Base::mapFromGlobal(point);
|
||||
}
|
||||
QPoint mapToGlobal(const QPoint &point) const {
|
||||
return Base::mapToGlobal(point);
|
||||
}
|
||||
QRect mapFromGlobal(const QRect &rect) const {
|
||||
return QRect(mapFromGlobal(rect.topLeft()), rect.size());
|
||||
}
|
||||
QRect mapToGlobal(const QRect &rect) {
|
||||
return QRect(mapToGlobal(rect.topLeft()), rect.size());
|
||||
}
|
||||
|
||||
protected:
|
||||
void enterEvent(QEvent *e) final override {
|
||||
if (auto parent = tparent()) {
|
||||
|
@ -22,11 +22,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace Window {
|
||||
|
||||
class Controller;
|
||||
class SectionWidget;
|
||||
|
||||
class SectionMemento {
|
||||
public:
|
||||
virtual object_ptr<Window::SectionWidget> createWidget(QWidget *parent, const QRect &geometry) const = 0;
|
||||
virtual object_ptr<Window::SectionWidget> createWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller, const QRect &geometry) const = 0;
|
||||
virtual ~SectionMemento() {
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace Window {
|
||||
|
||||
SectionWidget::SectionWidget(QWidget *parent) : TWidget(parent) {
|
||||
SectionWidget::SectionWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller) : AbstractSectionWidget(parent, controller) {
|
||||
}
|
||||
|
||||
void SectionWidget::setGeometryWithTopMoved(const QRect &newGeometry, int topDelta) {
|
||||
|
@ -25,6 +25,60 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace Window {
|
||||
|
||||
class Controller;
|
||||
|
||||
enum class Column {
|
||||
First,
|
||||
Second,
|
||||
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) {
|
||||
}
|
||||
|
||||
// Float player interface.
|
||||
virtual bool wheelEventFromFloatPlayer(QEvent *e, Window::Column myColumn, Window::Column playerColumn) {
|
||||
return false;
|
||||
}
|
||||
virtual QRect rectForFloatPlayer(Window::Column myColumn, Window::Column playerColumn) {
|
||||
return mapToGlobal(rect());
|
||||
}
|
||||
|
||||
protected:
|
||||
gsl::not_null<Window::Controller*> controller() const {
|
||||
return _controller;
|
||||
}
|
||||
|
||||
private:
|
||||
gsl::not_null<Window::Controller*> _controller;
|
||||
|
||||
};
|
||||
|
||||
class SectionMemento;
|
||||
|
||||
struct SectionSlideParams {
|
||||
@ -37,12 +91,9 @@ struct SectionSlideParams {
|
||||
}
|
||||
};
|
||||
|
||||
class SectionWidget : public TWidget, protected base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
class SectionWidget : public AbstractSectionWidget {
|
||||
public:
|
||||
|
||||
SectionWidget(QWidget *parent);
|
||||
SectionWidget(QWidget *parent, gsl::not_null<Window::Controller*> controller);
|
||||
|
||||
virtual PeerData *peerForDialogs() const {
|
||||
return nullptr;
|
||||
|
@ -181,6 +181,8 @@
|
||||
<(src_loc)/media/player/media_player_button.h
|
||||
<(src_loc)/media/player/media_player_cover.cpp
|
||||
<(src_loc)/media/player/media_player_cover.h
|
||||
<(src_loc)/media/player/media_player_float.cpp
|
||||
<(src_loc)/media/player/media_player_float.h
|
||||
<(src_loc)/media/player/media_player_instance.cpp
|
||||
<(src_loc)/media/player/media_player_instance.h
|
||||
<(src_loc)/media/player/media_player_list.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user