Add a floating player for round video messages.

This commit is contained in:
John Preston 2017-05-22 18:25:49 +03:00
parent 0bfff65306
commit b7550f63c9
33 changed files with 771 additions and 138 deletions

View File

@ -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

View File

@ -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;

View File

@ -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() {

View File

@ -77,6 +77,10 @@ public:
_beforeHidingCallback = std::move(callback);
}
// Float player interface.
bool wheelEventFromFloatPlayer(QEvent *e);
QRect rectForFloatPlayer();
~TabbedSelector();
class Inner;

View File

@ -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);

View File

@ -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;

View File

@ -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() {

View File

@ -973,6 +973,7 @@ void HistoryItem::clipCallback(Media::Clip::Notification notification) {
if (!stopped) {
setPendingInitDimensions();
Notify::historyItemLayoutChanged(this);
Global::RefPendingRepaintItems().insert(this);
}
} break;

View File

@ -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();

View File

@ -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;

View File

@ -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 &params) {
QPixmap result;
for (auto &instance : _playerFloats) {
instance->widget->hide();
}
if (_player) {
_player->hideShadow();
}
@ -2761,6 +2901,11 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams &param
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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -241,3 +241,6 @@ mediaPlayerFileLayout: OverviewFileLayout(overviewFileLayout) {
songIconBg: mediaPlayerActiveFg;
songOverBg: mediaPlayerActiveFg;
}
mediaPlayerFloatSize: 128px;
mediaPlayerFloatMargin: 12px;

View 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

View 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

View File

@ -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;

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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()) {

View File

@ -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() {
}

View File

@ -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) {

View File

@ -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;

View File

@ -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